Skip to content

feat: add centerSlide to centre narrower slides with their neighbours peeking in - #31

Merged
petrkonecny2 merged 9 commits into
mainfrom
fix/slide-width-support
Oct 10, 2026
Merged

petrkonecny2 merged 9 commits into
mainfrom
fix/slide-width-support

Conversation

@petrkonecny2

@petrkonecny2 petrkonecny2 commented Jan 12, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Adds centerSlide to the provider. When slideWidth is narrower than the carousel, the current slide is centred and its neighbours peek in on both sides.

<HeroCarousel.Provider slideWidth={300} centerSlide disableInfiniteScroll disableAutoScroll>

This replaces the first take on this branch, which set the pager's overflow to visible and wrapped the carousel in marginLeft: (windowWidth - slideWidth) / 2. That had three problems:

  • It changed every consumer. With visible overflow, a carousel inside a padded container showed its slides spilling outside it.
  • It centred on the window, not the carousel. That's off-centre whenever the carousel isn't full width.
  • The left peek wasn't swipeable. The margin moved the gesture view, so only the right peek responded.

How it works now:

  • AnimatedPagedView measures its own width with onLayout, starting from the window width so there's no jump on mount. It shifts the moving row by (width - slideWidth) / 2.
  • The pager keeps overflow: 'hidden', so neighbours show inside the carousel's own bounds and nothing spills out.
  • The offset sits inside the gesture view, so swipes work from either peek.
  • It's opt-in and defaults to false, so existing carousels don't move.
  • The animated style now lists clampValue and the offset as dependencies. It had [] before, so a change in slide count or width didn't reach it.
  • The README documents the prop, with an example. To keep a slide's content centred, put the gap between slides inside each slide as horizontal padding, half on each side.

The branch also keeps the type HeroCarouselProps export from the first take, and has #32 merged in, so it's based on #32.

Testing: type:check, lint, test and Prettier. On the iOS simulator in the Ascension app (Expo SDK 57, Reanimated 4.5.1), with two 337pt slides on a 402pt screen:

  • The current card is centred, with the neighbour peeking on each side.
  • Swipes that start on the left peek and on the right peek both move the carousel.
  • Each card lands centred.

Related Tickets/Issues

Based on #32 and to be merged after it. Used by the parishes carousel in the Ascension app.

🤖 Generated with Claude Code

📦 Published PR as canary version: 0.1.16--canary.31.67831fd.0

✨ Test out this PR locally via:

npm install @strv/react-native-hero-carousel@0.1.16--canary.31.67831fd.0
# or 
yarn add @strv/react-native-hero-carousel@0.1.16--canary.31.67831fd.0

Petr Konecny and others added 5 commits October 10, 2026 18:18
The pan gesture, the scroll reaction and the infinite-scroll reaction read from props and the children array inside worklets. Reanimated 4 copies everything a worklet captures to the UI thread and throws on React elements in development (Cannot copy value of type FiberNode), so the carousel crashed on mount. They now capture only the callbacks and the slide count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every slide stayed mounted, so a long carousel paid for all of them on mount and ran every slide's animated styles on each frame. With renderWindow, slides further away render as empty slots of the same width. It's opt-in, since unmounting drops slide state such as video playback. With infinite scroll the window wraps around, so the slide a clone jumps to is already mounted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ow' into fix/slide-width-support

# Conflicts:
#	src/components/HeroCarousel/index.tsx
… peeking in

Replaces the window-width margin and the visible overflow. With slideWidth narrower than the carousel, centerSlide offsets the moving row by half the free space, using the carousel's measured width, so the current slide is centred and its neighbours show on both sides inside the carousel's own bounds. The offset sits inside the gesture view, so swipes work on either peek. It's opt-in, so existing carousels don't move, and the pager clips again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@petrkonecny2 petrkonecny2 changed the title fix: added basic support for slide width WIP feat: add centerSlide to centre narrower slides with their neighbours peeking in Oct 10, 2026
@petrkonecny2
petrkonecny2 changed the base branch from main to fix/reanimated-4-and-render-window October 10, 2026 16:47
@petrkonecny2
petrkonecny2 requested a balanced review from Copilot October 10, 2026 17:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The window-based initial width can visibly misposition slides before the actual container measurement arrives.

1 open finding
What changed in this PR

Adds opt-in centering for slides narrower than their carousel container.

Changes:

  • Adds and documents the centerSlide provider option.
  • Measures the carousel and offsets its animated slide row.
  • Exports HeroCarouselProps explicitly as a type.
File Description
src/​context/​CarouselContext/​index.tsx Adds centerSlide to provider context.
src/​components/​AnimatedPagedView/​index.tsx Measures and centers the slide row.
src/​components/​index.ts Corrects the type-only export.
README.md Documents centered-slide usage.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/components/AnimatedPagedView/index.tsx Outdated
@petrkonecny2
petrkonecny2 changed the base branch from fix/reanimated-4-and-render-window to main October 10, 2026 17:07
# Conflicts:
#	README.md
#	src/context/CarouselContext/index.tsx
The offset came from the window's width until onLayout reported the carousel's own, so a carousel narrower than the window rendered shifted for its first frame. The row now starts at left: 50% less half a slide, which layout resolves against the carousel's real width from the first frame, with no state or measuring.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@petrkonecny2
petrkonecny2 merged commit 8fd3753 into main Oct 10, 2026
3 checks passed

This branch was successfully deployed

1 active deployment
npm — 67831fd5 Deployed Oct 10, 2026 by petrkonecny2 via Publish #56
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.

3 participants