@gnome-ui/react 1.49.1 → 1.50.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Carousel/Carousel.cjs +1 -1
- package/dist/components/Carousel/Carousel.cjs.map +1 -1
- package/dist/components/Carousel/Carousel.d.ts +29 -3
- package/dist/components/Carousel/Carousel.js +165 -86
- package/dist/components/Carousel/Carousel.js.map +1 -1
- package/dist/components/Carousel/Carousel.module.css.cjs +1 -1
- package/dist/components/Carousel/Carousel.module.css.cjs.map +1 -1
- package/dist/components/Carousel/Carousel.module.css.js +11 -11
- package/dist/components/Carousel/Carousel.module.css.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +6 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Carousel.js","names":[],"sources":["../../../src/components/Carousel/Carousel.tsx"],"sourcesContent":["import {\n Children,\n type HTMLAttributes,\n type KeyboardEvent,\n type MouseEvent,\n type PointerEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nimport styles from './Carousel.module.css';\n\n// ─── CarouselIndicatorDots ────────────────────────────────────────────────────\n\nexport interface CarouselIndicatorDotsProps extends HTMLAttributes<HTMLDivElement> {\n /** Total number of pages. */\n pages: number;\n /** Zero-based index of the current page. */\n currentPage: number;\n /** Called when the user clicks a dot. */\n onPageSelected?: (index: number) => void;\n}\n\n/**\n * Dot-style page indicator for `Carousel`.\n * Mirrors `AdwCarouselIndicatorDots`.\n */\nexport const CarouselIndicatorDots = ({\n pages,\n currentPage,\n onPageSelected,\n className,\n ...props\n}: CarouselIndicatorDotsProps) => {\n return (\n <div\n className={[styles.indicatorDots, className].filter(Boolean).join(' ')}\n role=\"tablist\"\n aria-label=\"Carousel pages\"\n {...props}\n >\n {Array.from({ length: pages }, (_, i) => (\n <button\n key={i}\n type=\"button\"\n role=\"tab\"\n aria-selected={i === currentPage}\n aria-label={`Page ${i + 1}`}\n className={[styles.dot, i === currentPage ? styles.dotActive : null]\n .filter(Boolean)\n .join(' ')}\n onClick={() => onPageSelected?.(i)}\n />\n ))}\n </div>\n );\n};\n\n// ─── CarouselIndicatorLines ───────────────────────────────────────────────────\n\nexport interface CarouselIndicatorLinesProps extends HTMLAttributes<HTMLDivElement> {\n /** Total number of pages. */\n pages: number;\n /** Zero-based index of the current page. */\n currentPage: number;\n /** Called when the user clicks a line. */\n onPageSelected?: (index: number) => void;\n}\n\n/**\n * Line-style page indicator for `Carousel`.\n * Mirrors `AdwCarouselIndicatorLines`.\n */\nexport const CarouselIndicatorLines = ({\n pages,\n currentPage,\n onPageSelected,\n className,\n ...props\n}: CarouselIndicatorLinesProps) => {\n return (\n <div\n className={[styles.indicatorLines, className].filter(Boolean).join(' ')}\n role=\"tablist\"\n aria-label=\"Carousel pages\"\n {...props}\n >\n {Array.from({ length: pages }, (_, i) => (\n <button\n key={i}\n type=\"button\"\n role=\"tab\"\n aria-selected={i === currentPage}\n aria-label={`Page ${i + 1}`}\n className={[styles.line, i === currentPage ? styles.lineActive : null]\n .filter(Boolean)\n .join(' ')}\n onClick={() => onPageSelected?.(i)}\n />\n ))}\n </div>\n );\n};\n\n// ─── Carousel ─────────────────────────────────────────────────────────────────\n\nexport interface CarouselProps extends HTMLAttributes<HTMLDivElement> {\n children?: ReactNode;\n /**\n * Scroll orientation.\n * @default \"horizontal\"\n */\n orientation?: 'horizontal' | 'vertical';\n /**\n * Gap between pages in px.\n * @default 0\n */\n spacing?: number;\n /**\n * Allow infinite looping (wraps around on the last/first page).\n * @default false\n */\n loop?: boolean;\n /**\n * Number of slides visible at once. Supports fractional values (e.g. `1.5`)\n * to peek at the edge of the next slide.\n * @default 1\n */\n visibleSlides?: number;\n /** Called whenever the visible page changes. */\n onPageChanged?: (index: number) => void;\n /**\n * Controlled current page index.\n * When omitted the carousel manages page state internally.\n */\n page?: number;\n}\n\n/**\n * Swipeable content carousel.\n *\n * Mirrors `AdwCarousel`. Uses CSS scroll-snapping for smooth, native-feeling\n * page transitions. Supports keyboard navigation (arrow keys), touch/mouse drag,\n * and velocity-based flick gestures.\n *\n * Pair with `CarouselIndicatorDots` or `CarouselIndicatorLines` for pagination UI.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Carousel.html\n */\nexport const Carousel = ({\n children,\n orientation = 'horizontal',\n spacing = 0,\n loop = false,\n visibleSlides = 1,\n onPageChanged,\n page: controlledPage,\n className,\n ...props\n}: CarouselProps) => {\n const scrollRef = useRef<HTMLDivElement>(null);\n const pageCount = Children.count(children);\n const [internalPage, setInternalPage] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n const isControlled = controlledPage !== undefined;\n const currentPage = isControlled ? controlledPage : internalPage;\n\n // Refs for drag state — avoids stale closures in pointer handlers\n const draggingRef = useRef(false);\n const hasDraggedRef = useRef(false);\n const dragStartRef = useRef({ pos: 0, scroll: 0, time: 0 });\n const snapRestoreTimerRef = useRef(0);\n\n // Scroll distance per page: (containerSize - spacing*(visibleSlides-1)) / visibleSlides + spacing\n // Simplifies to: (containerSize + spacing) / visibleSlides\n const getPageSize = useCallback(() => {\n const el = scrollRef.current;\n if (!el) {\n return 1;\n }\n const containerSize = orientation === 'horizontal' ? el.clientWidth : el.clientHeight;\n return (containerSize + spacing) / visibleSlides;\n }, [orientation, spacing, visibleSlides]);\n\n const scrollToPage = useCallback(\n (index: number, behavior: ScrollBehavior = 'smooth') => {\n const el = scrollRef.current;\n if (!el) {\n return;\n }\n const offset = getPageSize() * index;\n if (orientation === 'horizontal') {\n el.scrollTo({ left: offset, behavior });\n } else {\n el.scrollTo({ top: offset, behavior });\n }\n },\n [orientation, getPageSize],\n );\n\n // Scroll to the controlled page when it changes externally\n useEffect(() => {\n if (!isControlled) {\n return;\n }\n scrollToPage(controlledPage, 'smooth');\n }, [controlledPage, scrollToPage, isControlled]);\n\n // Detect page changes from native scroll (touch) — skipped during mouse drag\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) {\n return;\n }\n\n const handleScroll = () => {\n if (draggingRef.current) {\n return;\n }\n const pageSize = getPageSize();\n const scroll = orientation === 'horizontal' ? el.scrollLeft : el.scrollTop;\n const idx = Math.round(scroll / pageSize);\n const clamped = Math.max(0, Math.min(idx, pageCount - 1));\n if (!isControlled) {\n setInternalPage(clamped);\n }\n onPageChanged?.(clamped);\n };\n\n el.addEventListener('scroll', handleScroll, { passive: true });\n return () => el.removeEventListener('scroll', handleScroll);\n }, [orientation, pageCount, isControlled, onPageChanged, getPageSize]);\n\n // ── Keyboard navigation ───────────────────────────────────────────────────\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n const isForward =\n orientation === 'horizontal' ? e.key === 'ArrowRight' : e.key === 'ArrowDown';\n const isBack = orientation === 'horizontal' ? e.key === 'ArrowLeft' : e.key === 'ArrowUp';\n\n if (!isForward && !isBack) {\n return;\n }\n e.preventDefault();\n\n const next = isForward\n ? loop\n ? (currentPage + 1) % pageCount\n : Math.min(currentPage + 1, pageCount - 1)\n : loop\n ? (currentPage - 1 + pageCount) % pageCount\n : Math.max(currentPage - 1, 0);\n\n scrollToPage(next);\n if (!isControlled) {\n setInternalPage(next);\n }\n onPageChanged?.(next);\n },\n [currentPage, pageCount, loop, orientation, scrollToPage, isControlled, onPageChanged],\n );\n\n // ── Drag (mouse + touch + pen) ───────────────────────────────────────────\n\n const handlePointerDown = useCallback(\n (e: PointerEvent<HTMLDivElement>) => {\n // Ignore non-primary mouse buttons\n if (e.pointerType === 'mouse' && e.button !== 0) {\n return;\n }\n const el = scrollRef.current;\n if (!el) {\n return;\n }\n\n // Disable scroll-snap immediately via DOM — not via React state, which would\n // only apply after the next render, letting scroll-snap fight the first moves.\n window.clearTimeout(snapRestoreTimerRef.current);\n el.style.scrollSnapType = 'none';\n\n // Capture so pointermove/up fire even when the pointer leaves the element\n e.currentTarget.setPointerCapture(e.pointerId);\n draggingRef.current = true;\n hasDraggedRef.current = false;\n setIsDragging(true);\n dragStartRef.current = {\n pos: orientation === 'horizontal' ? e.clientX : e.clientY,\n scroll: orientation === 'horizontal' ? el.scrollLeft : el.scrollTop,\n time: Date.now(),\n };\n },\n [orientation],\n );\n\n const handlePointerMove = useCallback(\n (e: PointerEvent<HTMLDivElement>) => {\n if (!draggingRef.current) {\n return;\n }\n const el = scrollRef.current;\n if (!el) {\n return;\n }\n\n const pos = orientation === 'horizontal' ? e.clientX : e.clientY;\n const delta = dragStartRef.current.pos - pos;\n\n if (Math.abs(delta) > 4) {\n hasDraggedRef.current = true;\n }\n\n // Apply boundary resistance: dampen movement past first/last slide\n const maxScroll =\n orientation === 'horizontal'\n ? el.scrollWidth - el.clientWidth\n : el.scrollHeight - el.clientHeight;\n let newScroll = dragStartRef.current.scroll + delta;\n\n if (newScroll < 0) {\n newScroll *= 0.3;\n } else if (newScroll > maxScroll) {\n newScroll = maxScroll + (newScroll - maxScroll) * 0.3;\n }\n\n if (orientation === 'horizontal') {\n el.scrollLeft = newScroll;\n } else {\n el.scrollTop = newScroll;\n }\n },\n [orientation],\n );\n\n const handlePointerUp = useCallback(\n (e: PointerEvent<HTMLDivElement>) => {\n if (!draggingRef.current) {\n return;\n }\n\n draggingRef.current = false;\n setIsDragging(false);\n\n const el = scrollRef.current;\n\n if (!hasDraggedRef.current || !el) {\n // No real drag — restore snap immediately\n el?.style.removeProperty('scroll-snap-type');\n return;\n }\n\n const isCancel = e.type === 'pointercancel';\n const pos = isCancel\n ? dragStartRef.current.pos\n : orientation === 'horizontal'\n ? e.clientX\n : e.clientY;\n\n const delta = dragStartRef.current.pos - pos;\n const elapsed = Math.max(Date.now() - dragStartRef.current.time, 1);\n const velocity = isCancel ? 0 : delta / elapsed; // px/ms, positive = forward\n const pageSize = getPageSize();\n const scroll = orientation === 'horizontal' ? el.scrollLeft : el.scrollTop;\n\n let target: number;\n if (Math.abs(velocity) > 0.3) {\n // Flick gesture — advance in the swipe direction\n target = velocity > 0 ? Math.ceil(scroll / pageSize) : Math.floor(scroll / pageSize);\n } else {\n // Slow drag — snap to nearest page\n target = Math.round(scroll / pageSize);\n }\n\n if (loop) {\n target = ((target % pageCount) + pageCount) % pageCount;\n } else {\n target = Math.max(0, Math.min(target, pageCount - 1));\n }\n\n // Scroll to target while snap is still disabled, then re-enable once settled\n scrollToPage(target);\n if (!isControlled) {\n setInternalPage(target);\n }\n onPageChanged?.(target);\n\n // Re-enable snap after the smooth scroll animation completes (~300 ms)\n snapRestoreTimerRef.current = window.setTimeout(() => {\n el.style.removeProperty('scroll-snap-type');\n }, 350);\n },\n [orientation, getPageSize, loop, pageCount, scrollToPage, isControlled, onPageChanged],\n );\n\n // Clean up the snap-restore timer on unmount\n useEffect(() => {\n return () => window.clearTimeout(snapRestoreTimerRef.current);\n }, []);\n\n // Prevent click events that fire after a drag (e.g. links/buttons inside slides)\n const handleClick = useCallback((e: MouseEvent) => {\n if (hasDraggedRef.current) {\n e.preventDefault();\n e.stopPropagation();\n hasDraggedRef.current = false;\n }\n }, []);\n\n const isHorizontal = orientation === 'horizontal';\n\n // Slide width when showing more than one at a time\n const slideFlexBasis =\n visibleSlides !== 1\n ? `calc((100% - ${spacing * (visibleSlides - 1)}px) / ${visibleSlides})`\n : undefined;\n\n return (\n <div\n ref={scrollRef}\n role=\"region\"\n aria-roledescription=\"carousel\"\n tabIndex={0}\n className={[\n styles.carousel,\n isHorizontal ? styles.horizontal : styles.vertical,\n isDragging ? styles.dragging : null,\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={isHorizontal ? { columnGap: spacing || undefined } : { rowGap: spacing || undefined }}\n onKeyDown={handleKeyDown}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerUp}\n onClick={handleClick}\n {...props}\n >\n {Children.map(children, (child, i) => (\n <div\n key={i}\n className={styles.slide}\n role=\"group\"\n aria-roledescription=\"slide\"\n aria-label={`${i + 1} of ${pageCount}`}\n style={slideFlexBasis ? { flex: `0 0 ${slideFlexBasis}` } : undefined}\n >\n {child}\n </div>\n ))}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Carousel.js","names":[],"sources":["../../../src/components/Carousel/Carousel.tsx"],"sourcesContent":["import {\n Children,\n type CSSProperties,\n type HTMLAttributes,\n type KeyboardEvent,\n type MouseEvent,\n type PointerEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nimport styles from './Carousel.module.css';\n\ntype IndicatorPosition = 'top' | 'bottom' | 'left' | 'right';\n\nconst INDICATOR_FLEX_DIR: Record<IndicatorPosition, CSSProperties['flexDirection']> = {\n bottom: 'column',\n top: 'column-reverse',\n left: 'row-reverse',\n right: 'row',\n};\n\n// ─── CarouselIndicatorDots ────────────────────────────────────────────────────\n\nexport interface CarouselIndicatorDotsProps extends HTMLAttributes<HTMLDivElement> {\n /** Total number of pages. */\n pages: number;\n /** Zero-based index of the current page. */\n currentPage: number;\n /** Called when the user clicks a dot. */\n onPageSelected?: (index: number) => void;\n}\n\n/**\n * Dot-style page indicator for `Carousel`.\n * Mirrors `AdwCarouselIndicatorDots`.\n */\nexport const CarouselIndicatorDots = ({\n pages,\n currentPage,\n onPageSelected,\n className,\n ...props\n}: CarouselIndicatorDotsProps) => {\n return (\n <div\n className={[styles.indicatorDots, className].filter(Boolean).join(' ')}\n role=\"tablist\"\n aria-label=\"Carousel pages\"\n {...props}\n >\n {Array.from({ length: pages }, (_, i) => (\n <button\n key={i}\n type=\"button\"\n role=\"tab\"\n aria-selected={i === currentPage}\n aria-label={`Page ${i + 1}`}\n className={[styles.dot, i === currentPage ? styles.dotActive : null]\n .filter(Boolean)\n .join(' ')}\n onClick={() => onPageSelected?.(i)}\n />\n ))}\n </div>\n );\n};\n\n// ─── CarouselIndicatorLines ───────────────────────────────────────────────────\n\nexport interface CarouselIndicatorLinesProps extends HTMLAttributes<HTMLDivElement> {\n /** Total number of pages. */\n pages: number;\n /** Zero-based index of the current page. */\n currentPage: number;\n /** Called when the user clicks a line. */\n onPageSelected?: (index: number) => void;\n}\n\n/**\n * Line-style page indicator for `Carousel`.\n * Mirrors `AdwCarouselIndicatorLines`.\n */\nexport const CarouselIndicatorLines = ({\n pages,\n currentPage,\n onPageSelected,\n className,\n ...props\n}: CarouselIndicatorLinesProps) => {\n return (\n <div\n className={[styles.indicatorLines, className].filter(Boolean).join(' ')}\n role=\"tablist\"\n aria-label=\"Carousel pages\"\n {...props}\n >\n {Array.from({ length: pages }, (_, i) => (\n <button\n key={i}\n type=\"button\"\n role=\"tab\"\n aria-selected={i === currentPage}\n aria-label={`Page ${i + 1}`}\n className={[styles.line, i === currentPage ? styles.lineActive : null]\n .filter(Boolean)\n .join(' ')}\n onClick={() => onPageSelected?.(i)}\n />\n ))}\n </div>\n );\n};\n\n// ─── Carousel ─────────────────────────────────────────────────────────────────\n\nexport interface CarouselProps extends HTMLAttributes<HTMLDivElement> {\n children?: ReactNode;\n /**\n * Scroll orientation.\n * @default \"horizontal\"\n */\n orientation?: 'horizontal' | 'vertical';\n /**\n * Gap between pages in px.\n * @default 0\n */\n spacing?: number;\n /**\n * Allow infinite looping (wraps around on the last/first page).\n * @default false\n */\n loop?: boolean;\n /**\n * Number of slides visible at once (integer ≥ 1). Navigation advances one\n * full group at a time, and the indicator shows one dot/line per group.\n * @default 1\n */\n visibleSlides?: number;\n /** Called whenever the visible page changes. */\n onPageChanged?: (index: number) => void;\n /**\n * Controlled current page index.\n * When omitted the carousel manages page state internally.\n */\n page?: number;\n /**\n * Automatically advance to the next slide. Pauses while the pointer\n * hovers over the carousel or during drag.\n * @default false\n */\n autoPlay?: boolean;\n /**\n * Milliseconds between automatic slide transitions.\n * Only used when `autoPlay` is `true`.\n * @default 3000\n */\n interval?: number;\n /**\n * Page indicator rendered alongside the carousel.\n * - `'dots'`: small circular dots (`CarouselIndicatorDots`)\n * - `'lines'`: short line segments (`CarouselIndicatorLines`)\n * - `'none'` or omitted: no indicator\n */\n indicator?: 'dots' | 'lines' | 'none';\n /**\n * Position of the indicator relative to the carousel.\n * @default 'bottom'\n */\n indicatorPosition?: IndicatorPosition;\n}\n\n/**\n * Swipeable content carousel.\n *\n * Mirrors `AdwCarousel`. Uses CSS scroll-snapping for smooth, native-feeling\n * page transitions. Supports keyboard navigation (arrow keys), touch/mouse drag,\n * and velocity-based flick gestures.\n *\n * Pair with `CarouselIndicatorDots` or `CarouselIndicatorLines` for pagination UI.\n *\n * @see https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.Carousel.html\n */\nexport const Carousel = ({\n children,\n orientation = 'horizontal',\n spacing = 0,\n loop = false,\n visibleSlides = 1,\n onPageChanged,\n page: controlledPage,\n autoPlay = false,\n interval = 3000,\n indicator,\n indicatorPosition = 'bottom',\n className,\n style,\n ...props\n}: CarouselProps) => {\n const scrollRef = useRef<HTMLDivElement>(null);\n const vSlides = Math.max(1, Math.floor(visibleSlides)); // enforce integer\n const slideCount = Children.count(children);\n const pageCount = Math.ceil(slideCount / vSlides);\n const [internalPage, setInternalPage] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n const isControlled = controlledPage !== undefined;\n const currentPage = isControlled ? controlledPage : internalPage;\n\n // Refs for drag state — avoids stale closures in pointer handlers\n const draggingRef = useRef(false);\n const hasDraggedRef = useRef(false);\n const dragStartRef = useRef({ pos: 0, scroll: 0, time: 0 });\n const snapRestoreTimerRef = useRef(0);\n // Suppresses handleScroll feedback during programmatic scrolls (e.g. dot clicks)\n const isProgrammaticScrollRef = useRef(false);\n const programmaticScrollTimerRef = useRef(0);\n // Auto-play: always-fresh snapshot of currentPage to avoid stale closure in setInterval\n const currentPageRef = useRef(currentPage);\n const isHoveringRef = useRef(false);\n useEffect(() => {\n currentPageRef.current = currentPage;\n });\n\n // Scroll distance per page = one full group = containerSize + spacing.\n // Each slide is (containerSize - spacing*(vSlides-1)) / vSlides wide, so\n // vSlides slides + (vSlides-1) gaps = containerSize, plus the inter-group\n // gap gives containerSize + spacing.\n const getPageSize = useCallback(() => {\n const el = scrollRef.current;\n if (!el) {\n return 1;\n }\n const containerSize = orientation === 'horizontal' ? el.clientWidth : el.clientHeight;\n const size = containerSize + spacing;\n return size > 0 ? size : 1;\n }, [orientation, spacing]);\n\n const scrollToPage = useCallback(\n (index: number, behavior: ScrollBehavior = 'smooth') => {\n const el = scrollRef.current;\n if (!el) {\n return;\n }\n // Suppress handleScroll feedback while the animation runs\n isProgrammaticScrollRef.current = true;\n window.clearTimeout(programmaticScrollTimerRef.current);\n programmaticScrollTimerRef.current = window.setTimeout(\n () => {\n isProgrammaticScrollRef.current = false;\n },\n behavior === 'smooth' ? 400 : 50,\n );\n\n const offset = getPageSize() * index;\n if (orientation === 'horizontal') {\n el.scrollTo({ left: offset, behavior });\n } else {\n el.scrollTo({ top: offset, behavior });\n }\n },\n [orientation, getPageSize],\n );\n\n // Scroll to the controlled page when it changes externally\n useEffect(() => {\n if (!isControlled) {\n return;\n }\n scrollToPage(controlledPage, 'smooth');\n }, [controlledPage, scrollToPage, isControlled]);\n\n // Detect page changes from native scroll (touch) — skipped during mouse drag\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) {\n return;\n }\n\n const handleScroll = () => {\n if (draggingRef.current || isProgrammaticScrollRef.current) {\n return;\n }\n const pageSize = getPageSize();\n const scroll = orientation === 'horizontal' ? el.scrollLeft : el.scrollTop;\n const idx = Math.round(scroll / pageSize);\n const clamped = Math.max(0, Math.min(idx, pageCount - 1));\n if (!isControlled) {\n setInternalPage(clamped);\n }\n onPageChanged?.(clamped);\n };\n\n el.addEventListener('scroll', handleScroll, { passive: true });\n return () => el.removeEventListener('scroll', handleScroll);\n }, [orientation, pageCount, isControlled, onPageChanged, getPageSize]);\n\n // ── Keyboard navigation ───────────────────────────────────────────────────\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n const isForward =\n orientation === 'horizontal' ? e.key === 'ArrowRight' : e.key === 'ArrowDown';\n const isBack = orientation === 'horizontal' ? e.key === 'ArrowLeft' : e.key === 'ArrowUp';\n\n if (!isForward && !isBack) {\n return;\n }\n e.preventDefault();\n\n const next = isForward\n ? loop\n ? (currentPage + 1) % pageCount\n : Math.min(currentPage + 1, pageCount - 1)\n : loop\n ? (currentPage - 1 + pageCount) % pageCount\n : Math.max(currentPage - 1, 0);\n\n scrollToPage(next);\n if (!isControlled) {\n setInternalPage(next);\n }\n onPageChanged?.(next);\n },\n [currentPage, pageCount, loop, orientation, scrollToPage, isControlled, onPageChanged],\n );\n\n // ── Drag (mouse + touch + pen) ───────────────────────────────────────────\n\n const handlePointerDown = useCallback(\n (e: PointerEvent<HTMLDivElement>) => {\n // Ignore non-primary mouse buttons\n if (e.pointerType === 'mouse' && e.button !== 0) {\n return;\n }\n const el = scrollRef.current;\n if (!el) {\n return;\n }\n\n // Disable scroll-snap immediately via DOM — not via React state, which would\n // only apply after the next render, letting scroll-snap fight the first moves.\n window.clearTimeout(snapRestoreTimerRef.current);\n el.style.scrollSnapType = 'none';\n\n // Capture so pointermove/up fire even when the pointer leaves the element\n e.currentTarget.setPointerCapture(e.pointerId);\n draggingRef.current = true;\n hasDraggedRef.current = false;\n setIsDragging(true);\n dragStartRef.current = {\n pos: orientation === 'horizontal' ? e.clientX : e.clientY,\n scroll: orientation === 'horizontal' ? el.scrollLeft : el.scrollTop,\n time: Date.now(),\n };\n },\n [orientation],\n );\n\n const handlePointerMove = useCallback(\n (e: PointerEvent<HTMLDivElement>) => {\n if (!draggingRef.current) {\n return;\n }\n const el = scrollRef.current;\n if (!el) {\n return;\n }\n\n const pos = orientation === 'horizontal' ? e.clientX : e.clientY;\n const delta = dragStartRef.current.pos - pos;\n\n if (Math.abs(delta) > 4) {\n hasDraggedRef.current = true;\n }\n\n // Apply boundary resistance: dampen movement past first/last slide\n const maxScroll =\n orientation === 'horizontal'\n ? el.scrollWidth - el.clientWidth\n : el.scrollHeight - el.clientHeight;\n let newScroll = dragStartRef.current.scroll + delta;\n\n if (newScroll < 0) {\n newScroll *= 0.3;\n } else if (newScroll > maxScroll) {\n newScroll = maxScroll + (newScroll - maxScroll) * 0.3;\n }\n\n if (orientation === 'horizontal') {\n el.scrollLeft = newScroll;\n } else {\n el.scrollTop = newScroll;\n }\n },\n [orientation],\n );\n\n const handlePointerUp = useCallback(\n (e: PointerEvent<HTMLDivElement>) => {\n if (!draggingRef.current) {\n return;\n }\n\n draggingRef.current = false;\n setIsDragging(false);\n\n const el = scrollRef.current;\n\n if (!hasDraggedRef.current || !el) {\n // No real drag — restore snap immediately\n el?.style.removeProperty('scroll-snap-type');\n return;\n }\n\n const isCancel = e.type === 'pointercancel';\n const pos = isCancel\n ? dragStartRef.current.pos\n : orientation === 'horizontal'\n ? e.clientX\n : e.clientY;\n\n const delta = dragStartRef.current.pos - pos;\n const elapsed = Math.max(Date.now() - dragStartRef.current.time, 1);\n const velocity = isCancel ? 0 : delta / elapsed; // px/ms, positive = forward\n const pageSize = getPageSize();\n const scroll = orientation === 'horizontal' ? el.scrollLeft : el.scrollTop;\n\n const maxScroll =\n orientation === 'horizontal'\n ? el.scrollWidth - el.clientWidth\n : el.scrollHeight - el.clientHeight;\n\n let target: number;\n if (Math.abs(velocity) > 0.3) {\n const rawTarget =\n velocity > 0 ? Math.ceil(scroll / pageSize) : Math.floor(scroll / pageSize);\n // In loop mode the browser clamps scroll to [0, maxScroll], so a backward\n // flick from page 0 and a forward flick from the last page both produce a\n // rawTarget stuck at the boundary. Detect and wrap explicitly.\n if (loop && velocity < 0 && rawTarget === 0 && scroll <= 0) {\n target = pageCount - 1;\n } else if (loop && velocity > 0 && rawTarget === pageCount - 1 && scroll >= maxScroll) {\n target = 0;\n } else {\n target = rawTarget;\n }\n } else {\n // Slow drag — snap to nearest page\n target = Math.round(scroll / pageSize);\n }\n\n if (loop) {\n target = ((target % pageCount) + pageCount) % pageCount;\n } else {\n target = Math.max(0, Math.min(target, pageCount - 1));\n }\n\n // Scroll to target while snap is still disabled, then re-enable once settled\n scrollToPage(target);\n if (!isControlled) {\n setInternalPage(target);\n }\n onPageChanged?.(target);\n\n // Re-enable snap after the smooth scroll animation completes (~300 ms)\n snapRestoreTimerRef.current = window.setTimeout(() => {\n el.style.removeProperty('scroll-snap-type');\n }, 350);\n },\n [orientation, getPageSize, loop, pageCount, scrollToPage, isControlled, onPageChanged],\n );\n\n // Auto-play: advance one slide every `interval` ms, pausing on hover/drag\n useEffect(() => {\n if (!autoPlay || interval <= 0) {\n return;\n }\n\n const timer = window.setInterval(() => {\n if (draggingRef.current || isHoveringRef.current) {\n return;\n }\n const page = currentPageRef.current;\n if (!loop && page >= pageCount - 1) {\n return;\n }\n const next = loop ? (page + 1) % pageCount : page + 1;\n // Update ref immediately so back-to-back ticks see the correct page\n currentPageRef.current = next;\n scrollToPage(next);\n if (!isControlled) {\n setInternalPage(next);\n }\n onPageChanged?.(next);\n }, interval);\n\n return () => window.clearInterval(timer);\n }, [autoPlay, interval, loop, pageCount, scrollToPage, isControlled, onPageChanged]);\n\n // Clean up timers on unmount\n useEffect(() => {\n return () => {\n window.clearTimeout(snapRestoreTimerRef.current);\n window.clearTimeout(programmaticScrollTimerRef.current);\n };\n }, []);\n\n // Prevent click events that fire after a drag (e.g. links/buttons inside slides)\n const handleClick = useCallback((e: MouseEvent) => {\n if (hasDraggedRef.current) {\n e.preventDefault();\n e.stopPropagation();\n hasDraggedRef.current = false;\n }\n }, []);\n\n const isHorizontal = orientation === 'horizontal';\n const showIndicator = indicator === 'dots' || indicator === 'lines';\n const isSide = indicatorPosition === 'left' || indicatorPosition === 'right';\n\n // Slide width when showing more than one at a time\n const slideFlexBasis =\n vSlides !== 1 ? `calc((100% - ${spacing * (vSlides - 1)}px) / ${vSlides})` : undefined;\n\n const scrollContainer = (\n <div\n ref={scrollRef}\n role=\"region\"\n aria-roledescription=\"carousel\"\n tabIndex={0}\n className={[\n styles.carousel,\n isHorizontal ? styles.horizontal : styles.vertical,\n isDragging ? styles.dragging : null,\n showIndicator ? null : className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={{\n ...(isHorizontal ? { columnGap: spacing || undefined } : { rowGap: spacing || undefined }),\n ...(showIndicator && (isSide ? { flex: '1 1 0', width: 'auto' } : { flex: '1 1 auto' })),\n }}\n onMouseEnter={() => {\n isHoveringRef.current = true;\n }}\n onMouseLeave={() => {\n isHoveringRef.current = false;\n }}\n onKeyDown={handleKeyDown}\n onClick={handleClick}\n {...(showIndicator ? {} : props)}\n >\n {Children.map(children, (child, i) => (\n <div\n key={i}\n className={styles.slide}\n role=\"group\"\n aria-roledescription=\"slide\"\n aria-label={`${i + 1} of ${slideCount}`}\n style={{\n ...(slideFlexBasis ? { flex: `0 0 ${slideFlexBasis}` } : undefined),\n // Only the first slide of each group is a snap target; intermediate\n // slides would cause the carousel to stop mid-group.\n ...(vSlides > 1 && i % vSlides !== 0 ? { scrollSnapAlign: 'none' } : undefined),\n }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerUp}\n >\n {child}\n </div>\n ))}\n </div>\n );\n\n if (!showIndicator) {\n return scrollContainer;\n }\n\n const onPageSelected = (i: number) => {\n scrollToPage(i);\n if (!isControlled) {\n setInternalPage(i);\n }\n onPageChanged?.(i);\n };\n\n const indicatorStyle: CSSProperties | undefined = isSide\n ? { flexDirection: 'column', padding: '0 12px' }\n : undefined;\n\n return (\n <div\n className={className}\n style={{\n ...style,\n display: 'flex',\n flexDirection: INDICATOR_FLEX_DIR[indicatorPosition],\n }}\n {...props}\n >\n {scrollContainer}\n {indicator === 'dots' && (\n <CarouselIndicatorDots\n pages={pageCount}\n currentPage={currentPage}\n onPageSelected={onPageSelected}\n style={indicatorStyle}\n />\n )}\n {indicator === 'lines' && (\n <CarouselIndicatorLines\n pages={pageCount}\n currentPage={currentPage}\n onPageSelected={onPageSelected}\n style={indicatorStyle}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e={carousel:`
|
|
1
|
+
var e={carousel:`_carousel_78m8m_3`,horizontal:`_horizontal_78m8m_25`,vertical:`_vertical_78m8m_34`,dragging:`_dragging_78m8m_44`,slide:`_slide_78m8m_53`,indicatorDots:`_indicatorDots_78m8m_63`,dot:`_dot_78m8m_71`,dotActive:`_dotActive_78m8m_90`,indicatorLines:`_indicatorLines_78m8m_102`,line:`_line_78m8m_110`,lineActive:`_lineActive_78m8m_127`};exports.default=e;
|
|
2
2
|
//# sourceMappingURL=Carousel.module.css.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Carousel.module.css.cjs","names":[],"sources":["../../../src/components/Carousel/Carousel.module.css"],"sourcesContent":["/* ─── Carousel container ────────────────────────────────────────────────────── */\n\n.carousel {\n display: flex;\n overflow: hidden;\n overscroll-behavior: contain;\n outline: none;\n width: 100%;\n cursor: grab;\n scrollbar-width: none; /* Firefox */\n}\n\n.carousel::-webkit-scrollbar {\n display: none; /* Chrome / Safari / Edge */\n}\n\n.carousel:focus-visible {\n box-shadow:\n 0 0 0 var(--gnome-focus-ring-offset, 2px) var(--gnome-window-bg-color, #fafafa),\n 0 0 0 calc(var(--gnome-focus-ring-offset, 2px) + var(--gnome-focus-ring-width, 3px))\n var(--gnome-focus-ring-color, #3584e4);\n border-radius: var(--gnome-radius-md, 8px);\n}\n\n.horizontal {\n flex-direction: row;\n overflow: scroll hidden;\n scroll-snap-type: x mandatory;\n\n /* pan-y: browser handles vertical page scroll; we handle horizontal drag */\n touch-action: pan-y;\n}\n\n.vertical {\n flex-direction: column;\n overflow: hidden scroll;\n scroll-snap-type: y mandatory;\n\n /* pan-x: browser handles horizontal page scroll; we handle vertical drag */\n touch-action: pan-x;\n}\n\n/* Active mouse drag state */\n.dragging {\n cursor: grabbing;\n user-select: none;\n\n /* scroll-snap-type is disabled via inline style during drag */\n}\n\n/* ─── Slide ─────────────────────────────────────────────────────────────────── */\n\n.slide {\n flex: 0 0 100%;\n scroll-snap-align: start;\n min-width: 0;\n min-height: 0;\n}\n\n/* ─── Dot indicators ────────────────────────────────────────────────────────── */\n\n.indicatorDots {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: var(--gnome-space-2, 12px) 0;\n}\n\n.dot {\n appearance: none;\n border: none;\n padding: 0;\n cursor: pointer;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background-color: var(--gnome-window-fg-color, rgb(0 0 0 / 0.8));\n opacity: 0.2;\n transition:\n opacity var(--gnome-duration-fast) var(--gnome-easing-default),\n transform var(--gnome-duration-fast) var(--gnome-easing-default);\n}\n\n.dot:hover {\n opacity: 0.5;\n}\n\n.dotActive {\n opacity: 1;\n transform: scale(1.15);\n}\n\n.dot:focus-visible {\n outline: 2px solid var(--gnome-focus-ring-color, #3584e4);\n outline-offset: 2px;\n}\n\n/* ─── Line indicators ───────────────────────────────────────────────────────── */\n\n.indicatorLines {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: var(--gnome-space-2, 12px) 0;\n}\n\n.line {\n appearance: none;\n border: none;\n padding: 0;\n cursor: pointer;\n width: 24px;\n height: 3px;\n border-radius: 2px;\n background-color: var(--gnome-window-fg-color, rgb(0 0 0 / 0.8));\n opacity: 0.2;\n transition: opacity var(--gnome-duration-fast) var(--gnome-easing-default);\n}\n\n.line:hover {\n opacity: 0.5;\n}\n\n.lineActive {\n opacity: 1;\n}\n\n.line:focus-visible {\n outline: 2px solid var(--gnome-focus-ring-color, #3584e4);\n outline-offset: 2px;\n}\n\n/* ─── Reduced motion ────────────────────────────────────────────────────────── */\n\n@media (prefers-reduced-motion: reduce) {\n .carousel {\n scroll-behavior: auto;\n }\n\n .dot,\n .line {\n transition: none;\n }\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Carousel.module.css.cjs","names":[],"sources":["../../../src/components/Carousel/Carousel.module.css"],"sourcesContent":["/* ─── Carousel container ────────────────────────────────────────────────────── */\n\n.carousel {\n display: flex;\n overflow: hidden;\n overscroll-behavior: contain;\n outline: none;\n width: 100%;\n cursor: grab;\n scrollbar-width: none; /* Firefox */\n}\n\n.carousel::-webkit-scrollbar {\n display: none; /* Chrome / Safari / Edge */\n}\n\n.carousel:focus-visible {\n box-shadow:\n 0 0 0 var(--gnome-focus-ring-offset, 2px) var(--gnome-window-bg-color, #fafafa),\n 0 0 0 calc(var(--gnome-focus-ring-offset, 2px) + var(--gnome-focus-ring-width, 3px))\n var(--gnome-focus-ring-color, #3584e4);\n border-radius: var(--gnome-radius-md, 8px);\n}\n\n.horizontal {\n flex-direction: row;\n overflow: scroll hidden;\n scroll-snap-type: x mandatory;\n\n /* pan-y: browser handles vertical page scroll; we handle horizontal drag */\n touch-action: pan-y;\n}\n\n.vertical {\n flex-direction: column;\n overflow: hidden scroll;\n scroll-snap-type: y mandatory;\n\n /* pan-x: browser handles horizontal page scroll; we handle vertical drag */\n touch-action: pan-x;\n}\n\n/* Active mouse drag state */\n.dragging {\n cursor: grabbing;\n user-select: none;\n\n /* scroll-snap-type is disabled via inline style during drag */\n}\n\n/* ─── Slide ─────────────────────────────────────────────────────────────────── */\n\n.slide {\n flex: 0 0 100%;\n scroll-snap-align: start;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n/* ─── Dot indicators ────────────────────────────────────────────────────────── */\n\n.indicatorDots {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: var(--gnome-space-2, 12px) 0;\n}\n\n.dot {\n appearance: none;\n border: none;\n padding: 0;\n cursor: pointer;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background-color: var(--gnome-window-fg-color, rgb(0 0 0 / 0.8));\n opacity: 0.2;\n transition:\n opacity var(--gnome-duration-fast) var(--gnome-easing-default),\n transform var(--gnome-duration-fast) var(--gnome-easing-default);\n}\n\n.dot:hover {\n opacity: 0.5;\n}\n\n.dotActive {\n opacity: 1;\n transform: scale(1.15);\n}\n\n.dot:focus-visible {\n outline: 2px solid var(--gnome-focus-ring-color, #3584e4);\n outline-offset: 2px;\n}\n\n/* ─── Line indicators ───────────────────────────────────────────────────────── */\n\n.indicatorLines {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: var(--gnome-space-2, 12px) 0;\n}\n\n.line {\n appearance: none;\n border: none;\n padding: 0;\n cursor: pointer;\n width: 24px;\n height: 3px;\n border-radius: 2px;\n background-color: var(--gnome-window-fg-color, rgb(0 0 0 / 0.8));\n opacity: 0.2;\n transition: opacity var(--gnome-duration-fast) var(--gnome-easing-default);\n}\n\n.line:hover {\n opacity: 0.5;\n}\n\n.lineActive {\n opacity: 1;\n}\n\n.line:focus-visible {\n outline: 2px solid var(--gnome-focus-ring-color, #3584e4);\n outline-offset: 2px;\n}\n\n/* ─── Reduced motion ────────────────────────────────────────────────────────── */\n\n@media (prefers-reduced-motion: reduce) {\n .carousel {\n scroll-behavior: auto;\n }\n\n .dot,\n .line {\n transition: none;\n }\n}\n"],"mappings":""}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
var e = {
|
|
2
|
-
carousel: "
|
|
3
|
-
horizontal: "
|
|
4
|
-
vertical: "
|
|
5
|
-
dragging: "
|
|
6
|
-
slide: "
|
|
7
|
-
indicatorDots: "
|
|
8
|
-
dot: "
|
|
9
|
-
dotActive: "
|
|
10
|
-
indicatorLines: "
|
|
11
|
-
line: "
|
|
12
|
-
lineActive: "
|
|
2
|
+
carousel: "_carousel_78m8m_3",
|
|
3
|
+
horizontal: "_horizontal_78m8m_25",
|
|
4
|
+
vertical: "_vertical_78m8m_34",
|
|
5
|
+
dragging: "_dragging_78m8m_44",
|
|
6
|
+
slide: "_slide_78m8m_53",
|
|
7
|
+
indicatorDots: "_indicatorDots_78m8m_63",
|
|
8
|
+
dot: "_dot_78m8m_71",
|
|
9
|
+
dotActive: "_dotActive_78m8m_90",
|
|
10
|
+
indicatorLines: "_indicatorLines_78m8m_102",
|
|
11
|
+
line: "_line_78m8m_110",
|
|
12
|
+
lineActive: "_lineActive_78m8m_127"
|
|
13
13
|
};
|
|
14
14
|
//#endregion
|
|
15
15
|
export { e as default };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Carousel.module.css.js","names":[],"sources":["../../../src/components/Carousel/Carousel.module.css"],"sourcesContent":["/* ─── Carousel container ────────────────────────────────────────────────────── */\n\n.carousel {\n display: flex;\n overflow: hidden;\n overscroll-behavior: contain;\n outline: none;\n width: 100%;\n cursor: grab;\n scrollbar-width: none; /* Firefox */\n}\n\n.carousel::-webkit-scrollbar {\n display: none; /* Chrome / Safari / Edge */\n}\n\n.carousel:focus-visible {\n box-shadow:\n 0 0 0 var(--gnome-focus-ring-offset, 2px) var(--gnome-window-bg-color, #fafafa),\n 0 0 0 calc(var(--gnome-focus-ring-offset, 2px) + var(--gnome-focus-ring-width, 3px))\n var(--gnome-focus-ring-color, #3584e4);\n border-radius: var(--gnome-radius-md, 8px);\n}\n\n.horizontal {\n flex-direction: row;\n overflow: scroll hidden;\n scroll-snap-type: x mandatory;\n\n /* pan-y: browser handles vertical page scroll; we handle horizontal drag */\n touch-action: pan-y;\n}\n\n.vertical {\n flex-direction: column;\n overflow: hidden scroll;\n scroll-snap-type: y mandatory;\n\n /* pan-x: browser handles horizontal page scroll; we handle vertical drag */\n touch-action: pan-x;\n}\n\n/* Active mouse drag state */\n.dragging {\n cursor: grabbing;\n user-select: none;\n\n /* scroll-snap-type is disabled via inline style during drag */\n}\n\n/* ─── Slide ─────────────────────────────────────────────────────────────────── */\n\n.slide {\n flex: 0 0 100%;\n scroll-snap-align: start;\n min-width: 0;\n min-height: 0;\n}\n\n/* ─── Dot indicators ────────────────────────────────────────────────────────── */\n\n.indicatorDots {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: var(--gnome-space-2, 12px) 0;\n}\n\n.dot {\n appearance: none;\n border: none;\n padding: 0;\n cursor: pointer;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background-color: var(--gnome-window-fg-color, rgb(0 0 0 / 0.8));\n opacity: 0.2;\n transition:\n opacity var(--gnome-duration-fast) var(--gnome-easing-default),\n transform var(--gnome-duration-fast) var(--gnome-easing-default);\n}\n\n.dot:hover {\n opacity: 0.5;\n}\n\n.dotActive {\n opacity: 1;\n transform: scale(1.15);\n}\n\n.dot:focus-visible {\n outline: 2px solid var(--gnome-focus-ring-color, #3584e4);\n outline-offset: 2px;\n}\n\n/* ─── Line indicators ───────────────────────────────────────────────────────── */\n\n.indicatorLines {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: var(--gnome-space-2, 12px) 0;\n}\n\n.line {\n appearance: none;\n border: none;\n padding: 0;\n cursor: pointer;\n width: 24px;\n height: 3px;\n border-radius: 2px;\n background-color: var(--gnome-window-fg-color, rgb(0 0 0 / 0.8));\n opacity: 0.2;\n transition: opacity var(--gnome-duration-fast) var(--gnome-easing-default);\n}\n\n.line:hover {\n opacity: 0.5;\n}\n\n.lineActive {\n opacity: 1;\n}\n\n.line:focus-visible {\n outline: 2px solid var(--gnome-focus-ring-color, #3584e4);\n outline-offset: 2px;\n}\n\n/* ─── Reduced motion ────────────────────────────────────────────────────────── */\n\n@media (prefers-reduced-motion: reduce) {\n .carousel {\n scroll-behavior: auto;\n }\n\n .dot,\n .line {\n transition: none;\n }\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Carousel.module.css.js","names":[],"sources":["../../../src/components/Carousel/Carousel.module.css"],"sourcesContent":["/* ─── Carousel container ────────────────────────────────────────────────────── */\n\n.carousel {\n display: flex;\n overflow: hidden;\n overscroll-behavior: contain;\n outline: none;\n width: 100%;\n cursor: grab;\n scrollbar-width: none; /* Firefox */\n}\n\n.carousel::-webkit-scrollbar {\n display: none; /* Chrome / Safari / Edge */\n}\n\n.carousel:focus-visible {\n box-shadow:\n 0 0 0 var(--gnome-focus-ring-offset, 2px) var(--gnome-window-bg-color, #fafafa),\n 0 0 0 calc(var(--gnome-focus-ring-offset, 2px) + var(--gnome-focus-ring-width, 3px))\n var(--gnome-focus-ring-color, #3584e4);\n border-radius: var(--gnome-radius-md, 8px);\n}\n\n.horizontal {\n flex-direction: row;\n overflow: scroll hidden;\n scroll-snap-type: x mandatory;\n\n /* pan-y: browser handles vertical page scroll; we handle horizontal drag */\n touch-action: pan-y;\n}\n\n.vertical {\n flex-direction: column;\n overflow: hidden scroll;\n scroll-snap-type: y mandatory;\n\n /* pan-x: browser handles horizontal page scroll; we handle vertical drag */\n touch-action: pan-x;\n}\n\n/* Active mouse drag state */\n.dragging {\n cursor: grabbing;\n user-select: none;\n\n /* scroll-snap-type is disabled via inline style during drag */\n}\n\n/* ─── Slide ─────────────────────────────────────────────────────────────────── */\n\n.slide {\n flex: 0 0 100%;\n scroll-snap-align: start;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n/* ─── Dot indicators ────────────────────────────────────────────────────────── */\n\n.indicatorDots {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: var(--gnome-space-2, 12px) 0;\n}\n\n.dot {\n appearance: none;\n border: none;\n padding: 0;\n cursor: pointer;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background-color: var(--gnome-window-fg-color, rgb(0 0 0 / 0.8));\n opacity: 0.2;\n transition:\n opacity var(--gnome-duration-fast) var(--gnome-easing-default),\n transform var(--gnome-duration-fast) var(--gnome-easing-default);\n}\n\n.dot:hover {\n opacity: 0.5;\n}\n\n.dotActive {\n opacity: 1;\n transform: scale(1.15);\n}\n\n.dot:focus-visible {\n outline: 2px solid var(--gnome-focus-ring-color, #3584e4);\n outline-offset: 2px;\n}\n\n/* ─── Line indicators ───────────────────────────────────────────────────────── */\n\n.indicatorLines {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: var(--gnome-space-2, 12px) 0;\n}\n\n.line {\n appearance: none;\n border: none;\n padding: 0;\n cursor: pointer;\n width: 24px;\n height: 3px;\n border-radius: 2px;\n background-color: var(--gnome-window-fg-color, rgb(0 0 0 / 0.8));\n opacity: 0.2;\n transition: opacity var(--gnome-duration-fast) var(--gnome-easing-default);\n}\n\n.line:hover {\n opacity: 0.5;\n}\n\n.lineActive {\n opacity: 1;\n}\n\n.line:focus-visible {\n outline: 2px solid var(--gnome-focus-ring-color, #3584e4);\n outline-offset: 2px;\n}\n\n/* ─── Reduced motion ────────────────────────────────────────────────────────── */\n\n@media (prefers-reduced-motion: reduce) {\n .carousel {\n scroll-behavior: auto;\n }\n\n .dot,\n .line {\n transition: none;\n }\n}\n"],"mappings":""}
|