{"version":3,"file":"carousel.mjs","names":[],"sources":["../../../../package/common/carousel/carousel.tsx"],"sourcesContent":["import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport { faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport InteractableDiv from '../interactableDiv';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport type { CarouselLabels, CarouselProps } from './carousel.types';\nimport { CarouselSlideContext } from './carouselSlideContext';\n\nimport styles from './carousel.module.pcss';\nimport fadeStyles from 'components/common/styles/scrollFade.module.pcss';\nimport { ButtonStyleType } from '../button/button.types';\n\n// Opaque, because the arrows sit over the slide's own image; Neutral's fill is see-through.\nconst ARROW_COLOR = 'var(--background-accent)';\n\nconst AXIS_LOCK_THRESHOLD = 10;\nconst DRAG_COMMIT_RATIO = 0.2;\n/** With `lazyRender`, further than this a jump cuts straight to the slide: sliding there would mount every slide in between. */\nconst MAX_ANIMATED_JUMP = 3;\n\nconst DEFAULT_LABELS: Required<CarouselLabels> = {\n  previous: 'Previous slide',\n  next: 'Next slide',\n  slides: 'Slides',\n  goToSlide: (slide) => `Go to slide ${slide}`,\n};\n\nfunction Carousel(props: CarouselProps) {\n  const {\n    children,\n    currentIndex,\n    defaultIndex = 0,\n    onChange = () => {},\n    loop = true,\n    autoPlay = false,\n    autoPlayInterval = 4000,\n    pauseOnHover = true,\n    showArrows = true,\n    showDots = true,\n    swipeable = true,\n    lazyRender = false,\n    adaptiveHeight = false,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    labels,\n    ...rest\n  } = useComponentDefaults('Carousel', props);\n  const text = useLabels('Carousel', labels, DEFAULT_LABELS);\n\n  const instanceId = React.useId();\n  const slides = React.Children.toArray(children);\n  const count = slides.length;\n  const canLoop = loop && count > 1;\n  const offset = canLoop ? 1 : 0;\n  const extendedSlides = canLoop ? [slides[count - 1], ...slides, slides[0]] : slides;\n\n  const isControlled = currentIndex !== undefined;\n  const [internalIndex, setInternalIndex] = useState(() => clampIndex(defaultIndex, count, canLoop));\n  const realIndex = clampIndex(isControlled ? currentIndex! : internalIndex, count, canLoop);\n\n  const [displayIndex, setDisplayIndex] = useState(realIndex + offset);\n  const [transitionEnabled, setTransitionEnabled] = useState(true);\n  const prevRealIndexRef = useRef(realIndex);\n\n  const [hovered, setHovered] = useState(false);\n  const [focused, setFocused] = useState(false);\n  const [dragging, setDragging] = useState(false);\n\n  const containerRef = useRef<HTMLDivElement>(null);\n  const trackRef = useRef<HTMLDivElement>(null);\n  const dotsRef = useRef<HTMLDivElement>(null);\n\n  // The window of slides `lazyRender` keeps mounted, as of the last settled position. Slides are\n  // rendered for the union of this and the current window, so the ones a multi-step jump slides\n  // past stay mounted until the transform transition ends instead of blanking out mid-flight.\n  const [settledMountRange, setSettledMountRange] = useState(() => ({\n    min: realIndex + offset - 1,\n    max: realIndex + offset + 1,\n  }));\n\n  useLayoutEffect(() => {\n    const prevReal = prevRealIndexRef.current;\n    prevRealIndexRef.current = realIndex;\n    if (prevReal === realIndex) return;\n\n    // A wrap parks the track on a clone until transitionEnd swaps it back, so a change arriving\n    // before that has to animate from the normalised position rather than the clone.\n    const lastExtendedIndex = extendedSlides.length - 1;\n    let fromIndex = displayIndex;\n    if (canLoop && displayIndex === 0) fromIndex = count;\n    else if (canLoop && displayIndex === lastExtendedIndex) fromIndex = 1;\n\n    const forwardWrap = canLoop && prevReal === count - 1 && realIndex === 0;\n    const backwardWrap = canLoop && prevReal === 0 && realIndex === count - 1;\n\n    let target: number;\n    if (forwardWrap) target = fromIndex + 1;\n    else if (backwardWrap) target = fromIndex - 1;\n    else target = realIndex + offset;\n\n    if (lazyRender && Math.abs(target - fromIndex) > MAX_ANIMATED_JUMP) {\n      setTransitionEnabled(false);\n      setDisplayIndex(target);\n      setSettledMountRange({ min: target - 1, max: target + 1 });\n      return;\n    }\n\n    // Slides the track has passed are off screen. Kept until a transition ended, which quick steps\n    // keep restarting, the window grew by a slide each step, and after a wrap spanned the whole set.\n    // Off a wrap's clone, the track is put back on `fromIndex` below.\n    const shown = lazyRender && fromIndex === displayIndex ? (shownPosition(trackRef.current) ?? fromIndex) : fromIndex;\n    setSettledMountRange({ min: Math.floor(shown) - 1, max: Math.ceil(shown) + 1 });\n\n    // Written straight to the node: the normalised position has to reach style recalculation on its\n    // own, or it and the animated position collapse into one jump with no transition.\n    const track = trackRef.current;\n    if (fromIndex !== displayIndex && track) {\n      track.style.transition = 'none';\n      track.style.transform = buildTrackTransform(fromIndex, 0);\n      void track.offsetWidth;\n      track.style.transition = '';\n    }\n\n    setTransitionEnabled(true);\n    setDisplayIndex(target);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [realIndex]);\n\n  const mountMin = Math.min(settledMountRange.min, displayIndex - 1);\n  const mountMax = Math.max(settledMountRange.max, displayIndex + 1);\n  // Without `lazyRender`, every slide. With it, the others get no element at all: a placeholder per\n  // slide made every render of a large set cost as much as the set.\n  const lastPosition = extendedSlides.length - 1;\n  const firstRendered = lazyRender ? Math.max(0, mountMin) : 0;\n  const lastRendered = lazyRender ? Math.min(lastPosition, mountMax) : lastPosition;\n  const isRendered = (position: number) => position >= firstRendered && position <= lastRendered;\n\n  const [contentHeight, setContentHeight] = useState<number | undefined>(undefined);\n\n  useLayoutEffect(() => {\n    if (!adaptiveHeight) return;\n    const slideElement = trackRef.current?.children[displayIndex - firstRendered] as HTMLElement | undefined;\n    if (!slideElement) return;\n\n    const update = () => setContentHeight(slideElement.offsetHeight);\n    update();\n\n    const observer = new ResizeObserver(update);\n    observer.observe(slideElement);\n    return () => observer.disconnect();\n  }, [adaptiveHeight, displayIndex, firstRendered, count]);\n\n  // Keeps the selected dot in view. Written as `scrollLeft` because `scrollIntoView` is free to\n  // scroll the page along with it.\n  useLayoutEffect(() => {\n    const strip = dotsRef.current;\n    const dot = strip?.children[realIndex] as HTMLElement | undefined;\n    if (!strip || !dot) return;\n    strip.scrollLeft = dot.offsetLeft - (strip.clientWidth - dot.offsetWidth) / 2;\n  }, [realIndex, count]);\n\n  function goToIndex(nextIndex: number) {\n    const clamped = clampIndex(nextIndex, count, canLoop);\n    if (!isControlled) setInternalIndex(clamped);\n    onChange(clamped);\n  }\n\n  function goNext() {\n    if (!canLoop && realIndex >= count - 1) return;\n    goToIndex(realIndex + 1);\n  }\n\n  function goPrev() {\n    if (!canLoop && realIndex <= 0) return;\n    goToIndex(realIndex - 1);\n  }\n\n  function handleTrackTransitionEnd(event: React.TransitionEvent<HTMLDivElement>) {\n    if (event.target !== event.currentTarget) return;\n\n    // A wrap ends on a clone, and the swap to the real slide has no transition of its own: settled\n    // around the clone, the window spanned the whole set.\n    let settled = displayIndex;\n    if (canLoop && displayIndex === 0) settled = count;\n    else if (canLoop && displayIndex === extendedSlides.length - 1) settled = 1;\n    setSettledMountRange({ min: settled - 1, max: settled + 1 });\n\n    if (settled !== displayIndex) {\n      setTransitionEnabled(false);\n      setDisplayIndex(settled);\n    }\n  }\n\n  // Hover and focus only ever pause the timer, so without one they are not worth tracking.\n  const tracksPointerPause = autoPlay && pauseOnHover;\n  const isPaused = hovered || focused || dragging;\n  useEffect(() => {\n    if (!autoPlay || count <= 1 || isPaused) return;\n    const timer = setInterval(goNext, autoPlayInterval);\n    return () => clearInterval(timer);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [autoPlay, autoPlayInterval, isPaused, realIndex, count]);\n\n  // A drag follows the pointer through the node, not through state: a render per move event would\n  // rebuild every slide and dot for something only the track's transform sees.\n  const dragRef = useRef<{\n    startX: number;\n    startY: number;\n    width: number;\n    axisLocked: boolean;\n    active: boolean;\n    offsetPercent: number;\n  } | null>(null);\n\n  function handlePointerDown(event: React.PointerEvent<HTMLDivElement>) {\n    if (!swipeable || count <= 1) return;\n    // Another mouse button opens a menu, which can keep its release from the page.\n    if (event.button !== 0) return;\n    dragRef.current = {\n      startX: event.clientX,\n      startY: event.clientY,\n      // Measured once: measuring per move would force a layout between the transform writes.\n      width: containerRef.current?.getBoundingClientRect().width || 1,\n      axisLocked: false,\n      active: true,\n      offsetPercent: 0,\n    };\n  }\n\n  function handlePointerMove(event: React.PointerEvent<HTMLDivElement>) {\n    const drag = dragRef.current;\n    if (!drag) return;\n    // No button held, so its release went somewhere the track never heard of: over the dots, an\n    // arrow, outside the page, or into a context menu. Without this, every hover after it dragged.\n    if (event.buttons === 0) {\n      handlePointerUp();\n      return;\n    }\n    if (!drag.active) return;\n\n    const deltaX = event.clientX - drag.startX;\n    const deltaY = event.clientY - drag.startY;\n\n    if (!drag.axisLocked) {\n      if (Math.abs(deltaX) < AXIS_LOCK_THRESHOLD && Math.abs(deltaY) < AXIS_LOCK_THRESHOLD) return;\n      if (Math.abs(deltaY) > Math.abs(deltaX)) {\n        drag.active = false;\n        return;\n      }\n      drag.axisLocked = true;\n      // Never on pointerdown: a captured pointer sends its `click` to the capture target too, so\n      // capturing earlier would swallow every click meant for a button inside a slide.\n      event.currentTarget.setPointerCapture?.(event.pointerId);\n      setDragging(true);\n      setTransitionEnabled(false);\n    }\n\n    drag.offsetPercent = (deltaX / drag.width) * 100;\n    if (trackRef.current) {\n      trackRef.current.style.transform = buildTrackTransform(displayIndex, drag.offsetPercent);\n    }\n  }\n\n  function handlePointerUp() {\n    const drag = dragRef.current;\n    if (!drag) return;\n    dragRef.current = null;\n    if (!drag.axisLocked) {\n      setDragging(false);\n      return;\n    }\n\n    setTransitionEnabled(true);\n    setDragging(false);\n\n    // React never saw the dragged transform, so it has to be handed back by hand, and always: a\n    // drag that changes no index would otherwise strand the track off-screen. One that does change\n    // it overwrites this in the same tick, and still animates on from where the pointer left off.\n    if (trackRef.current) {\n      trackRef.current.style.transition = '';\n      trackRef.current.style.transform = buildTrackTransform(displayIndex, 0);\n    }\n\n    if (Math.abs(drag.offsetPercent) >= DRAG_COMMIT_RATIO * 100) {\n      if (drag.offsetPercent < 0) goNext();\n      else goPrev();\n    }\n  }\n\n  // Arrow keys belong to the carousel's own controls, never to the slides: a text field or a\n  // scrollable region in a slide has to keep its own. `defaultPrevented` leaves the dots to their\n  // own handling below.\n  function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n    if (event.defaultPrevented || trackRef.current?.contains(event.target as Node)) return;\n\n    if (event.key === 'ArrowLeft') {\n      event.preventDefault();\n      goPrev();\n    } else if (event.key === 'ArrowRight') {\n      event.preventDefault();\n      goNext();\n    }\n  }\n\n  function handleDotsKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n    let next: number | null = null;\n    if (event.key === 'ArrowLeft') next = realIndex - 1;\n    else if (event.key === 'ArrowRight') next = realIndex + 1;\n    else if (event.key === 'Home') next = 0;\n    else if (event.key === 'End') next = count - 1;\n    if (next === null) return;\n\n    event.preventDefault();\n    const clamped = clampIndex(next, count, canLoop);\n    goToIndex(clamped);\n    // The selected dot is the one holding the tab stop, so focus has to travel with the selection.\n    (dotsRef.current?.children[clamped] as HTMLElement | undefined)?.focus();\n  }\n\n  // The dots are the APG tablist over the slides. Without them the slides describe themselves as\n  // plain slides rather than as panels.\n  const hasTabs = showDots && count > 1;\n  const dotId = (index: number) => `${instanceId}-dot-${index}`;\n  const slideId = (index: number) => `${instanceId}-slide-${index}`;\n\n  // An unnamed landmark is worse than none, so the carousel is only a region once it has a name.\n  const isNamed = rest['aria-label'] !== undefined || rest['aria-labelledby'] !== undefined;\n\n  function slideSemantics(index: number) {\n    if (hasTabs) {\n      return { id: slideId(index), role: 'tabpanel', 'aria-labelledby': dotId(index) };\n    }\n    return {\n      id: slideId(index),\n      role: 'group',\n      'aria-roledescription': 'slide',\n      'aria-label': `${index + 1} of ${count}`,\n    };\n  }\n\n  const isPrevDisabled = !canLoop && realIndex <= 0;\n  const isNextDisabled = !canLoop && realIndex >= count - 1;\n\n  const trackTransform = buildTrackTransform(displayIndex, 0);\n\n  return (\n    <div\n      {...rest}\n      ref={containerRef}\n      role={isNamed ? 'region' : 'group'}\n      aria-roledescription='carousel'\n      className={cx(styles.carousel, adaptiveHeight && styles.carouselAdaptive, classNames?.root, className)}\n      style={{ height: adaptiveHeight ? contentHeight : undefined, ...style, ...slotStyles?.root }}\n      onMouseEnter={tracksPointerPause ? () => setHovered(true) : undefined}\n      onMouseLeave={tracksPointerPause ? () => setHovered(false) : undefined}\n      onFocus={tracksPointerPause ? () => setFocused(true) : undefined}\n      onBlur={tracksPointerPause ? () => setFocused(false) : undefined}\n      onKeyDown={handleKeyDown}\n    >\n      <div\n        ref={trackRef}\n        className={cx(styles.carouselTrack, adaptiveHeight && styles.carouselTrackAdaptive, classNames?.track)}\n        style={{\n          transform: trackTransform,\n          transition: transitionEnabled ? undefined : 'none',\n          ...slotStyles?.track,\n        }}\n        aria-live={autoPlay ? 'off' : 'polite'}\n        onTransitionEnd={handleTrackTransitionEnd}\n        onPointerDown={handlePointerDown}\n        onPointerMove={handlePointerMove}\n        onPointerUp={handlePointerUp}\n        onPointerCancel={handlePointerUp}\n      >\n        {extendedSlides.slice(firstRendered, lastRendered + 1).map((slide, i) => {\n          const idx = firstRendered + i;\n          const isClonePosition = canLoop && (idx === 0 || idx === lastPosition);\n          const logicalIndex = idx - offset;\n          const isActive = !isClonePosition && logicalIndex === realIndex;\n\n          return (\n            <div\n              key={`carousel-slide-${idx}`}\n              {...(isClonePosition ? {} : slideSemantics(logicalIndex))}\n              className={cx(styles.carouselSlide, classNames?.slide)}\n              style={{ transform: `translateX(${idx * 100}%)`, ...slotStyles?.slide }}\n              inert={isActive ? undefined : true}\n            >\n              <CarouselSlideContext.Provider value={isActive}>{slide}</CarouselSlideContext.Provider>\n            </div>\n          );\n        })}\n      </div>\n\n      {showArrows && count > 1 && (\n        <>\n          <ActionIcon\n            styleType={ButtonStyleType.Filled}\n            color={ARROW_COLOR}\n            icon={faChevronLeft}\n            title={text.previous}\n            aria-label={text.previous}\n            aria-disabled={isPrevDisabled || undefined}\n            onClick={() => {\n              if (!isPrevDisabled) goPrev();\n            }}\n            className={cx(\n              styles.carouselPrevButton,\n              isPrevDisabled && styles.carouselButtonDisabled,\n              classNames?.prevButton,\n            )}\n            style={slotStyles?.prevButton}\n          />\n          <ActionIcon\n            styleType={ButtonStyleType.Filled}\n            color={ARROW_COLOR}\n            icon={faChevronRight}\n            title={text.next}\n            aria-label={text.next}\n            aria-disabled={isNextDisabled || undefined}\n            onClick={() => {\n              if (!isNextDisabled) goNext();\n            }}\n            className={cx(\n              styles.carouselNextButton,\n              isNextDisabled && styles.carouselButtonDisabled,\n              classNames?.nextButton,\n            )}\n            style={slotStyles?.nextButton}\n          />\n        </>\n      )}\n\n      {hasTabs && (\n        <div\n          ref={dotsRef}\n          role='tablist'\n          aria-label={text.slides}\n          className={cx(\n            styles.carouselDots,\n            // Both ends always: the strip's own padding holds the outer dots clear of the fade\n            // until there is something to scroll, so this needs no measuring.\n            fadeStyles.scrollFade,\n            fadeStyles.scrollFadeX,\n            fadeStyles.scrollFadeFadedStart,\n            fadeStyles.scrollFadeFadedEnd,\n            classNames?.dots,\n          )}\n          style={slotStyles?.dots}\n          onKeyDown={handleDotsKeyDown}\n        >\n          {slides.map((_, index) => (\n            <InteractableDiv\n              key={`carousel-dot-${index}`}\n              id={dotId(index)}\n              role='tab'\n              aria-label={text.goToSlide(index + 1)}\n              aria-selected={index === realIndex}\n              // Only at a panel that exists: `lazyRender` leaves most of them out.\n              aria-controls={isRendered(index + offset) ? slideId(index) : undefined}\n              // One tab stop for the whole set, arrow keys inside it.\n              tabIndex={index === realIndex ? 0 : -1}\n              onClick={() => goToIndex(index)}\n              className={cx(styles.carouselDot, index === realIndex && styles.carouselDotActive, classNames?.dot)}\n              style={slotStyles?.dot}\n            />\n          ))}\n        </div>\n      )}\n    </div>\n  );\n}\n\n/** Where the track is on screen, mid-transition included; null without a layout to read it from. */\nfunction shownPosition(track: HTMLElement | null): number | null {\n  const width = track?.offsetWidth;\n  if (!track || !width) return null;\n  const { m41 } = new DOMMatrixReadOnly(getComputedStyle(track).transform);\n  // Rounded, so a track at rest reads as a whole slide despite the float error in the matrix.\n  return Math.round((-m41 / width) * 100) / 100;\n}\n\n/** The track is one slide wide, so both are in slide widths. */\nfunction buildTrackTransform(position: number, dragPercent: number): string {\n  return `translateX(calc(${-position * 100}% + ${dragPercent}%))`;\n}\n\nfunction clampIndex(index: number, count: number, canLoop: boolean): number {\n  if (count === 0) return 0;\n  if (canLoop) return ((index % count) + count) % count;\n  return Math.max(0, Math.min(count - 1, index));\n}\n\nexport default Carousel;\n"],"mappings":"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"}