@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
1 lines • 9.59 kB
Source Map (JSON)
{"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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"}