Files
2026-05-27 23:29:53 +01:00

5.6 KiB

Tasks: enterprise-react-virtual

Change ID: enterprise-react-virtual Package: @suaveplan/react-virtual Target path: react/ui/react-virtual Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/types


0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create directory tree per proposal layout.
  • Write package.json at 0.1.0 with "sideEffects": ["**/*.css"], exports map (top-level barrel + per-module subpaths: ./core/virtual-engine/virtual-engine, ./core/range-calculator/range-calculator, ./hooks/use-virtual-list/use-virtual-list, ./hooks/use-virtual-grid/use-virtual-grid, ./hooks/use-virtual-scroll/use-virtual-scroll, ./components/virtual-list/virtual-list, ./components/virtual-grid/virtual-grid, ./utils/resize-observer/resize-observer, ./utils/scroll-utils/scroll-utils, ./styles/virtual.css).
  • dependencies: @suaveplan/error workspace:^, @suaveplan/types workspace:^, clsx catalog:ui, tailwind-merge catalog:ui, zod catalog:forms, @opentelemetry/api catalog:observability, tslib catalog:build.
  • Write tsconfig.json, vitest.config.ts, scripts.

2. Error codes and types

  • Define VirtualError extending SuaveplanError; codes: VIRTUAL_INVALID_CONFIG, VIRTUAL_SCROLL_FAILED.
  • Define all public types: VirtualEngineConfig, VirtualEngineResult, VirtualItem, VisibleRange, SizeCache, SizeMode, ScrollDirection, ScrollAlignment, ScrollToIndexParams, ThrottleOptions, ResizeCallback, grid-axis types.
  • Write VirtualEngineConfigSchema Zod schema; validate in createVirtualEngine.

3. Core engine (src/core/virtual-engine/)

  • Implement createVirtualEngine(config) — validates config, initialises SizeCache, computes initial VirtualEngineResult.
  • Implement calculateVirtualItems(engine, scrollOffset, containerSize) — returns VirtualItem[] with index, start, size, end, lane. Handles overscan.
  • Implement buildVirtualItems, calculateVisibleRange, calculateScrollToOffset, createSizeCache, findIndexAtOffset, getItemOffset, getItemSize, setItemSize, getScrollDirection, getTotalSize.
  • Write virtual-engine.test.ts (≤500 lines): fixed size, dynamic size, overscan, scroll direction, scroll-to-index, size cache mutation. { timeout: 5000 }.
  • Write virtual-engine.md (≥200 words).

4. Range calculator (src/core/range-calculator/)

  • Implement calculateGridRange, calculateGridDimensions, buildGridCells, findAxisIndex, getAxisOffset, getAxisTotalSize, resolveSize for 2D grid axis math (rows + columns with mixed fixed/dynamic sizes).
  • Write range-calculator.test.ts, range-calculator.md.

5. Hooks

useVirtualList

  • Wraps engine + useRef for container scroll; integrates SharedResizeObserver for container size; returns virtualItems, totalSize, scrollToIndex.
  • Write use-virtual-list.test.ts, use-virtual-list.md.

useVirtualGrid

  • 2D binding: row + column axis, scrollToCell(row, col).
  • Write use-virtual-grid.test.ts, use-virtual-grid.md.

useVirtualScroll

  • Scroll-offset observer only — RAF throttled; returns scrollOffset, scrollDirection, isNearEnd.
  • Write use-virtual-scroll.test.ts, use-virtual-scroll.md.

6. Components

VirtualList<T>

  • Render-prop component wrapping useVirtualList. Forwards VirtualListHandle ref with scrollToIndex.
  • prefers-reduced-motion scroll behavior.
  • OTel virtual.scroll span event.
  • Write virtual-list.test.tsx, virtual-list.stories.tsx (default, dynamic-sizes, large-dataset 100k, playground, a11y), virtual-list.md.

VirtualGrid<T>

  • Render-prop component wrapping useVirtualGrid. Forwards VirtualGridHandle.
  • Write virtual-grid.test.tsx, virtual-grid.stories.tsx, virtual-grid.md.

7. Utilities

  • src/utils/resize-observer/resize-observer.ts — SharedResizeObserver pooling (one observer per root), getSharedResizeObserver(), measureElement(), resetSharedResizeObserver(). Write test + md.
  • src/utils/scroll-utils/scroll-utils.ts — throttle, throttleWithRaf, getScrollOffset, clampScrollOffset, scrollTo (respects prefers-reduced-motion), isNearEnd. Write test + md.
  • src/styles/virtual.css — positioning helpers (absolute container, transform translate3d items).

8. Integration and E2E

  • tests/integration.test.tsx — VirtualList with 10k items: scroll offset updates virtual items, scrollToIndex snaps.
  • e2e/virtual.spec.ts — Playwright: render 100k-row list, verify only ~20 DOM nodes, scroll to bottom, scrollToIndex(5000) lands at correct position.

9. Verify and publish

  • bunx turbo lint typecheck test --filter=@suaveplan/react-virtual — green.
  • bunx turbo test:coverage --filter=@suaveplan/react-virtual — 100%.
  • bun scripts/publish.ts --package @suaveplan/react-virtual.
  • Archive change folder.