Skip to content

feat(ScrollArea): add getScrollElement virtualize option#233

Merged
IgorShevchik merged 1 commit into
mainfrom
sync/nuxt-a84de85
Jun 29, 2026
Merged

feat(ScrollArea): add getScrollElement virtualize option#233
IgorShevchik merged 1 commit into
mainfrom
sync/nuxt-a84de85

Conversation

@IgorShevchik

Copy link
Copy Markdown
Collaborator

Upstream

a84de85feat(ScrollArea): add getScrollElement virtualize option (#6650)

Change

Lets ScrollArea virtualize against an external ancestor scroll container instead of its own root viewport:

  • new getScrollElement?: () => Element | null in ScrollAreaVirtualizeOptions (removed from the Omit exclusion)
  • isExternalScroll computed + a getScrollElement() resolver (external el when provided, else rootRef.$el), used by the virtualizer
  • getVirtualItemStyle subtracts scrollMargin from virtualItem.start in external mode (offset) so items sit inline
  • resize-observer moved from a one-shot onMounted to watch(getScrollElement, …, { immediate: true }) so it re-observes when the scroll element changes (onMounted import dropped)
  • root gets overflow: visible in external mode (it no longer owns the scroll)

Component — 1:1

b24ui's ScrollArea.vue matched upstream's pre-change structure; all script + the :style change applied verbatim (only the pre-existing b24ui.root class binding differs, untouched).

Tests

Added the with virtualize external scroll element renderEach case verbatim (getScrollElement: () => document.body, scrollMargin: 20). 2 snapshots regenerated — the new case renders style="overflow: visible;" on the root, the feature demonstrating itself. Full suite 5145 passed (+2 vs 5143) / 6 skipped.

Docs — adapted & trimmed (deviation)

Ported the ### With external scroll element md section (badge New, per b24ui convention, not upstream's Soon) with the same explanation + note/caution. The example is trimmed to the feature core: an external scroll container owning the scroll, a sticky header measured via useElementSize (border-box) feeding scrollMargin, and B24ScrollArea with { getScrollElement, scrollMargin, estimateSize, skipMeasurement } over B24PageCard/B24User rows (same dummyjson fetch b24ui's ScrollAreaInfiniteScrollExample already uses) + a "Top" button (B24Button, ArrowToTheTopIcon, color="air-tertiary").

Dropped upstream's incidental "find" toolbar (search input + prev/next-match buttons + result counter + UBadge/UFieldGroup) — demo sugar, not part of the feature, and its neutral/subtle/outline + iconify mappings carry design-judgment risk in a non-test-validated example. Can follow as a separate doc enhancement if wanted.

useElementSize is imported explicitly from @vueuse/core (not auto-imported in b24ui docs).

Maps (§4)

Added 3 icon-map.json entries: i-lucide-arrow-up-to-lineactions/ArrowToTheTopIcon, i-lucide-chevron-upoutline/ChevronTopSIcon (b24icons names up-chevrons "Top"), i-lucide-searchmain/Search2Icon.

Verify (CI=true)

dev:prepare · lint · typecheck (docs example compiles) · test · build — all green.

Ledger

🤖 Generated with Claude Code


Generated by Claude Code

Port of upstream nuxt/ui a84de85 (#6650).

Adds a `getScrollElement` option to `ScrollAreaVirtualizeOptions`, letting the
component virtualize against an external ancestor scroll container instead of
its own root viewport:

- new `getScrollElement?: () => Element | null` option
- `isExternalScroll` + a `getScrollElement()` resolver, used by the virtualizer
- `getVirtualItemStyle` subtracts `scrollMargin` from the item start in external
  mode so items sit inline
- the resize-observer moves from `onMounted` to
  `watch(getScrollElement, …, { immediate: true })` so it re-observes when the
  scroll element changes
- root gets `overflow: visible` in external mode (it no longer owns the scroll)

Adds a renderEach spec case (2 snapshots regenerated) and a docs section. The
docs example is adapted to b24ui conventions and trimmed to the feature core
(external scroll container + scrollMargin); upstream's incidental find-toolbar
is omitted. Adds 3 icon-map entries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JS8ypVfQSFzYVZzkTHhURb
@IgorShevchik IgorShevchik merged commit 63fe722 into main Jun 29, 2026
1 check passed
@IgorShevchik IgorShevchik deleted the sync/nuxt-a84de85 branch June 29, 2026 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants