@rcarls/rc-carousel 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-carousel-CdDEH9fb.js","sources":["../src/rc-carousel-item.styles.ts","../src/rc-carousel-item.ts","../src/rc-carousel.styles.ts","../src/rc-carousel.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const carouselItemStyles = css`\n :host {\n display: block;\n box-sizing: border-box;\n min-inline-size: 0;\n min-block-size: 0;\n scroll-snap-align: var(--rc-carousel-item-scroll-snap-align, start);\n /*\n * Without this, a normal-speed swipe carries enough fling momentum to\n * sail past the very next item and settle two (or more) items over —\n * \\`mandatory\\` scroll-snap-type on the track alone only guarantees\n * landing on *a* snap point, not the nearest one. \\`always\\` forces the\n * browser to stop at each snap point in turn.\n */\n scroll-snap-stop: always;\n overflow: var(--rc-carousel-item-overflow, hidden);\n border-radius: var(--rc-carousel-item-border-radius, 0);\n background: var(--rc-carousel-item-background, transparent);\n color: var(--rc-carousel-item-color, CanvasText);\n color-scheme: inherit;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n /*\n * Peeking/off-screen items are hidden from assistive technology (see\n * rc-carousel-item.ts's IntersectionObserver) but must stay visually and\n * interactively present — a peek is a legitimate visual affordance, not\n * decoration to strip. This only removes it from the accessibility tree\n * and, via inert, the tab sequence, matching the same technique proven\n * carousels (e.g. Shoelace's sl-carousel) use for exactly this problem.\n */\n :host([aria-hidden='true']) {\n pointer-events: none;\n }\n`;\n\nexport default carouselItemStyles;\n","import { LitElement, html } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport type { RCCarousel } from './rc-carousel.js';\n\nimport carouselItemStyles from './rc-carousel-item.styles.js';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-carousel-item': RCCarouselItem;\n }\n}\n\n/**\n * One slide of an `rc-carousel`. Participates directly in the parent's\n * scroll-snap track as a grid-auto-flow child — `rc-carousel-item` owns its\n * own snap alignment and accessibility state, but authored content (an\n * `<img>` with real alt text, arbitrary markup) stays exactly as slotted,\n * never cloned or re-parented, so it remains directly available to forms,\n * labels, and assistive technology.\n *\n * While off-screen (not the intersecting slide, including a partially\n * visible peek), the slide is marked `aria-hidden` and `inert`: it stays\n * visually and pointer-interactively present as a peek affordance, but\n * drops out of the accessibility tree and the Tab sequence, so a slide's\n * interactive descendants (a button, a link) can't be reached by keyboard\n * or announced by a screen reader until their slide is actually active.\n *\n * @slot - Slide content.\n *\n * @cssprop [--rc-carousel-item-color=CanvasText] - Slide foreground color.\n * @cssprop [--rc-carousel-item-scroll-snap-align=start] - Snap alignment\n * within the track. Override for a center-aligned layout.\n * @cssprop [--rc-carousel-item-background=transparent] - Slide surface\n * background, e.g. for an MD3 card-like slide shape.\n * @cssprop [--rc-carousel-item-border-radius=0] - Slide corner radius.\n * @cssprop [--rc-carousel-item-overflow=hidden] - Overflow behavior for\n * slotted content that exceeds the slide's own box. `hidden` clips to\n * the corner radius (matching media-item slides); text-heavy slides\n * that need their own internal scroll may want `auto` instead.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-carousel rc-carousel documentation}\n */\nexport class RCCarouselItem extends LitElement {\n static override styles = carouselItemStyles;\n\n private readonly _internals: ElementInternals;\n\n private _intersectionObserver: IntersectionObserver | null = null;\n\n /**\n * \"N of M\" position assigned by the parent `rc-carousel`. Internal\n * integration point, not a public API — see the `@attr` note above.\n */\n @property({ type: String, attribute: false })\n position = '';\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n this._internals.role = 'group';\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n\n if (!this.hasAttribute('role')) {\n this.setAttribute('role', 'group');\n }\n\n if (!this.hasAttribute('aria-roledescription')) {\n this.setAttribute('aria-roledescription', 'slide');\n }\n\n this._observeIntersection();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n\n this._intersectionObserver?.disconnect();\n this._intersectionObserver = null;\n }\n\n protected override updated(changed: Map<string, unknown>): void {\n super.updated(changed);\n\n if (changed.has('position')) {\n this._syncDefaultLabel();\n }\n }\n\n private _syncDefaultLabel(): void {\n if (this.hasAttribute('aria-label') || this.hasAttribute('aria-labelledby')) {\n return;\n }\n\n if (this.position) {\n this.setAttribute('aria-label', this.position);\n }\n }\n\n /**\n * Observes intersection against the parent carousel's own scroll\n * container (not the viewport — a slide can be viewport-visible yet\n * still clipped/off-screen within the track on a page where the\n * carousel isn't the whole viewport). Reads `trackElement` off the\n * parent `rc-carousel`, an internal integration point rather than a\n * public API — awaits the parent's own first render first, since a\n * slotted item's `connectedCallback` isn't guaranteed to run after its\n * host's, and `trackElement` only exists once the host has rendered.\n */\n private async _observeIntersection(): Promise<void> {\n if (this.hasAttribute('data-clone')) {\n this.setAttribute('aria-hidden', 'true');\n this.setAttribute('inert', '');\n\n return;\n }\n\n const $carousel = this.closest('rc-carousel') as RCCarousel | null;\n\n await $carousel?.updateComplete;\n\n const $root = $carousel?.trackElement ?? null;\n\n this._intersectionObserver?.disconnect();\n\n if (typeof IntersectionObserver !== 'function' || !$root) {\n this.removeAttribute('aria-hidden');\n this.removeAttribute('inert');\n\n return;\n }\n\n this._intersectionObserver = new IntersectionObserver(\n ([entry]) => {\n const hidden = entry !== undefined && !entry.isIntersecting;\n\n if (hidden) {\n if (this.contains(this.ownerDocument.activeElement)) {\n $carousel?.trackElement?.focus();\n }\n\n this.setAttribute('aria-hidden', 'true');\n } else {\n this.removeAttribute('aria-hidden');\n }\n\n this.toggleAttribute('inert', hidden);\n },\n // A low/zero threshold would count a peeking neighbor's own sliver\n // of visible pixels as \"intersecting\", never hiding it — a peek is\n // real but not readable/interactive, so it should still hide. 0.5\n // reliably separates \"the active slide\" (near-fully visible) from\n // \"a peeking neighbor\" (a fraction of it showing).\n { root: $root, threshold: 0.5 },\n );\n\n this._intersectionObserver.observe(this);\n }\n\n protected override render() {\n return html`<slot></slot>`;\n }\n}\n","import { css } from 'lit';\n\nexport const carouselStyles = css`\n :host {\n display: block;\n position: relative;\n color: var(--rc-carousel-color, CanvasText);\n color-scheme: inherit;\n }\n\n #track {\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: var(--rc-carousel-slide-size, calc(100% - 4rem));\n column-gap: var(--rc-carousel-gap, 8px);\n inline-size: 100%;\n block-size: 100%;\n overflow-x: auto;\n overflow-y: hidden;\n overscroll-behavior-x: contain;\n scroll-snap-type: x mandatory;\n /*\n * Deliberately no \\`scroll-behavior: smooth\\` here — _scrollToIndex in\n * rc-carousel.ts passes \\`behavior: 'smooth'\\` explicitly per\n * programmatic call instead, which is unaffected either way (an\n * explicit argument always overrides this CSS default). Setting it\n * here as an ambient default would also apply to the browser's own\n * native snap-settle correction after a swipe — a documented\n * cross-browser conflict with scroll-snap-stop: always (each\n * rc-carousel-item sets that; Firefox bugzilla 1643217, 1959811)\n * that produces a jerk-then-snap-back artifact on an ordinary swipe.\n */\n scrollbar-width: none;\n }\n\n #track::-webkit-scrollbar {\n display: none;\n }\n\n :host([mouse-dragging]) #track {\n cursor: grab;\n }\n\n :host([mouse-dragging]) #track.dragging {\n cursor: grabbing;\n /*\n * scroll-snap-type itself is toggled imperatively in rc-carousel.ts,\n * not here — it must be off before the very first scrollLeft write of\n * a drag, and this class only lands on the next reactive render, a\n * render pass too late for that first write (the browser eagerly\n * resnaps a plain scrollLeft assignment right back to the nearest\n * snap point, same as any other programmatic scroll).\n */\n }\n\n #navigation {\n display: contents;\n }\n\n [part~='navigation-button'] {\n position: absolute;\n top: 50%;\n translate: 0 -50%;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n inline-size: var(--rc-carousel-navigation-button-size, 40px);\n block-size: var(--rc-carousel-navigation-button-size, 40px);\n border: none;\n border-radius: 50%;\n background: var(\n --rc-carousel-navigation-button-background,\n color-mix(in srgb, CanvasText 12%, transparent)\n );\n color: var(--rc-carousel-navigation-button-color, CanvasText);\n cursor: pointer;\n }\n\n [part~='navigation-button'][aria-disabled='true'] {\n opacity: 0.38;\n cursor: default;\n }\n\n [part~='navigation-button-previous'] {\n inset-inline-start: var(--rc-carousel-navigation-inset, 8px);\n }\n\n [part~='navigation-button-next'] {\n inset-inline-end: var(--rc-carousel-navigation-inset, 8px);\n }\n\n #pagination {\n position: absolute;\n inset-block-end: var(--rc-carousel-navigation-inset, 8px);\n inset-inline: 0;\n display: flex;\n justify-content: center;\n gap: var(--rc-carousel-gap, 8px);\n }\n\n [part~='pagination-item'] {\n inline-size: var(--rc-carousel-pagination-item-size, 8px);\n block-size: var(--rc-carousel-pagination-item-size, 8px);\n padding: 0;\n border: none;\n border-radius: 50%;\n background: var(\n --rc-carousel-pagination-item-color,\n color-mix(in srgb, CanvasText 40%, transparent)\n );\n cursor: pointer;\n }\n\n [part~='pagination-item-active'] {\n background: var(--rc-carousel-pagination-item-active-color, Highlight);\n cursor: default;\n }\n`;\n\nexport default carouselStyles;\n","import { LitElement, html, nothing } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\n\nimport {\n DragGestureController,\n findExtremeSnapIndex,\n findNearestSnapIndex,\n findNextSnapIndex,\n keyNavigation,\n warnMissingDirectChild,\n type DragGestureDetail,\n type KeyboardNavigationAction,\n} from '@rcarls/rc-common';\nimport type { RCCarouselItem } from './rc-carousel-item.js';\n\nimport carouselStyles from './rc-carousel.styles.js';\n\n/** Release velocity (px/s) past which a mouse-drag release is treated as a\n * decisive swipe (advance one further slide) rather than settling to the\n * nearest snap point. */\nconst DECISIVE_DRAG_VELOCITY = 300;\n\nexport type RCCarouselChangeTrigger = 'api' | 'button' | 'keyboard' | 'swipe';\n\nexport interface RCCarouselChangeDetail {\n index: number;\n trigger: RCCarouselChangeTrigger;\n}\n\ndeclare global {\n interface HTMLElementEventMap {\n 'rc-carousel-change': CustomEvent<RCCarouselChangeDetail>;\n }\n interface HTMLElementTagNameMap {\n 'rc-carousel': RCCarousel;\n }\n}\n\nconst SETTLE_DEBOUNCE_MS = 120;\n\n/**\n * WAI-ARIA APG carousel pattern built on native CSS scroll-snap. One\n * `rc-carousel-item` per slide, swiped or paged between; the track's own\n * native scroll-snap settling (not a hand-rolled drag simulation) is the\n * primary interaction, per this monorepo's \"build on native browser\n * behavior\" principle.\n *\n * `activeIndex` is a controlled/uncontrolled property, mirroring\n * `rc-adaptive-menu`'s `open`/`defaultOpen` pair: leave it unset for\n * uncontrolled usage (`default-active-index` seeds the initial slide),\n * or set it directly to drive the carousel externally — a settle from\n * swipe, a keyboard action, or the imperative API all report back through\n * `rc-carousel-change` rather than silently self-correcting a controlled\n * value out from under the consumer.\n *\n * Previous/next navigation and a slide picker are both opt-in\n * (`navigation`/`pagination`), following the WAI-ARIA APG carousel\n * pattern's \"grouped\" (non-tab) picker style deliberately: the tabbed\n * `role=\"tabpanel\"` variant requires cross-references between light-DOM\n * slides and shadow-DOM tab buttons that, in comparable shadow-DOM\n * carousels, has hit real accessibility-tooling failures across the\n * shadow boundary (including non-recognition by some screen readers\n * entirely). `loop` wraps seamlessly via cloned lead/trail slides rather\n * than a discontinuous jump back to the other end.\n *\n * @slot - One or more `rc-carousel-item` elements.\n * @slot previous-icon - Optional icon for the previous button, replacing\n * the default chevron. Only rendered when `navigation` is set.\n * @slot next-icon - Optional icon for the next button, replacing the\n * default chevron. Only rendered when `navigation` is set.\n *\n * @fires rc-carousel-change - Fires when the active slide changes, from a\n * swipe settling, a keyboard action, or the imperative API.\n * `detail: { index, trigger: 'swipe'|'button'|'keyboard'|'api' }`\n *\n * @attr active-index - Controls the active slide. Host writes are silent —\n * listen for `rc-carousel-change` to stay in sync.\n * @attr default-active-index - Initial active slide for uncontrolled usage.\n * @attr loop - Wraps past the first/last slide back to the other end,\n * seamlessly (via cloned lead/trail slides), for swipe, buttons, and\n * keyboard alike. Off by default: the ends are real boundaries, not a\n * loop, unless a consumer opts in.\n * @attr navigation - Shows previous/next buttons.\n * @attr pagination - Shows a slide-picker button group.\n * @attr mouse-dragging - Enables click-and-drag scrolling with a mouse.\n * Touch/pen already get native scroll-snap physics; off by default.\n *\n * @cssprop [--rc-carousel-color=CanvasText] - Carousel foreground color.\n * @cssprop [--rc-carousel-gap=8px] - Space between slides.\n * @cssprop [--rc-carousel-slide-size=calc(100% - 4rem)] - Rendered size of\n * each slide along the scroll axis. Set this directly or from a consumer\n * container query to coordinate hero and multi-browse layouts.\n * @cssprop [--rc-carousel-navigation-button-size=40px] - Previous/next\n * button diameter.\n * @cssprop [--rc-carousel-navigation-button-background=color-mix(in srgb, CanvasText 12%, transparent)] -\n * Previous/next button background.\n * @cssprop [--rc-carousel-navigation-button-color=CanvasText] -\n * Previous/next button icon color.\n * @cssprop [--rc-carousel-navigation-inset=8px] - Previous/next button\n * inset from the track edge.\n * @cssprop [--rc-carousel-pagination-item-size=8px] - Slide-picker dot\n * diameter.\n * @cssprop [--rc-carousel-pagination-item-color=color-mix(in srgb, CanvasText 40%, transparent)] -\n * Inactive slide-picker dot color.\n * @cssprop [--rc-carousel-pagination-item-active-color=Highlight] - Active\n * slide-picker dot color.\n *\n * @csspart track - The scrollable slide track.\n * @csspart navigation - Previous/next button wrapper.\n * @csspart navigation-button - A previous or next button.\n * @csspart navigation-button-previous - The previous button specifically.\n * @csspart navigation-button-next - The next button specifically.\n * @csspart pagination - Slide-picker button group wrapper.\n * @csspart pagination-item - A slide-picker button.\n * @csspart pagination-item-active - The active slide's picker button.\n *\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/carousel/ WAI-ARIA APG Carousel pattern}\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-carousel rc-carousel documentation}\n */\nexport class RCCarousel extends LitElement {\n static override styles = carouselStyles;\n\n private readonly _internals: ElementInternals;\n\n @query('#track') private _trackEl?: HTMLDivElement;\n @query('slot:not([name])') private _slotEl?: HTMLSlotElement;\n\n // Seeded from direct children during connection so the first render can\n // build pagination without changing reactive state from firstUpdated().\n private _items: RCCarouselItem[] = [];\n private _cloneItems: RCCarouselItem[] = [];\n private _mounted = false;\n private _suppressSync = false;\n private _pendingInstant = false;\n private _settleTimer: ReturnType<typeof setTimeout> | undefined;\n\n /** Wraps past the first/last slide back to the other end, seamlessly. */\n @property({ type: Boolean, reflect: true })\n loop = false;\n\n /** Shows previous/next buttons. */\n @property({ type: Boolean, reflect: true })\n navigation = false;\n\n /** Shows a slide-picker button group. */\n @property({ type: Boolean, reflect: true })\n pagination = false;\n\n /**\n * Enables click-and-drag scrolling with a mouse — native scroll-snap\n * touch physics already cover touch/pen, but a mouse has no built-in\n * equivalent. Off by default.\n */\n @property({ type: Boolean, reflect: true, attribute: 'mouse-dragging' })\n mouseDragging = false;\n\n @state() private _dragging = false;\n\n private _dragStartLeft = 0;\n private _suppressNextClick = false;\n\n /**\n * Drives `scrollLeft` directly from pointer deltas while `mouseDragging`\n * is on. `activation: 'axis'` (not `'immediate'`) means a plain click\n * never activates a drag at all — it requires clearing\n * `activationDistance` (8px) of movement first — but a *real* drag still\n * ends in a native `click` on release, on whatever's under the pointer,\n * which `_suppressNextClick` swallows so dragging across an unrelated\n * link or button inside a slide doesn't activate it (the same edge case\n * Shoelace's own `sl-carousel` handles for exactly this reason).\n */\n protected readonly _dragController = new DragGestureController(this, {\n target: () => this._trackEl ?? null,\n axis: 'x',\n activation: 'axis',\n canStart: (event) => this.mouseDragging && event.pointerType === 'mouse',\n onStart: () => {\n this._dragging = true;\n this._dragStartLeft = this._trackEl?.scrollLeft ?? 0;\n this._suppressNextClick = true;\n\n // Imperative, not reactive-class-driven: this must land before\n // onMove's very first scrollLeft write below, and a Lit re-render\n // (triggered by _dragging above) is a whole render pass too late for\n // that — with scroll-snap-type still active, the browser eagerly\n // resnaps a plain scrollLeft assignment straight back to the\n // nearest snap point, same as it would any other programmatic\n // scroll with no notion this is \"mid-gesture\".\n if (this._trackEl) {\n this._trackEl.style.scrollSnapType = 'none';\n }\n },\n onMove: (detail) => {\n if (this._trackEl) {\n this._trackEl.scrollLeft = this._dragStartLeft - detail.deltaX;\n }\n },\n onEnd: (detail) => this._endDrag(detail),\n onCancel: (detail) => this._endDrag(detail),\n });\n\n @state() private _busy = false;\n\n private _activeIndex: number | undefined;\n private _defaultActiveIndex = 0;\n private _uncontrolledActiveIndex: number | undefined;\n private _activeIndexInitialized = false;\n\n /** Controls the active slide. Host writes are silent. */\n @property({ type: Number, attribute: 'active-index' })\n get activeIndex(): number {\n return this._clampIndex(\n this._activeIndex ?? this._uncontrolledActiveIndex ?? this._defaultActiveIndex,\n );\n }\n\n set activeIndex(value: number | undefined) {\n const oldValue = this.activeIndex;\n\n this._activeIndex = value;\n this._activeIndexInitialized = true;\n this.requestUpdate('activeIndex', oldValue);\n }\n\n /** Initial active slide for uncontrolled usage. */\n @property({ type: Number, attribute: 'default-active-index' })\n get defaultActiveIndex(): number {\n return this._defaultActiveIndex;\n }\n\n set defaultActiveIndex(value: number) {\n const oldValue = this._defaultActiveIndex;\n\n this._defaultActiveIndex = value;\n\n if (\n !this._activeIndexInitialized &&\n this._activeIndex === undefined &&\n this._uncontrolledActiveIndex === undefined\n ) {\n this.requestUpdate('activeIndex', oldValue);\n }\n\n this.requestUpdate('defaultActiveIndex', oldValue);\n }\n\n /**\n * The scroll-snap track element. Internal integration point consumed by\n * `rc-carousel-item`'s IntersectionObserver (its `root` must be this\n * track, not the viewport, so peeking/clipped-but-viewport-visible\n * slides are still correctly detected as off-screen) — not a public API.\n */\n get trackElement(): HTMLElement | null {\n return this._trackEl ?? null;\n }\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n this._internals.role = 'group';\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n\n this._items = this._directItems();\n this._setItemPositions();\n\n if (!this.hasAttribute('role')) {\n this.setAttribute('role', 'group');\n }\n\n if (!this.hasAttribute('aria-roledescription')) {\n this.setAttribute('aria-roledescription', 'carousel');\n }\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n\n if (this._settleTimer !== undefined) {\n clearTimeout(this._settleTimer);\n }\n }\n\n protected override firstUpdated(): void {\n if (import.meta.env.DEV) {\n warnMissingDirectChild(this, {\n selector: ':scope > rc-carousel-item',\n childDescription: '<rc-carousel-item> elements',\n });\n\n if (!this.hasAttribute('aria-label') && !this.hasAttribute('aria-labelledby')) {\n console.warn(\n \"[rc-carousel] No aria-label/aria-labelledby set. Provide one describing this carousel's content.\",\n );\n }\n }\n\n this._scrollToIndex(this.activeIndex, true);\n this._mounted = true;\n }\n\n protected override updated(changed: Map<string, unknown>): void {\n super.updated(changed);\n\n this._initPaginationTabIndex();\n\n if (changed.has('loop') && this._mounted) {\n // Live toggle: _syncItems is idempotent (see its own comment) when\n // clone presence already matches, so this is cheap to call even when\n // nothing actually needs to change.\n this._syncItems();\n }\n\n if (!changed.has('activeIndex')) {\n return;\n }\n\n if (this._suppressSync) {\n this._suppressSync = false;\n\n return;\n }\n\n const instant = !this._mounted || this._pendingInstant;\n\n this._pendingInstant = false;\n this._scrollToIndex(this.activeIndex, instant);\n }\n\n /** Moves to the next slide. */\n next(): void {\n this._step(1, 'api');\n }\n\n /** Moves to the previous slide. */\n previous(): void {\n this._step(-1, 'api');\n }\n\n /** Moves directly to a slide index. */\n goToIndex(index: number, instant = false): void {\n this._pendingInstant = instant;\n this._setActiveIndex(index, 'api');\n }\n\n private _step(delta: 1 | -1, trigger: RCCarouselChangeTrigger): void {\n this._setActiveIndex(this.activeIndex + delta, trigger);\n }\n\n /** Whether a previous/next button (or an equivalent keyboard action)\n * currently has anywhere to go — always true when `loop` is set and\n * there's more than one slide. */\n private _canStep(delta: 1 | -1): boolean {\n const count = this._items.length;\n\n if (count <= 1) {\n return false;\n }\n\n if (this.loop) {\n return true;\n }\n\n return delta > 0 ? this.activeIndex < count - 1 : this.activeIndex > 0;\n }\n\n /**\n * Wraps when `loop` is set, otherwise clamps to the real slide range.\n * Wrapping here covers button/keyboard navigation; a swipe wraps\n * seamlessly through `_trackSlots`'s cloned lead/trail slides instead,\n * since that needs the clones' own track positions, not just an index.\n */\n private _clampIndex(index: number): number {\n const count = this._items.length;\n\n if (count === 0) {\n return 0;\n }\n\n if (this.loop) {\n return ((index % count) + count) % count;\n }\n\n return Math.min(count - 1, Math.max(0, index));\n }\n\n private _snapPoints(): number[] {\n const step = this._itemStep();\n\n return this._items.map((_item, index) => index * step);\n }\n\n /**\n * Every scrollable track position in rendered order, including cloned\n * lead/trail slides when `loop` is on — `[lastClone, item0, ..., itemN-1,\n * firstClone]`. Each slot reports which real slide index it represents\n * (a clone mirrors the real slide it duplicates), so both scrolling to\n * an index and reading back a settled scroll position can stay in terms\n * of real indices while the clones do the seamless-wrap work.\n */\n private _trackSlots(): { point: number; index: number; isClone: boolean }[] {\n const step = this._itemStep();\n const count = this._items.length;\n\n if (!this.loop || count < 2) {\n return this._items.map((_item, index) => ({ point: index * step, index, isClone: false }));\n }\n\n return [\n { point: 0, index: count - 1, isClone: true },\n ...this._items.map((_item, index) => ({ point: (index + 1) * step, index, isClone: false })),\n { point: (count + 1) * step, index: 0, isClone: true },\n ];\n }\n\n private _itemStep(): number {\n if (!this._trackEl) {\n return 1;\n }\n\n const first = this._items[0];\n\n if (!(first instanceof HTMLElement)) {\n return this._trackEl.clientWidth;\n }\n\n const gap = Number.parseFloat(getComputedStyle(this._trackEl).columnGap || '0') || 0;\n\n return first.offsetWidth + gap;\n }\n\n private _setActiveIndex(index: number, trigger: RCCarouselChangeTrigger): void {\n const clamped = this._clampIndex(index);\n const oldValue = this.activeIndex;\n\n if (oldValue === clamped) {\n return;\n }\n\n // Controlled usage (activeIndex currently has a host-owned value)\n // never self-writes here — only the consumer's own activeIndex setter\n // may change it, in response to the event dispatched below. Only\n // genuinely uncontrolled usage updates its own backing state directly.\n if (this._activeIndex === undefined) {\n this._uncontrolledActiveIndex = clamped;\n this.requestUpdate('activeIndex', oldValue);\n }\n\n this.dispatchEvent(\n new CustomEvent<RCCarouselChangeDetail>('rc-carousel-change', {\n bubbles: true,\n composed: true,\n detail: { index: clamped, trigger },\n }),\n );\n }\n\n private _scrollToIndex(index: number, instant: boolean): void {\n if (!this._trackEl) {\n return;\n }\n\n const slot = this._trackSlots().find((s) => !s.isClone && s.index === index);\n const left = slot ? slot.point : index * this._itemStep();\n const reducedMotion =\n this.ownerDocument.defaultView?.matchMedia('(prefers-reduced-motion: reduce)').matches ??\n false;\n\n if (instant || reducedMotion) {\n this._trackEl.scrollLeft = left;\n } else {\n // Explicit per call, not an ambient CSS default — see the comment on\n // #track in rc-carousel.styles.ts.\n this._trackEl.scrollTo({ left, behavior: 'smooth' });\n }\n }\n\n private _onSlotChange = (): void => {\n this._syncItems();\n };\n\n private _directItems(): RCCarouselItem[] {\n return Array.from(this.children).filter(\n (el): el is RCCarouselItem =>\n el.tagName === 'RC-CAROUSEL-ITEM' && !el.hasAttribute('data-clone'),\n );\n }\n\n private _setItemPositions(): void {\n this._items.forEach((item, index) => {\n item.position = `${index + 1} of ${this._items.length}`;\n });\n }\n\n /**\n * Prepending/appending the loop clones below is itself a light-DOM\n * mutation on this host, which re-fires `slotchange` (asynchronously) —\n * `_onSlotChange` calls back into this method, so a naive unconditional\n * remove-then-recreate would recreate a fresh pair of clones on every\n * pass forever. Clones are always excluded from `nextItems`, so the\n * real item list is byte-for-byte identical on that re-entrant pass;\n * only rebuild clone DOM when either the real items or the desired\n * clone presence has actually changed, so the re-entrant pass is a\n * true no-op and the recursion terminates.\n */\n private _syncItems(): void {\n const assigned = this._slotEl?.assignedElements() ?? [];\n const nextItems = assigned.filter(\n (el): el is RCCarouselItem =>\n el.tagName === 'RC-CAROUSEL-ITEM' && !el.hasAttribute('data-clone'),\n );\n const itemsChanged =\n nextItems.length !== this._items.length ||\n nextItems.some((item, index) => item !== this._items[index]);\n const wantsClones = this.loop && nextItems.length > 1;\n const hasClones = this._cloneItems.length > 0;\n\n this._items = nextItems;\n this._setItemPositions();\n\n if (itemsChanged && this._mounted) {\n this.requestUpdate();\n }\n\n if (!itemsChanged && wantsClones === hasClones) {\n return;\n }\n\n this._removeClones();\n\n if (wantsClones) {\n this._addClones();\n }\n }\n\n /**\n * Clones the first and last slide and prepends/appends them\n * (`data-clone=\"<real index>\"` marks them, excluded from `_items` and\n * from position numbering) so the track has real scrollable content\n * past both visual ends. `_trackSlots`/`_commitSettledIndex` detect\n * when a clone settles and instantly re-anchor to the real slide it\n * mirrors — Shoelace's proven technique for a seamless infinite swipe,\n * rather than a discontinuous index-modulo jump back to the other end.\n */\n private _addClones(): void {\n const first = this._items[0];\n const last = this._items[this._items.length - 1];\n\n if (!first || !last) {\n return;\n }\n\n const leadingClone = last.cloneNode(true) as RCCarouselItem;\n\n leadingClone.setAttribute('data-clone', String(this._items.length - 1));\n this._sanitizeClone(leadingClone);\n this.prepend(leadingClone);\n\n const trailingClone = first.cloneNode(true) as RCCarouselItem;\n\n trailingClone.setAttribute('data-clone', '0');\n this._sanitizeClone(trailingClone);\n this.append(trailingClone);\n\n this._cloneItems = [leadingClone, trailingClone];\n }\n\n /** Keeps visual loop clones out of forms, focus order, and the accessibility tree. */\n private _sanitizeClone($clone: RCCarouselItem): void {\n $clone.setAttribute('aria-hidden', 'true');\n $clone.setAttribute('inert', '');\n\n for (const $element of [$clone, ...$clone.querySelectorAll<HTMLElement>('*')]) {\n $element.removeAttribute('id');\n $element.removeAttribute('name');\n $element.removeAttribute('form');\n }\n }\n\n private _removeClones(): void {\n this._cloneItems.forEach((clone) => clone.remove());\n this._cloneItems = [];\n }\n\n /**\n * On release, a fast enough flick pre-nudges `scrollLeft` to the next\n * snap point in the drag direction (a \"decisive swipe\" — mirroring\n * `rc-bottom-sheet`'s own velocity-vs-nearest-point settle heuristic,\n * adapted from \"jump to the extreme end\" for a 2-point sheet to \"advance\n * one further point\" for a carousel that can have many). Either way,\n * this hands off to the same debounced settle path a native swipe\n * already goes through — our own `scrollLeft` writes during the drag\n * already fired real `scroll` events, so `_onScroll` just needs to run\n * its usual timer to pick up wherever things ended.\n */\n private _endDrag(detail: DragGestureDetail): void {\n this._dragging = false;\n\n if (this._trackEl) {\n this._trackEl.style.scrollSnapType = '';\n }\n\n if (this._trackEl && Math.abs(detail.velocityX) > DECISIVE_DRAG_VELOCITY) {\n const slots = this._trackSlots();\n // Dragging left (negative velocityX) moves content right-to-left,\n // i.e. advances forward — toward higher track points.\n const direction = detail.velocityX < 0 ? 1 : -1;\n const targetSlotIndex = findNextSnapIndex(\n slots.map((slot) => slot.point),\n this._trackEl.scrollLeft,\n direction,\n );\n const targetSlot = slots[targetSlotIndex];\n\n if (targetSlot) {\n this._trackEl.scrollLeft = targetSlot.point;\n }\n }\n\n this._onScroll();\n }\n\n /**\n * Debounced, not per-frame: `_scrollToIndex`'s own smooth scroll fires\n * many `scroll` events while still mid-flight, and reading the settled\n * index from an in-progress position would report the wrong slide and\n * immediately fight the very scroll driving it. Waiting for scrolling to\n * actually stop (swipe release or animation end) avoids that oscillation.\n */\n private _onScroll = (): void => {\n if (this._settleTimer !== undefined) {\n clearTimeout(this._settleTimer);\n }\n\n this._busy = true;\n this._settleTimer = setTimeout(this._commitSettledIndex, SETTLE_DEBOUNCE_MS);\n };\n\n private _commitSettledIndex = (): void => {\n this._busy = false;\n\n if (!this._trackEl) {\n return;\n }\n\n const slots = this._trackSlots();\n const settledSlotIndex = findNearestSnapIndex(\n slots.map((slot) => slot.point),\n this._trackEl.scrollLeft,\n );\n const settledSlot = settledSlotIndex >= 0 ? slots[settledSlotIndex] : undefined;\n\n if (!settledSlot) {\n return;\n }\n\n if (settledSlot.isClone) {\n // Seamless re-anchor: a clone settling means the user scrolled past\n // the real end. Instantly (no animation — this must be invisible)\n // correct to the real slide's own track position, which renders\n // identically to the clone at this scroll offset.\n const realSlot = slots.find((slot) => !slot.isClone && slot.index === settledSlot.index);\n\n if (realSlot) {\n this._trackEl.scrollLeft = realSlot.point;\n }\n }\n\n if (settledSlot.index === this.activeIndex) {\n // Already in sync (or the clone correction above just made it so) —\n // no prop change means `updated()` won't run at all, so there's no\n // echo to suppress here.\n return;\n }\n\n // Settling a user swipe reports the new index back up through the same\n // `activeIndex` prop the sync effect (`updated()`) watches. Replaying\n // that through `_scrollToIndex` would still fire a real, non-instant\n // `scrollTo`, not a no-op — if a genuine next swipe starts during that\n // echoed animation, it fights the swipe and can swallow it.\n // `_suppressSync` skips `updated()`'s scroll for exactly that one echo.\n const controlled = this._activeIndex !== undefined;\n\n if (!controlled) {\n this._suppressSync = true;\n }\n\n this._setActiveIndex(settledSlot.index, 'swipe');\n\n // A controlled consumer may reject the requested swipe by leaving the\n // property unchanged. Restore the visual position to the host-owned slide\n // instead of leaving scroll state and public state disagreeing.\n if (controlled && this.activeIndex !== settledSlot.index) {\n this._scrollToIndex(this.activeIndex, true);\n }\n };\n\n private _onNavigate = (action: KeyboardNavigationAction): void => {\n switch (action) {\n case 'next':\n this._step(1, 'keyboard');\n break;\n case 'prev':\n this._step(-1, 'keyboard');\n break;\n\n case 'start': {\n const index = findExtremeSnapIndex(this._snapPoints(), -1);\n\n if (index >= 0) {\n this._setActiveIndex(index, 'keyboard');\n }\n\n break;\n }\n\n case 'end': {\n const index = findExtremeSnapIndex(this._snapPoints(), 1);\n\n if (index >= 0) {\n this._setActiveIndex(index, 'keyboard');\n }\n\n break;\n }\n }\n };\n\n /**\n * A real drag still ends in a native `click` on release (browsers fire\n * one on pointerup as long as pointerdown/pointerup share a target),\n * landing on whatever's currently under the pointer — which, mid-drag,\n * is often unrelated slide content the user was scrolling past, not\n * clicking. Capturing and swallowing exactly one click right after a\n * drag activated (`_suppressNextClick`, set in the drag controller's\n * `onStart`) stops that from reaching a link/button inside a slide.\n */\n private _onTrackClickCapture = (event: MouseEvent): void => {\n if (!this._suppressNextClick) {\n return;\n }\n\n this._suppressNextClick = false;\n event.stopPropagation();\n event.preventDefault();\n };\n\n private _paginationButtons(): HTMLButtonElement[] {\n return Array.from(\n this.shadowRoot?.querySelectorAll<HTMLButtonElement>('[data-pagination-item]') ?? [],\n );\n }\n\n /**\n * Hand-rolled roving tabindex for the pagination row, not\n * `RovingTabIndexMixin`: that mixin collects items from a real\n * `slotchange`-firing `<slot>`, matching its intended use (consumer-\n * authored, slotted content, like `rc-toolbar`'s buttons) — these\n * buttons are shadow-DOM content this component renders itself, so\n * there's no slot to collect from. The pattern is otherwise the same\n * one the mixin itself implements: exactly one button is a tab stop,\n * arrow keys move focus among them (not activeIndex — matching\n * rc-toolbar's own \"arrows move focus, Enter/Space activates\" model,\n * not a tablist's auto-activate-on-arrow), and focus keeps tabindex in\n * sync wherever it lands (click, Tab, or a `.focus()` call below).\n */\n private _onPaginationFocus = (event: FocusEvent): void => {\n const buttons = this._paginationButtons();\n const target = event.composedPath().find((el) => buttons.includes(el as HTMLButtonElement)) as\n | HTMLButtonElement\n | undefined;\n\n if (!target) {\n return;\n }\n\n buttons.forEach((button) => button.setAttribute('tabindex', button === target ? '0' : '-1'));\n };\n\n private _onPaginationNavigate = (action: KeyboardNavigationAction): void => {\n const buttons = this._paginationButtons();\n\n if (!buttons.length) {\n return;\n }\n\n const current = buttons.indexOf(this.shadowRoot?.activeElement as HTMLButtonElement);\n const from = current < 0 ? 0 : current;\n\n switch (action) {\n case 'next':\n buttons[(from + 1) % buttons.length]?.focus();\n break;\n case 'prev':\n buttons[(from - 1 + buttons.length) % buttons.length]?.focus();\n break;\n case 'start':\n buttons[0]?.focus();\n break;\n case 'end':\n buttons[buttons.length - 1]?.focus();\n break;\n }\n };\n\n /**\n * One-time tabindex seed for pagination buttons that have never\n * received focus yet (matching `activeIndex`, the sane default) —\n * `_onPaginationFocus` takes over from there. Safe to call on every\n * render: a no-op once every button already has some tabindex value.\n */\n private _initPaginationTabIndex(): void {\n this._paginationButtons().forEach((button, index) => {\n if (!button.hasAttribute('tabindex')) {\n button.setAttribute('tabindex', index === this.activeIndex ? '0' : '-1');\n }\n });\n }\n\n protected override render() {\n const canPrev = this._canStep(-1);\n const canNext = this._canStep(1);\n\n return html`\n <div\n id=\"track\"\n part=\"track\"\n tabindex=\"0\"\n class=${this._dragging ? 'dragging' : nothing}\n aria-busy=${this._busy ? 'true' : 'false'}\n aria-atomic=\"true\"\n @scroll=${this._onScroll}\n @click=${{ handleEvent: this._onTrackClickCapture, capture: true }}\n ${keyNavigation(this._onNavigate, { navigationAxis: 'horizontal' })}\n >\n <slot @slotchange=${this._onSlotChange}></slot>\n </div>\n\n ${this.navigation\n ? html`\n <div id=\"navigation\" part=\"navigation\">\n <button\n type=\"button\"\n part=\"navigation-button navigation-button-previous\"\n aria-label=\"Previous slide\"\n aria-disabled=${canPrev ? 'false' : 'true'}\n @click=${() => {\n if (canPrev) {\n this._step(-1, 'button');\n }\n }}\n >\n <span aria-hidden=\"true\">\n <slot name=\"previous-icon\">\n <svg\n viewBox=\"0 0 6 10\"\n width=\"10\"\n height=\"16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"5,1 1,5 5,9\" />\n </svg>\n </slot>\n </span>\n </button>\n <button\n type=\"button\"\n part=\"navigation-button navigation-button-next\"\n aria-label=\"Next slide\"\n aria-disabled=${canNext ? 'false' : 'true'}\n @click=${() => {\n if (canNext) {\n this._step(1, 'button');\n }\n }}\n >\n <span aria-hidden=\"true\">\n <slot name=\"next-icon\">\n <svg\n viewBox=\"0 0 6 10\"\n width=\"10\"\n height=\"16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"1,1 5,5 1,9\" />\n </svg>\n </slot>\n </span>\n </button>\n </div>\n `\n : nothing}\n ${this.pagination\n ? html`\n <div\n id=\"pagination\"\n part=\"pagination\"\n role=\"group\"\n aria-label=\"Choose slide to display\"\n @focusin=${this._onPaginationFocus}\n ${keyNavigation(this._onPaginationNavigate, { navigationAxis: 'horizontal' })}\n >\n ${this._items.map(\n (_item, index) => html`\n <button\n type=\"button\"\n data-pagination-item\n part=\"pagination-item${index === this.activeIndex\n ? ' pagination-item-active'\n : ''}\"\n aria-label=\"Go to slide ${index + 1}\"\n aria-current=${index === this.activeIndex ? 'true' : 'false'}\n aria-disabled=${index === this.activeIndex ? 'true' : 'false'}\n @click=${() => this._setActiveIndex(index, 'button')}\n ></button>\n `,\n )}\n </div>\n `\n : nothing}\n `;\n }\n}\n"],"names":["carouselItemStyles","css","_RCCarouselItem","LitElement","changed","$carousel","$root","entry","hidden","html","RCCarouselItem","__decorateClass","property","carouselStyles","DECISIVE_DRAG_VELOCITY","SETTLE_DEBOUNCE_MS","_RCCarousel","DragGestureController","event","detail","slots","settledSlotIndex","findNearestSnapIndex","slot","settledSlot","realSlot","controlled","action","index","findExtremeSnapIndex","buttons","target","el","button","current","from","value","oldValue","instant","delta","trigger","count","step","_item","first","gap","clamped","s","left","reducedMotion","item","nextItems","itemsChanged","wantsClones","hasClones","last","leadingClone","trailingClone","$clone","$element","clone","direction","targetSlotIndex","findNextSnapIndex","targetSlot","canPrev","canNext","nothing","keyNavigation","RCCarousel","query","state"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { a as e, R as s } from "./rc-carousel-CdDEH9fb.js";
2
+ customElements.get("rc-carousel-item") || customElements.define("rc-carousel-item", e);
3
+ customElements.get("rc-carousel") || customElements.define("rc-carousel", s);
4
+ export {
5
+ s as RCCarousel,
6
+ e as RCCarouselItem
7
+ };
8
+ //# sourceMappingURL=rc-carousel-define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-carousel-define.js","sources":["../src/define.ts"],"sourcesContent":["import { RCCarousel, RCCarouselItem } from './index.js';\n\ncustomElements.get('rc-carousel-item') || customElements.define('rc-carousel-item', RCCarouselItem);\ncustomElements.get('rc-carousel') || customElements.define('rc-carousel', RCCarousel);\n\nexport * from './index.js';\n"],"names":["RCCarouselItem","RCCarousel"],"mappings":";AAEA,eAAe,IAAI,kBAAkB,KAAK,eAAe,OAAO,oBAAoBA,CAAc;AAClG,eAAe,IAAI,aAAa,KAAK,eAAe,OAAO,eAAeC,CAAU;"}
@@ -0,0 +1,6 @@
1
+ import { R as o, a as r } from "./rc-carousel-CdDEH9fb.js";
2
+ export {
3
+ o as RCCarousel,
4
+ r as RCCarouselItem
5
+ };
6
+ //# sourceMappingURL=rc-carousel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-carousel.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1 @@
1
+ export * from './index.js';
@@ -0,0 +1,2 @@
1
+ export * from './rc-carousel-item.js';
2
+ export * from './rc-carousel.js';
@@ -0,0 +1,63 @@
1
+ import { LitElement } from 'lit';
2
+ declare global {
3
+ interface HTMLElementTagNameMap {
4
+ 'rc-carousel-item': RCCarouselItem;
5
+ }
6
+ }
7
+ /**
8
+ * One slide of an `rc-carousel`. Participates directly in the parent's
9
+ * scroll-snap track as a grid-auto-flow child — `rc-carousel-item` owns its
10
+ * own snap alignment and accessibility state, but authored content (an
11
+ * `<img>` with real alt text, arbitrary markup) stays exactly as slotted,
12
+ * never cloned or re-parented, so it remains directly available to forms,
13
+ * labels, and assistive technology.
14
+ *
15
+ * While off-screen (not the intersecting slide, including a partially
16
+ * visible peek), the slide is marked `aria-hidden` and `inert`: it stays
17
+ * visually and pointer-interactively present as a peek affordance, but
18
+ * drops out of the accessibility tree and the Tab sequence, so a slide's
19
+ * interactive descendants (a button, a link) can't be reached by keyboard
20
+ * or announced by a screen reader until their slide is actually active.
21
+ *
22
+ * @slot - Slide content.
23
+ *
24
+ * @cssprop [--rc-carousel-item-color=CanvasText] - Slide foreground color.
25
+ * @cssprop [--rc-carousel-item-scroll-snap-align=start] - Snap alignment
26
+ * within the track. Override for a center-aligned layout.
27
+ * @cssprop [--rc-carousel-item-background=transparent] - Slide surface
28
+ * background, e.g. for an MD3 card-like slide shape.
29
+ * @cssprop [--rc-carousel-item-border-radius=0] - Slide corner radius.
30
+ * @cssprop [--rc-carousel-item-overflow=hidden] - Overflow behavior for
31
+ * slotted content that exceeds the slide's own box. `hidden` clips to
32
+ * the corner radius (matching media-item slides); text-heavy slides
33
+ * that need their own internal scroll may want `auto` instead.
34
+ *
35
+ * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-carousel rc-carousel documentation}
36
+ */
37
+ export declare class RCCarouselItem extends LitElement {
38
+ static styles: import('lit').CSSResult;
39
+ private readonly _internals;
40
+ private _intersectionObserver;
41
+ /**
42
+ * "N of M" position assigned by the parent `rc-carousel`. Internal
43
+ * integration point, not a public API — see the `@attr` note above.
44
+ */
45
+ position: string;
46
+ constructor();
47
+ connectedCallback(): void;
48
+ disconnectedCallback(): void;
49
+ protected updated(changed: Map<string, unknown>): void;
50
+ private _syncDefaultLabel;
51
+ /**
52
+ * Observes intersection against the parent carousel's own scroll
53
+ * container (not the viewport — a slide can be viewport-visible yet
54
+ * still clipped/off-screen within the track on a page where the
55
+ * carousel isn't the whole viewport). Reads `trackElement` off the
56
+ * parent `rc-carousel`, an internal integration point rather than a
57
+ * public API — awaits the parent's own first render first, since a
58
+ * slotted item's `connectedCallback` isn't guaranteed to run after its
59
+ * host's, and `trackElement` only exists once the host has rendered.
60
+ */
61
+ private _observeIntersection;
62
+ protected render(): import('lit').TemplateResult<1>;
63
+ }
@@ -0,0 +1,2 @@
1
+ export declare const carouselItemStyles: import('lit').CSSResult;
2
+ export default carouselItemStyles;
@@ -0,0 +1,270 @@
1
+ import { LitElement } from 'lit';
2
+ import { DragGestureController } from '@rcarls/rc-common';
3
+ export type RCCarouselChangeTrigger = 'api' | 'button' | 'keyboard' | 'swipe';
4
+ export interface RCCarouselChangeDetail {
5
+ index: number;
6
+ trigger: RCCarouselChangeTrigger;
7
+ }
8
+ declare global {
9
+ interface HTMLElementEventMap {
10
+ 'rc-carousel-change': CustomEvent<RCCarouselChangeDetail>;
11
+ }
12
+ interface HTMLElementTagNameMap {
13
+ 'rc-carousel': RCCarousel;
14
+ }
15
+ }
16
+ /**
17
+ * WAI-ARIA APG carousel pattern built on native CSS scroll-snap. One
18
+ * `rc-carousel-item` per slide, swiped or paged between; the track's own
19
+ * native scroll-snap settling (not a hand-rolled drag simulation) is the
20
+ * primary interaction, per this monorepo's "build on native browser
21
+ * behavior" principle.
22
+ *
23
+ * `activeIndex` is a controlled/uncontrolled property, mirroring
24
+ * `rc-adaptive-menu`'s `open`/`defaultOpen` pair: leave it unset for
25
+ * uncontrolled usage (`default-active-index` seeds the initial slide),
26
+ * or set it directly to drive the carousel externally — a settle from
27
+ * swipe, a keyboard action, or the imperative API all report back through
28
+ * `rc-carousel-change` rather than silently self-correcting a controlled
29
+ * value out from under the consumer.
30
+ *
31
+ * Previous/next navigation and a slide picker are both opt-in
32
+ * (`navigation`/`pagination`), following the WAI-ARIA APG carousel
33
+ * pattern's "grouped" (non-tab) picker style deliberately: the tabbed
34
+ * `role="tabpanel"` variant requires cross-references between light-DOM
35
+ * slides and shadow-DOM tab buttons that, in comparable shadow-DOM
36
+ * carousels, has hit real accessibility-tooling failures across the
37
+ * shadow boundary (including non-recognition by some screen readers
38
+ * entirely). `loop` wraps seamlessly via cloned lead/trail slides rather
39
+ * than a discontinuous jump back to the other end.
40
+ *
41
+ * @slot - One or more `rc-carousel-item` elements.
42
+ * @slot previous-icon - Optional icon for the previous button, replacing
43
+ * the default chevron. Only rendered when `navigation` is set.
44
+ * @slot next-icon - Optional icon for the next button, replacing the
45
+ * default chevron. Only rendered when `navigation` is set.
46
+ *
47
+ * @fires rc-carousel-change - Fires when the active slide changes, from a
48
+ * swipe settling, a keyboard action, or the imperative API.
49
+ * `detail: { index, trigger: 'swipe'|'button'|'keyboard'|'api' }`
50
+ *
51
+ * @attr active-index - Controls the active slide. Host writes are silent —
52
+ * listen for `rc-carousel-change` to stay in sync.
53
+ * @attr default-active-index - Initial active slide for uncontrolled usage.
54
+ * @attr loop - Wraps past the first/last slide back to the other end,
55
+ * seamlessly (via cloned lead/trail slides), for swipe, buttons, and
56
+ * keyboard alike. Off by default: the ends are real boundaries, not a
57
+ * loop, unless a consumer opts in.
58
+ * @attr navigation - Shows previous/next buttons.
59
+ * @attr pagination - Shows a slide-picker button group.
60
+ * @attr mouse-dragging - Enables click-and-drag scrolling with a mouse.
61
+ * Touch/pen already get native scroll-snap physics; off by default.
62
+ *
63
+ * @cssprop [--rc-carousel-color=CanvasText] - Carousel foreground color.
64
+ * @cssprop [--rc-carousel-gap=8px] - Space between slides.
65
+ * @cssprop [--rc-carousel-slide-size=calc(100% - 4rem)] - Rendered size of
66
+ * each slide along the scroll axis. Set this directly or from a consumer
67
+ * container query to coordinate hero and multi-browse layouts.
68
+ * @cssprop [--rc-carousel-navigation-button-size=40px] - Previous/next
69
+ * button diameter.
70
+ * @cssprop [--rc-carousel-navigation-button-background=color-mix(in srgb, CanvasText 12%, transparent)] -
71
+ * Previous/next button background.
72
+ * @cssprop [--rc-carousel-navigation-button-color=CanvasText] -
73
+ * Previous/next button icon color.
74
+ * @cssprop [--rc-carousel-navigation-inset=8px] - Previous/next button
75
+ * inset from the track edge.
76
+ * @cssprop [--rc-carousel-pagination-item-size=8px] - Slide-picker dot
77
+ * diameter.
78
+ * @cssprop [--rc-carousel-pagination-item-color=color-mix(in srgb, CanvasText 40%, transparent)] -
79
+ * Inactive slide-picker dot color.
80
+ * @cssprop [--rc-carousel-pagination-item-active-color=Highlight] - Active
81
+ * slide-picker dot color.
82
+ *
83
+ * @csspart track - The scrollable slide track.
84
+ * @csspart navigation - Previous/next button wrapper.
85
+ * @csspart navigation-button - A previous or next button.
86
+ * @csspart navigation-button-previous - The previous button specifically.
87
+ * @csspart navigation-button-next - The next button specifically.
88
+ * @csspart pagination - Slide-picker button group wrapper.
89
+ * @csspart pagination-item - A slide-picker button.
90
+ * @csspart pagination-item-active - The active slide's picker button.
91
+ *
92
+ * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/carousel/ WAI-ARIA APG Carousel pattern}
93
+ * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-carousel rc-carousel documentation}
94
+ */
95
+ export declare class RCCarousel extends LitElement {
96
+ static styles: import('lit').CSSResult;
97
+ private readonly _internals;
98
+ private _trackEl?;
99
+ private _slotEl?;
100
+ private _items;
101
+ private _cloneItems;
102
+ private _mounted;
103
+ private _suppressSync;
104
+ private _pendingInstant;
105
+ private _settleTimer;
106
+ /** Wraps past the first/last slide back to the other end, seamlessly. */
107
+ loop: boolean;
108
+ /** Shows previous/next buttons. */
109
+ navigation: boolean;
110
+ /** Shows a slide-picker button group. */
111
+ pagination: boolean;
112
+ /**
113
+ * Enables click-and-drag scrolling with a mouse — native scroll-snap
114
+ * touch physics already cover touch/pen, but a mouse has no built-in
115
+ * equivalent. Off by default.
116
+ */
117
+ mouseDragging: boolean;
118
+ private _dragging;
119
+ private _dragStartLeft;
120
+ private _suppressNextClick;
121
+ /**
122
+ * Drives `scrollLeft` directly from pointer deltas while `mouseDragging`
123
+ * is on. `activation: 'axis'` (not `'immediate'`) means a plain click
124
+ * never activates a drag at all — it requires clearing
125
+ * `activationDistance` (8px) of movement first — but a *real* drag still
126
+ * ends in a native `click` on release, on whatever's under the pointer,
127
+ * which `_suppressNextClick` swallows so dragging across an unrelated
128
+ * link or button inside a slide doesn't activate it (the same edge case
129
+ * Shoelace's own `sl-carousel` handles for exactly this reason).
130
+ */
131
+ protected readonly _dragController: DragGestureController;
132
+ private _busy;
133
+ private _activeIndex;
134
+ private _defaultActiveIndex;
135
+ private _uncontrolledActiveIndex;
136
+ private _activeIndexInitialized;
137
+ /** Controls the active slide. Host writes are silent. */
138
+ get activeIndex(): number;
139
+ set activeIndex(value: number | undefined);
140
+ /** Initial active slide for uncontrolled usage. */
141
+ get defaultActiveIndex(): number;
142
+ set defaultActiveIndex(value: number);
143
+ /**
144
+ * The scroll-snap track element. Internal integration point consumed by
145
+ * `rc-carousel-item`'s IntersectionObserver (its `root` must be this
146
+ * track, not the viewport, so peeking/clipped-but-viewport-visible
147
+ * slides are still correctly detected as off-screen) — not a public API.
148
+ */
149
+ get trackElement(): HTMLElement | null;
150
+ constructor();
151
+ connectedCallback(): void;
152
+ disconnectedCallback(): void;
153
+ protected firstUpdated(): void;
154
+ protected updated(changed: Map<string, unknown>): void;
155
+ /** Moves to the next slide. */
156
+ next(): void;
157
+ /** Moves to the previous slide. */
158
+ previous(): void;
159
+ /** Moves directly to a slide index. */
160
+ goToIndex(index: number, instant?: boolean): void;
161
+ private _step;
162
+ /** Whether a previous/next button (or an equivalent keyboard action)
163
+ * currently has anywhere to go — always true when `loop` is set and
164
+ * there's more than one slide. */
165
+ private _canStep;
166
+ /**
167
+ * Wraps when `loop` is set, otherwise clamps to the real slide range.
168
+ * Wrapping here covers button/keyboard navigation; a swipe wraps
169
+ * seamlessly through `_trackSlots`'s cloned lead/trail slides instead,
170
+ * since that needs the clones' own track positions, not just an index.
171
+ */
172
+ private _clampIndex;
173
+ private _snapPoints;
174
+ /**
175
+ * Every scrollable track position in rendered order, including cloned
176
+ * lead/trail slides when `loop` is on — `[lastClone, item0, ..., itemN-1,
177
+ * firstClone]`. Each slot reports which real slide index it represents
178
+ * (a clone mirrors the real slide it duplicates), so both scrolling to
179
+ * an index and reading back a settled scroll position can stay in terms
180
+ * of real indices while the clones do the seamless-wrap work.
181
+ */
182
+ private _trackSlots;
183
+ private _itemStep;
184
+ private _setActiveIndex;
185
+ private _scrollToIndex;
186
+ private _onSlotChange;
187
+ private _directItems;
188
+ private _setItemPositions;
189
+ /**
190
+ * Prepending/appending the loop clones below is itself a light-DOM
191
+ * mutation on this host, which re-fires `slotchange` (asynchronously) —
192
+ * `_onSlotChange` calls back into this method, so a naive unconditional
193
+ * remove-then-recreate would recreate a fresh pair of clones on every
194
+ * pass forever. Clones are always excluded from `nextItems`, so the
195
+ * real item list is byte-for-byte identical on that re-entrant pass;
196
+ * only rebuild clone DOM when either the real items or the desired
197
+ * clone presence has actually changed, so the re-entrant pass is a
198
+ * true no-op and the recursion terminates.
199
+ */
200
+ private _syncItems;
201
+ /**
202
+ * Clones the first and last slide and prepends/appends them
203
+ * (`data-clone="<real index>"` marks them, excluded from `_items` and
204
+ * from position numbering) so the track has real scrollable content
205
+ * past both visual ends. `_trackSlots`/`_commitSettledIndex` detect
206
+ * when a clone settles and instantly re-anchor to the real slide it
207
+ * mirrors — Shoelace's proven technique for a seamless infinite swipe,
208
+ * rather than a discontinuous index-modulo jump back to the other end.
209
+ */
210
+ private _addClones;
211
+ /** Keeps visual loop clones out of forms, focus order, and the accessibility tree. */
212
+ private _sanitizeClone;
213
+ private _removeClones;
214
+ /**
215
+ * On release, a fast enough flick pre-nudges `scrollLeft` to the next
216
+ * snap point in the drag direction (a "decisive swipe" — mirroring
217
+ * `rc-bottom-sheet`'s own velocity-vs-nearest-point settle heuristic,
218
+ * adapted from "jump to the extreme end" for a 2-point sheet to "advance
219
+ * one further point" for a carousel that can have many). Either way,
220
+ * this hands off to the same debounced settle path a native swipe
221
+ * already goes through — our own `scrollLeft` writes during the drag
222
+ * already fired real `scroll` events, so `_onScroll` just needs to run
223
+ * its usual timer to pick up wherever things ended.
224
+ */
225
+ private _endDrag;
226
+ /**
227
+ * Debounced, not per-frame: `_scrollToIndex`'s own smooth scroll fires
228
+ * many `scroll` events while still mid-flight, and reading the settled
229
+ * index from an in-progress position would report the wrong slide and
230
+ * immediately fight the very scroll driving it. Waiting for scrolling to
231
+ * actually stop (swipe release or animation end) avoids that oscillation.
232
+ */
233
+ private _onScroll;
234
+ private _commitSettledIndex;
235
+ private _onNavigate;
236
+ /**
237
+ * A real drag still ends in a native `click` on release (browsers fire
238
+ * one on pointerup as long as pointerdown/pointerup share a target),
239
+ * landing on whatever's currently under the pointer — which, mid-drag,
240
+ * is often unrelated slide content the user was scrolling past, not
241
+ * clicking. Capturing and swallowing exactly one click right after a
242
+ * drag activated (`_suppressNextClick`, set in the drag controller's
243
+ * `onStart`) stops that from reaching a link/button inside a slide.
244
+ */
245
+ private _onTrackClickCapture;
246
+ private _paginationButtons;
247
+ /**
248
+ * Hand-rolled roving tabindex for the pagination row, not
249
+ * `RovingTabIndexMixin`: that mixin collects items from a real
250
+ * `slotchange`-firing `<slot>`, matching its intended use (consumer-
251
+ * authored, slotted content, like `rc-toolbar`'s buttons) — these
252
+ * buttons are shadow-DOM content this component renders itself, so
253
+ * there's no slot to collect from. The pattern is otherwise the same
254
+ * one the mixin itself implements: exactly one button is a tab stop,
255
+ * arrow keys move focus among them (not activeIndex — matching
256
+ * rc-toolbar's own "arrows move focus, Enter/Space activates" model,
257
+ * not a tablist's auto-activate-on-arrow), and focus keeps tabindex in
258
+ * sync wherever it lands (click, Tab, or a `.focus()` call below).
259
+ */
260
+ private _onPaginationFocus;
261
+ private _onPaginationNavigate;
262
+ /**
263
+ * One-time tabindex seed for pagination buttons that have never
264
+ * received focus yet (matching `activeIndex`, the sane default) —
265
+ * `_onPaginationFocus` takes over from there. Safe to call on every
266
+ * render: a no-op once every button already has some tabindex value.
267
+ */
268
+ private _initPaginationTabIndex;
269
+ protected render(): import('lit').TemplateResult<1>;
270
+ }
@@ -0,0 +1,2 @@
1
+ export declare const carouselStyles: import('lit').CSSResult;
2
+ export default carouselStyles;
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@rcarls/rc-carousel",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.1.0",
7
+ "description": "WAI-ARIA APG carousel pattern built on native CSS scroll-snap and consumer-controlled slide sizing.",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/richardcarls/rc-webcomponents.git",
11
+ "directory": "packages/rc-carousel"
12
+ },
13
+ "homepage": "https://richardcarls.github.io/rc-webcomponents/components/rc-carousel",
14
+ "license": "MIT",
15
+ "type": "module",
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "module": "./dist/rc-carousel.js",
20
+ "exports": {
21
+ ".": {
22
+ "import": {
23
+ "types": "./dist/types/packages/rc-carousel/src/index.d.ts",
24
+ "default": "./dist/rc-carousel.js"
25
+ }
26
+ },
27
+ "./define": {
28
+ "import": {
29
+ "types": "./dist/types/packages/rc-carousel/src/define.d.ts",
30
+ "default": "./dist/rc-carousel-define.js"
31
+ }
32
+ }
33
+ },
34
+ "types": "./dist/types/packages/rc-carousel/src/index.d.ts",
35
+ "sideEffects": [
36
+ "./dist/rc-carousel-define.js"
37
+ ],
38
+ "customElements": "dist/custom-elements.json",
39
+ "scripts": {
40
+ "build": "tsc && vite build && cem analyze",
41
+ "cem:analyze": "cem analyze",
42
+ "preview": "vite preview",
43
+ "test:browser": "vitest --run",
44
+ "test:browser:chrome": "vitest --run --project=chromium",
45
+ "test:browser:firefox": "vitest --run --project=firefox"
46
+ },
47
+ "dependencies": {
48
+ "@rcarls/rc-common": "0.5.0"
49
+ },
50
+ "devDependencies": {
51
+ "@custom-elements-manifest/analyzer": "0.11.0",
52
+ "@vitest/browser-playwright": "4.1.5",
53
+ "lit": "^3.0.0",
54
+ "playwright": "^1.56.0",
55
+ "rollup": "^4.60.2",
56
+ "typescript": "~5.9.3",
57
+ "vite": "^7.1.7",
58
+ "vite-plugin-dts": "^4.5.4",
59
+ "vitest": "^4.0.6",
60
+ "vitest-browser-lit": "^1.0.1"
61
+ },
62
+ "peerDependencies": {
63
+ "lit": "^3.0.0"
64
+ }
65
+ }