{"version":3,"file":"Carousel.mjs","names":["useControlledState"],"sources":["../../../src/base-ui/Carousel/Carousel.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport {\n  Children,\n  isValidElement,\n  memo,\n  useEffect,\n  useImperativeHandle,\n  useLayoutEffect,\n  useRef,\n  useState,\n} from 'react';\nimport useControlledState from 'use-merge-value';\n\nimport ActionIcon from '@/base-ui/ActionIcon';\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport { styles } from './style';\nimport type { CarouselProps } from './type';\n\nconst DEFAULT_INTERVAL = 5000;\nconst SWIPE_THRESHOLD = 40;\nconst ARROW_SIZE = { blockSize: 28, borderRadius: '50%', size: 16 };\n\nconst Carousel = memo<CarouselProps>(\n  ({\n    adaptiveHeight = false,\n    arrows = false,\n    autoplay = false,\n    children,\n    className,\n    defaultIndex = 0,\n    dots = true,\n    index,\n    loop = true,\n    onIndexChange,\n    onPointerEnter,\n    onPointerLeave,\n    pauseOnHover = true,\n    ref,\n    ...rest\n  }) => {\n    const slides = Children.toArray(children);\n    const count = slides.length;\n    const keys = slides.map((slide, slideIndex) =>\n      isValidElement(slide) && slide.key !== null ? slide.key : slideIndex,\n    );\n    const [mergedIndex, setMergedIndex] = useControlledState(defaultIndex, {\n      onChange: onIndexChange,\n      value: index,\n    });\n    const current = Math.min(Math.max(mergedIndex, 0), Math.max(count - 1, 0));\n\n    const [hovered, setHovered] = useState(false);\n    const [height, setHeight] = useState<number>();\n    const slidesRef = useRef<(HTMLDivElement | null)[]>([]);\n    const swipeStartXRef = useRef<number | null>(null);\n\n    const goTo = useEventCallback((target: number) => {\n      if (count === 0) return;\n      const next = loop\n        ? ((target % count) + count) % count\n        : Math.min(Math.max(target, 0), count - 1);\n      if (next !== current) setMergedIndex(next);\n    });\n    const next = useEventCallback(() => goTo(current + 1));\n    const prev = useEventCallback(() => goTo(current - 1));\n\n    useImperativeHandle(ref, () => ({ goTo, next, prev }), [goTo, next, prev]);\n\n    const interval = autoplay === true ? DEFAULT_INTERVAL : autoplay || 0;\n    const paused = pauseOnHover && hovered;\n    const atEnd = !loop && current >= count - 1;\n\n    useEffect(() => {\n      if (!interval || paused || count < 2 || atEnd) return;\n      const timer = setTimeout(next, interval);\n      return () => clearTimeout(timer);\n    }, [interval, paused, count, atEnd, current, next]);\n\n    useLayoutEffect(() => {\n      const slide = slidesRef.current[current];\n      if (!adaptiveHeight || !slide) return;\n      setHeight(slide.offsetHeight);\n      if (typeof ResizeObserver === 'undefined') return;\n      const observer = new ResizeObserver(() => setHeight(slide.offsetHeight));\n      observer.observe(slide);\n      return () => observer.disconnect();\n    }, [adaptiveHeight, current, count]);\n\n    return (\n      <div\n        aria-roledescription=\"carousel\"\n        className={cx(styles.root, className)}\n        role=\"region\"\n        onPointerEnter={(event) => {\n          onPointerEnter?.(event);\n          setHovered(true);\n        }}\n        onPointerLeave={(event) => {\n          onPointerLeave?.(event);\n          setHovered(false);\n        }}\n        {...rest}\n      >\n        <div\n          className={styles.viewport}\n          style={adaptiveHeight && height !== undefined ? { height } : undefined}\n          onPointerCancel={() => {\n            swipeStartXRef.current = null;\n          }}\n          onPointerDown={(event) => {\n            if (event.pointerType !== 'mouse') swipeStartXRef.current = event.clientX;\n          }}\n          onPointerUp={(event) => {\n            if (swipeStartXRef.current === null) return;\n            const delta = event.clientX - swipeStartXRef.current;\n            swipeStartXRef.current = null;\n            if (Math.abs(delta) < SWIPE_THRESHOLD) return;\n            if (delta < 0) next();\n            else prev();\n          }}\n        >\n          <div\n            aria-live={interval && !paused ? 'off' : 'polite'}\n            className={cx(styles.track, adaptiveHeight && styles.trackAdaptive)}\n            style={{ transform: `translateX(-${current * 100}%)` }}\n          >\n            {slides.map((slide, slideIndex) => {\n              const active = slideIndex === current;\n              return (\n                <div\n                  aria-hidden={!active || undefined}\n                  aria-label={`${slideIndex + 1} of ${count}`}\n                  aria-roledescription=\"slide\"\n                  className={styles.slide}\n                  inert={!active}\n                  key={keys[slideIndex]}\n                  role=\"group\"\n                  ref={(node) => {\n                    slidesRef.current[slideIndex] = node;\n                  }}\n                >\n                  {slide}\n                </div>\n              );\n            })}\n          </div>\n          {arrows && count > 1 && (\n            <>\n              <ActionIcon\n                aria-label=\"Previous slide\"\n                className={styles.arrow}\n                disabled={!loop && current === 0}\n                icon={ChevronLeft}\n                size={ARROW_SIZE}\n                style={{ insetInlineStart: 8 }}\n                onClick={prev}\n              />\n              <ActionIcon\n                aria-label=\"Next slide\"\n                className={styles.arrow}\n                disabled={atEnd}\n                icon={ChevronRight}\n                size={ARROW_SIZE}\n                style={{ insetInlineEnd: 8 }}\n                onClick={next}\n              />\n            </>\n          )}\n        </div>\n        {dots && count > 1 && (\n          <div className={styles.dots}>\n            {slides.map((_, dotIndex) => (\n              <button\n                aria-current={dotIndex === current || undefined}\n                aria-label={`Go to slide ${dotIndex + 1}`}\n                className={styles.dot}\n                key={keys[dotIndex]}\n                type=\"button\"\n                onClick={() => goTo(dotIndex)}\n              />\n            ))}\n          </div>\n        )}\n      </div>\n    );\n  },\n);\n\nCarousel.displayName = 'Carousel';\n\nexport default Carousel;\n"],"mappings":"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"}