Skip to content

PageControl

PageControl (from scenerystack/sun) renders the classic iOS "row of dots" pagination indicator: one dot per page, with the current page's dot drawn distinctly. It's driven by two Properties rather than owning any paging state itself, which is exactly the shape Carousel exposes — pageNumberProperty and numberOfPagesProperty — so the two are typically constructed together, with PageControl reflecting (and, if interactive, controlling) which page of the carousel is showing.

ts
import { Carousel, PageControl, type CarouselItem } from 'scenerystack/sun';
import { Text, VBox } from 'scenerystack/scenery';
import { Tandem } from 'scenerystack/tandem';

const items: CarouselItem[] = [
  { createNode: () => new Text( 'Apple' ) },
  { createNode: () => new Text( 'Banana' ) },
  { createNode: () => new Text( 'Cherry' ) },
  { createNode: () => new Text( 'Date' ) },
  { createNode: () => new Text( 'Elderberry' ) }
];

const fruitCarousel = new Carousel( items, {
  itemsPerPage: 2,
  tandem: Tandem.REQUIRED.createTandem( 'fruitCarousel' )
} );

const pageControl = new PageControl(
  fruitCarousel.pageNumberProperty,
  fruitCarousel.numberOfPagesProperty,
  {
    interactive: true, // clicking a dot jumps to that page
    tandem: Tandem.REQUIRED.createTandem( 'pageControl' )
  }
);

const carouselWithPaging = new VBox( {
  spacing: 6,
  children: [ fruitCarousel, pageControl ]
} );

pageNumberProperty must be a settable TProperty<number> (not just readable) precisely because PageControl writes to it when interactive: true and a dot is clicked — Carousel.pageNumberProperty satisfies this directly.

Options

OptionDefaultEffect
interactivefalseWhether clicking a dot navigates to that page
orientation'horizontal''horizontal' (dots left-to-right) or 'vertical' (top-to-bottom); match your carousel's orientation
dotRadius3Radius of each dot
dotSpacing10Space between adjacent dots
lineWidth1Stroke width, if currentPageStroke/pageStroke are set
dotTouchAreaDilation / dotMouseAreaDilation4 / 4Pointer-area dilation beyond each dot's radius, when interactive
currentPageFill / currentPageStroke'black' / nullAppearance of the dot for the current page
pageFill / pageStroke'rgb( 200, 200, 200 )' / nullAppearance of every other dot

Dots are recreated whenever the page count changes

PageControl rebuilds its dots from scratch every time numberOfPagesProperty fires — including at construction — so it stays correct if a Carousel's visible item count changes at runtime (e.g. via Carousel.setItemVisible), without any extra wiring on your part.