2.0 KiB
name, description, metadata
| name | description | metadata | ||||||
|---|---|---|---|---|---|---|---|---|
| opds-groups-carousel-4750 | OPDS feed groups (>=2) render as horizontal virtualized carousels with lazy cover loading |
|
#4750 (PR #4755, merged): when an OPDS feed.groups.length >= 2, FeedView renders each group's publications/navigation as a horizontal carousel (src/app/opds/components/GroupCarousel.tsx) instead of the grid; single-group feeds keep the grid. Matches Thorium.
GroupCarousel wraps a horizontal react-virtuoso Virtuoso (horizontalDirection), so only in-view items mount → covers load lazily as you scroll (verified via network: ~12 covers/group fetched regardless of group size; far-right items fetch only after scrolling to them).
Gotchas (cost real debugging):
VirtuosoHandle.scrollBy({left})is a no-op in horizontal mode (the handle maps to the vertical axis). Page the arrows by index viascrollToIndex({index, align, behavior}), tracking the visible range fromrangeChanged.- Virtuoso sizes the horizontal track lazily, so a pixel
scrollByon the scroller element clamps to the currently-rendered width — another reason to scroll by index. - Arrow visibility comes from
atTopStateChange/atBottomStateChange(top=left, bottom=right). Row height is measured from the first[data-carousel-item]; arrows are vertically centered on the cover by measuring the first<figure>(cards have title/author below, so centering on the whole row looks low). - Scrollbar hidden via a scoped
.no-scrollbarutil inglobals.css; arrows useeink-bordered. - Tests must mock
react-virtuoso(jsdom has no layout) like the TOCView/BooknoteView tests — render all items viaitemContent.
PublicationCard (shared by carousel + grids) got rounded covers (overflow-hidden rounded, matching the library bookshelf) and dropped the inline acquisition/price badge — that badge still renders on the detail page (PublicationView).
Related: virtuoso_overlayscrollbars.