@rcarls/rc-carousel 0.0.0-next-20260921045401

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,671 @@
1
+ import { css as f, LitElement as m, html as h, nothing as d } from "lit";
2
+ import { property as l, query as b, state as x } from "lit/decorators.js";
3
+ import { DragGestureController as I, findNearestSnapIndex as k, findExtremeSnapIndex as g, findNextSnapIndex as A, keyNavigation as v } from "@rcarls/rc-common";
4
+ const C = f`
5
+ :host {
6
+ display: block;
7
+ box-sizing: border-box;
8
+ min-inline-size: 0;
9
+ min-block-size: 0;
10
+ scroll-snap-align: var(--rc-carousel-item-scroll-snap-align, start);
11
+ /*
12
+ * Without this, a normal-speed swipe carries enough fling momentum to
13
+ * sail past the very next item and settle two (or more) items over —
14
+ * \`mandatory\` scroll-snap-type on the track alone only guarantees
15
+ * landing on *a* snap point, not the nearest one. \`always\` forces the
16
+ * browser to stop at each snap point in turn.
17
+ */
18
+ scroll-snap-stop: always;
19
+ overflow: var(--rc-carousel-item-overflow, hidden);
20
+ border-radius: var(--rc-carousel-item-border-radius, 0);
21
+ background: var(--rc-carousel-item-background, transparent);
22
+ color: var(--rc-carousel-item-color, CanvasText);
23
+ color-scheme: inherit;
24
+ }
25
+
26
+ :host([hidden]) {
27
+ display: none;
28
+ }
29
+
30
+ /*
31
+ * Peeking/off-screen items are hidden from assistive technology (see
32
+ * rc-carousel-item.ts's IntersectionObserver) but must stay visually and
33
+ * interactively present — a peek is a legitimate visual affordance, not
34
+ * decoration to strip. This only removes it from the accessibility tree
35
+ * and, via inert, the tab sequence, matching the same technique proven
36
+ * carousels (e.g. Shoelace's sl-carousel) use for exactly this problem.
37
+ */
38
+ :host([aria-hidden='true']) {
39
+ pointer-events: none;
40
+ }
41
+ `;
42
+ var E = Object.defineProperty, S = (c, t, e, s) => {
43
+ for (var i = void 0, n = c.length - 1, a; n >= 0; n--)
44
+ (a = c[n]) && (i = a(t, e, i) || i);
45
+ return i && E(t, e, i), i;
46
+ };
47
+ const p = class p extends m {
48
+ constructor() {
49
+ super(), this._intersectionObserver = null, this.position = "", this._internals = this.attachInternals(), this._internals.role = "group";
50
+ }
51
+ connectedCallback() {
52
+ super.connectedCallback(), this.hasAttribute("role") || this.setAttribute("role", "group"), this.hasAttribute("aria-roledescription") || this.setAttribute("aria-roledescription", "slide"), this._observeIntersection();
53
+ }
54
+ disconnectedCallback() {
55
+ super.disconnectedCallback(), this._intersectionObserver?.disconnect(), this._intersectionObserver = null;
56
+ }
57
+ updated(t) {
58
+ super.updated(t), t.has("position") && this._syncDefaultLabel();
59
+ }
60
+ _syncDefaultLabel() {
61
+ this.hasAttribute("aria-label") || this.hasAttribute("aria-labelledby") || this.position && this.setAttribute("aria-label", this.position);
62
+ }
63
+ /**
64
+ * Observes intersection against the parent carousel's own scroll
65
+ * container (not the viewport — a slide can be viewport-visible yet
66
+ * still clipped/off-screen within the track on a page where the
67
+ * carousel isn't the whole viewport). Reads `trackElement` off the
68
+ * parent `rc-carousel`, an internal integration point rather than a
69
+ * public API — awaits the parent's own first render first, since a
70
+ * slotted item's `connectedCallback` isn't guaranteed to run after its
71
+ * host's, and `trackElement` only exists once the host has rendered.
72
+ */
73
+ async _observeIntersection() {
74
+ if (this.hasAttribute("data-clone")) {
75
+ this.setAttribute("aria-hidden", "true"), this.setAttribute("inert", "");
76
+ return;
77
+ }
78
+ const t = this.closest("rc-carousel");
79
+ await t?.updateComplete;
80
+ const e = t?.trackElement ?? null;
81
+ if (this._intersectionObserver?.disconnect(), typeof IntersectionObserver != "function" || !e) {
82
+ this.removeAttribute("aria-hidden"), this.removeAttribute("inert");
83
+ return;
84
+ }
85
+ this._intersectionObserver = new IntersectionObserver(
86
+ ([s]) => {
87
+ const i = s !== void 0 && !s.isIntersecting;
88
+ i ? (this.contains(this.ownerDocument.activeElement) && t?.trackElement?.focus(), this.setAttribute("aria-hidden", "true")) : this.removeAttribute("aria-hidden"), this.toggleAttribute("inert", i);
89
+ },
90
+ // A low/zero threshold would count a peeking neighbor's own sliver
91
+ // of visible pixels as "intersecting", never hiding it — a peek is
92
+ // real but not readable/interactive, so it should still hide. 0.5
93
+ // reliably separates "the active slide" (near-fully visible) from
94
+ // "a peeking neighbor" (a fraction of it showing).
95
+ { root: e, threshold: 0.5 }
96
+ ), this._intersectionObserver.observe(this);
97
+ }
98
+ render() {
99
+ return h`<slot></slot>`;
100
+ }
101
+ };
102
+ p.styles = C;
103
+ let u = p;
104
+ S([
105
+ l({ type: String, attribute: !1 })
106
+ ], u.prototype, "position");
107
+ const w = f`
108
+ :host {
109
+ display: block;
110
+ position: relative;
111
+ color: var(--rc-carousel-color, CanvasText);
112
+ color-scheme: inherit;
113
+ }
114
+
115
+ #track {
116
+ display: grid;
117
+ grid-auto-flow: column;
118
+ grid-auto-columns: var(--rc-carousel-slide-size, calc(100% - 4rem));
119
+ column-gap: var(--rc-carousel-gap, 8px);
120
+ inline-size: 100%;
121
+ block-size: 100%;
122
+ overflow-x: auto;
123
+ overflow-y: hidden;
124
+ overscroll-behavior-x: contain;
125
+ scroll-snap-type: x mandatory;
126
+ /*
127
+ * Deliberately no \`scroll-behavior: smooth\` here — _scrollToIndex in
128
+ * rc-carousel.ts passes \`behavior: 'smooth'\` explicitly per
129
+ * programmatic call instead, which is unaffected either way (an
130
+ * explicit argument always overrides this CSS default). Setting it
131
+ * here as an ambient default would also apply to the browser's own
132
+ * native snap-settle correction after a swipe — a documented
133
+ * cross-browser conflict with scroll-snap-stop: always (each
134
+ * rc-carousel-item sets that; Firefox bugzilla 1643217, 1959811)
135
+ * that produces a jerk-then-snap-back artifact on an ordinary swipe.
136
+ */
137
+ scrollbar-width: none;
138
+ }
139
+
140
+ #track::-webkit-scrollbar {
141
+ display: none;
142
+ }
143
+
144
+ :host([mouse-dragging]) #track {
145
+ cursor: grab;
146
+ }
147
+
148
+ :host([mouse-dragging]) #track.dragging {
149
+ cursor: grabbing;
150
+ /*
151
+ * scroll-snap-type itself is toggled imperatively in rc-carousel.ts,
152
+ * not here — it must be off before the very first scrollLeft write of
153
+ * a drag, and this class only lands on the next reactive render, a
154
+ * render pass too late for that first write (the browser eagerly
155
+ * resnaps a plain scrollLeft assignment right back to the nearest
156
+ * snap point, same as any other programmatic scroll).
157
+ */
158
+ }
159
+
160
+ #navigation {
161
+ display: contents;
162
+ }
163
+
164
+ [part~='navigation-button'] {
165
+ position: absolute;
166
+ top: 50%;
167
+ translate: 0 -50%;
168
+ display: inline-flex;
169
+ align-items: center;
170
+ justify-content: center;
171
+ inline-size: var(--rc-carousel-navigation-button-size, 40px);
172
+ block-size: var(--rc-carousel-navigation-button-size, 40px);
173
+ border: none;
174
+ border-radius: 50%;
175
+ background: var(
176
+ --rc-carousel-navigation-button-background,
177
+ color-mix(in srgb, CanvasText 12%, transparent)
178
+ );
179
+ color: var(--rc-carousel-navigation-button-color, CanvasText);
180
+ cursor: pointer;
181
+ }
182
+
183
+ [part~='navigation-button'][aria-disabled='true'] {
184
+ opacity: 0.38;
185
+ cursor: default;
186
+ }
187
+
188
+ [part~='navigation-button-previous'] {
189
+ inset-inline-start: var(--rc-carousel-navigation-inset, 8px);
190
+ }
191
+
192
+ [part~='navigation-button-next'] {
193
+ inset-inline-end: var(--rc-carousel-navigation-inset, 8px);
194
+ }
195
+
196
+ #pagination {
197
+ position: absolute;
198
+ inset-block-end: var(--rc-carousel-navigation-inset, 8px);
199
+ inset-inline: 0;
200
+ display: flex;
201
+ justify-content: center;
202
+ gap: var(--rc-carousel-gap, 8px);
203
+ }
204
+
205
+ [part~='pagination-item'] {
206
+ inline-size: var(--rc-carousel-pagination-item-size, 8px);
207
+ block-size: var(--rc-carousel-pagination-item-size, 8px);
208
+ padding: 0;
209
+ border: none;
210
+ border-radius: 50%;
211
+ background: var(
212
+ --rc-carousel-pagination-item-color,
213
+ color-mix(in srgb, CanvasText 40%, transparent)
214
+ );
215
+ cursor: pointer;
216
+ }
217
+
218
+ [part~='pagination-item-active'] {
219
+ background: var(--rc-carousel-pagination-item-active-color, Highlight);
220
+ cursor: default;
221
+ }
222
+ `;
223
+ var T = Object.defineProperty, $ = Object.getOwnPropertyDescriptor, o = (c, t, e, s) => {
224
+ for (var i = s > 1 ? void 0 : s ? $(t, e) : t, n = c.length - 1, a; n >= 0; n--)
225
+ (a = c[n]) && (i = (s ? a(t, e, i) : a(i)) || i);
226
+ return s && i && T(t, e, i), i;
227
+ };
228
+ const z = 300, P = 120, _ = class _ extends m {
229
+ constructor() {
230
+ super(), this._items = [], this._cloneItems = [], this._mounted = !1, this._suppressSync = !1, this._pendingInstant = !1, this.loop = !1, this.navigation = !1, this.pagination = !1, this.mouseDragging = !1, this._dragging = !1, this._dragStartLeft = 0, this._suppressNextClick = !1, this._dragController = new I(this, {
231
+ target: () => this._trackEl ?? null,
232
+ axis: "x",
233
+ activation: "axis",
234
+ canStart: (t) => this.mouseDragging && t.pointerType === "mouse",
235
+ onStart: () => {
236
+ this._dragging = !0, this._dragStartLeft = this._trackEl?.scrollLeft ?? 0, this._suppressNextClick = !0, this._trackEl && (this._trackEl.style.scrollSnapType = "none");
237
+ },
238
+ onMove: (t) => {
239
+ this._trackEl && (this._trackEl.scrollLeft = this._dragStartLeft - t.deltaX);
240
+ },
241
+ onEnd: (t) => this._endDrag(t),
242
+ onCancel: (t) => this._endDrag(t)
243
+ }), this._busy = !1, this._defaultActiveIndex = 0, this._activeIndexInitialized = !1, this._onSlotChange = () => {
244
+ this._syncItems();
245
+ }, this._onScroll = () => {
246
+ this._settleTimer !== void 0 && clearTimeout(this._settleTimer), this._busy = !0, this._settleTimer = setTimeout(this._commitSettledIndex, P);
247
+ }, this._commitSettledIndex = () => {
248
+ if (this._busy = !1, !this._trackEl)
249
+ return;
250
+ const t = this._trackSlots(), e = k(
251
+ t.map((n) => n.point),
252
+ this._trackEl.scrollLeft
253
+ ), s = e >= 0 ? t[e] : void 0;
254
+ if (!s)
255
+ return;
256
+ if (s.isClone) {
257
+ const n = t.find((a) => !a.isClone && a.index === s.index);
258
+ n && (this._trackEl.scrollLeft = n.point);
259
+ }
260
+ if (s.index === this.activeIndex)
261
+ return;
262
+ const i = this._activeIndex !== void 0;
263
+ i || (this._suppressSync = !0), this._setActiveIndex(s.index, "swipe"), i && this.activeIndex !== s.index && this._scrollToIndex(this.activeIndex, !0);
264
+ }, this._onNavigate = (t) => {
265
+ switch (t) {
266
+ case "next":
267
+ this._step(1, "keyboard");
268
+ break;
269
+ case "prev":
270
+ this._step(-1, "keyboard");
271
+ break;
272
+ case "start": {
273
+ const e = g(this._snapPoints(), -1);
274
+ e >= 0 && this._setActiveIndex(e, "keyboard");
275
+ break;
276
+ }
277
+ case "end": {
278
+ const e = g(this._snapPoints(), 1);
279
+ e >= 0 && this._setActiveIndex(e, "keyboard");
280
+ break;
281
+ }
282
+ }
283
+ }, this._onTrackClickCapture = (t) => {
284
+ this._suppressNextClick && (this._suppressNextClick = !1, t.stopPropagation(), t.preventDefault());
285
+ }, this._onPaginationFocus = (t) => {
286
+ const e = this._paginationButtons(), s = t.composedPath().find((i) => e.includes(i));
287
+ s && e.forEach((i) => i.setAttribute("tabindex", i === s ? "0" : "-1"));
288
+ }, this._onPaginationNavigate = (t) => {
289
+ const e = this._paginationButtons();
290
+ if (!e.length)
291
+ return;
292
+ const s = e.indexOf(this.shadowRoot?.activeElement), i = s < 0 ? 0 : s;
293
+ switch (t) {
294
+ case "next":
295
+ e[(i + 1) % e.length]?.focus();
296
+ break;
297
+ case "prev":
298
+ e[(i - 1 + e.length) % e.length]?.focus();
299
+ break;
300
+ case "start":
301
+ e[0]?.focus();
302
+ break;
303
+ case "end":
304
+ e[e.length - 1]?.focus();
305
+ break;
306
+ }
307
+ }, this._internals = this.attachInternals(), this._internals.role = "group";
308
+ }
309
+ get activeIndex() {
310
+ return this._clampIndex(
311
+ this._activeIndex ?? this._uncontrolledActiveIndex ?? this._defaultActiveIndex
312
+ );
313
+ }
314
+ set activeIndex(t) {
315
+ const e = this.activeIndex;
316
+ this._activeIndex = t, this._activeIndexInitialized = !0, this.requestUpdate("activeIndex", e);
317
+ }
318
+ get defaultActiveIndex() {
319
+ return this._defaultActiveIndex;
320
+ }
321
+ set defaultActiveIndex(t) {
322
+ const e = this._defaultActiveIndex;
323
+ this._defaultActiveIndex = t, !this._activeIndexInitialized && this._activeIndex === void 0 && this._uncontrolledActiveIndex === void 0 && this.requestUpdate("activeIndex", e), this.requestUpdate("defaultActiveIndex", e);
324
+ }
325
+ /**
326
+ * The scroll-snap track element. Internal integration point consumed by
327
+ * `rc-carousel-item`'s IntersectionObserver (its `root` must be this
328
+ * track, not the viewport, so peeking/clipped-but-viewport-visible
329
+ * slides are still correctly detected as off-screen) — not a public API.
330
+ */
331
+ get trackElement() {
332
+ return this._trackEl ?? null;
333
+ }
334
+ connectedCallback() {
335
+ super.connectedCallback(), this._items = this._directItems(), this._setItemPositions(), this.hasAttribute("role") || this.setAttribute("role", "group"), this.hasAttribute("aria-roledescription") || this.setAttribute("aria-roledescription", "carousel");
336
+ }
337
+ disconnectedCallback() {
338
+ super.disconnectedCallback(), this._settleTimer !== void 0 && clearTimeout(this._settleTimer);
339
+ }
340
+ firstUpdated() {
341
+ this._syncActiveItemAccessibility(this.activeIndex), this._scrollToIndex(this.activeIndex, !0), this._mounted = !0;
342
+ }
343
+ updated(t) {
344
+ if (super.updated(t), this._initPaginationTabIndex(), t.has("loop") && this._mounted && this._syncItems(), !t.has("activeIndex"))
345
+ return;
346
+ if (this._syncActiveItemAccessibility(this.activeIndex), this._suppressSync) {
347
+ this._suppressSync = !1;
348
+ return;
349
+ }
350
+ const e = !this._mounted || this._pendingInstant;
351
+ this._pendingInstant = !1, this._scrollToIndex(this.activeIndex, e);
352
+ }
353
+ /** Moves to the next slide. */
354
+ next() {
355
+ this._step(1, "api");
356
+ }
357
+ /** Moves to the previous slide. */
358
+ previous() {
359
+ this._step(-1, "api");
360
+ }
361
+ /** Moves directly to a slide index. */
362
+ goToIndex(t, e = !1) {
363
+ this._pendingInstant = e, this._setActiveIndex(t, "api");
364
+ }
365
+ _step(t, e) {
366
+ this._setActiveIndex(this.activeIndex + t, e);
367
+ }
368
+ /** Whether a previous/next button (or an equivalent keyboard action)
369
+ * currently has anywhere to go — always true when `loop` is set and
370
+ * there's more than one slide. */
371
+ _canStep(t) {
372
+ const e = this._items.length;
373
+ return e <= 1 ? !1 : this.loop ? !0 : t > 0 ? this.activeIndex < e - 1 : this.activeIndex > 0;
374
+ }
375
+ /**
376
+ * Wraps when `loop` is set, otherwise clamps to the real slide range.
377
+ * Wrapping here covers button/keyboard navigation; a swipe wraps
378
+ * seamlessly through `_trackSlots`'s cloned lead/trail slides instead,
379
+ * since that needs the clones' own track positions, not just an index.
380
+ */
381
+ _clampIndex(t) {
382
+ const e = this._items.length;
383
+ return e === 0 ? 0 : this.loop ? (t % e + e) % e : Math.min(e - 1, Math.max(0, t));
384
+ }
385
+ _snapPoints() {
386
+ const t = this._itemStep();
387
+ return this._items.map((e, s) => s * t);
388
+ }
389
+ /**
390
+ * Every scrollable track position in rendered order, including cloned
391
+ * lead/trail slides when `loop` is on — `[lastClone, item0, ..., itemN-1,
392
+ * firstClone]`. Each slot reports which real slide index it represents
393
+ * (a clone mirrors the real slide it duplicates), so both scrolling to
394
+ * an index and reading back a settled scroll position can stay in terms
395
+ * of real indices while the clones do the seamless-wrap work.
396
+ */
397
+ _trackSlots() {
398
+ const t = this._itemStep(), e = this._items.length;
399
+ return !this.loop || e < 2 ? this._items.map((s, i) => ({ point: i * t, index: i, isClone: !1 })) : [
400
+ { point: 0, index: e - 1, isClone: !0 },
401
+ ...this._items.map((s, i) => ({ point: (i + 1) * t, index: i, isClone: !1 })),
402
+ { point: (e + 1) * t, index: 0, isClone: !0 }
403
+ ];
404
+ }
405
+ _itemStep() {
406
+ if (!this._trackEl)
407
+ return 1;
408
+ const t = this._items[0];
409
+ if (!(t instanceof HTMLElement))
410
+ return this._trackEl.clientWidth;
411
+ const e = Number.parseFloat(getComputedStyle(this._trackEl).columnGap || "0") || 0;
412
+ return t.offsetWidth + e;
413
+ }
414
+ _setActiveIndex(t, e) {
415
+ const s = this._clampIndex(t), i = this.activeIndex;
416
+ i !== s && (this._activeIndex === void 0 && (this._uncontrolledActiveIndex = s, this.requestUpdate("activeIndex", i)), this.dispatchEvent(
417
+ new CustomEvent("rc-carousel-change", {
418
+ bubbles: !0,
419
+ composed: !0,
420
+ detail: { index: s, trigger: e }
421
+ })
422
+ ));
423
+ }
424
+ _scrollToIndex(t, e) {
425
+ if (!this._trackEl)
426
+ return;
427
+ const s = this._trackSlots().find((a) => !a.isClone && a.index === t), i = s ? s.point : t * this._itemStep(), n = this.ownerDocument.defaultView?.matchMedia("(prefers-reduced-motion: reduce)").matches ?? !1;
428
+ e || n ? this._trackEl.scrollLeft = i : this._trackEl.scrollTo({ left: i, behavior: "smooth" });
429
+ }
430
+ /**
431
+ * Keep the authored active slide available to assistive technology even
432
+ * before a programmatic scroll produces its next intersection callback.
433
+ * IntersectionObserver still takes over during direct manipulation, where
434
+ * the visually dominant slide can change before scroll settling updates the
435
+ * public active index.
436
+ */
437
+ _syncActiveItemAccessibility(t) {
438
+ this._directItems().forEach((e, s) => {
439
+ const i = s !== t;
440
+ e.toggleAttribute("aria-hidden", i), e.toggleAttribute("inert", i);
441
+ });
442
+ }
443
+ _directItems() {
444
+ return Array.from(this.children).filter(
445
+ (t) => t.tagName === "RC-CAROUSEL-ITEM" && !t.hasAttribute("data-clone")
446
+ );
447
+ }
448
+ _setItemPositions() {
449
+ this._items.forEach((t, e) => {
450
+ t.position = `${e + 1} of ${this._items.length}`;
451
+ });
452
+ }
453
+ /**
454
+ * Prepending/appending the loop clones below is itself a light-DOM
455
+ * mutation on this host, which re-fires `slotchange` (asynchronously) —
456
+ * `_onSlotChange` calls back into this method, so a naive unconditional
457
+ * remove-then-recreate would recreate a fresh pair of clones on every
458
+ * pass forever. Clones are always excluded from `nextItems`, so the
459
+ * real item list is byte-for-byte identical on that re-entrant pass;
460
+ * only rebuild clone DOM when either the real items or the desired
461
+ * clone presence has actually changed, so the re-entrant pass is a
462
+ * true no-op and the recursion terminates.
463
+ */
464
+ _syncItems() {
465
+ const e = (this._slotEl?.assignedElements() ?? []).filter(
466
+ (a) => a.tagName === "RC-CAROUSEL-ITEM" && !a.hasAttribute("data-clone")
467
+ ), s = e.length !== this._items.length || e.some((a, y) => a !== this._items[y]), i = this.loop && e.length > 1, n = this._cloneItems.length > 0;
468
+ this._items = e, this._setItemPositions(), s && this._mounted && this.requestUpdate(), !(!s && i === n) && (this._removeClones(), i && this._addClones());
469
+ }
470
+ /**
471
+ * Clones the first and last slide and prepends/appends them
472
+ * (`data-clone="<real index>"` marks them, excluded from `_items` and
473
+ * from position numbering) so the track has real scrollable content
474
+ * past both visual ends. `_trackSlots`/`_commitSettledIndex` detect
475
+ * when a clone settles and instantly re-anchor to the real slide it
476
+ * mirrors — Shoelace's proven technique for a seamless infinite swipe,
477
+ * rather than a discontinuous index-modulo jump back to the other end.
478
+ */
479
+ _addClones() {
480
+ const t = this._items[0], e = this._items[this._items.length - 1];
481
+ if (!t || !e)
482
+ return;
483
+ const s = e.cloneNode(!0);
484
+ s.setAttribute("data-clone", String(this._items.length - 1)), this._sanitizeClone(s), this.prepend(s);
485
+ const i = t.cloneNode(!0);
486
+ i.setAttribute("data-clone", "0"), this._sanitizeClone(i), this.append(i), this._cloneItems = [s, i];
487
+ }
488
+ /** Keeps visual loop clones out of forms, focus order, and the accessibility tree. */
489
+ _sanitizeClone(t) {
490
+ t.setAttribute("aria-hidden", "true"), t.setAttribute("inert", "");
491
+ for (const e of [t, ...t.querySelectorAll("*")])
492
+ e.removeAttribute("id"), e.removeAttribute("name"), e.removeAttribute("form");
493
+ }
494
+ _removeClones() {
495
+ this._cloneItems.forEach((t) => t.remove()), this._cloneItems = [];
496
+ }
497
+ /**
498
+ * On release, a fast enough flick pre-nudges `scrollLeft` to the next
499
+ * snap point in the drag direction (a "decisive swipe" — mirroring
500
+ * `rc-bottom-sheet`'s own velocity-vs-nearest-point settle heuristic,
501
+ * adapted from "jump to the extreme end" for a 2-point sheet to "advance
502
+ * one further point" for a carousel that can have many). Either way,
503
+ * this hands off to the same debounced settle path a native swipe
504
+ * already goes through — our own `scrollLeft` writes during the drag
505
+ * already fired real `scroll` events, so `_onScroll` just needs to run
506
+ * its usual timer to pick up wherever things ended.
507
+ */
508
+ _endDrag(t) {
509
+ if (this._dragging = !1, this._trackEl && (this._trackEl.style.scrollSnapType = ""), this._trackEl && Math.abs(t.velocityX) > z) {
510
+ const e = this._trackSlots(), s = t.velocityX < 0 ? 1 : -1, i = A(
511
+ e.map((a) => a.point),
512
+ this._trackEl.scrollLeft,
513
+ s
514
+ ), n = e[i];
515
+ n && (this._trackEl.scrollLeft = n.point);
516
+ }
517
+ this._onScroll();
518
+ }
519
+ _paginationButtons() {
520
+ return Array.from(
521
+ this.shadowRoot?.querySelectorAll("[data-pagination-item]") ?? []
522
+ );
523
+ }
524
+ /**
525
+ * One-time tabindex seed for pagination buttons that have never
526
+ * received focus yet (matching `activeIndex`, the sane default) —
527
+ * `_onPaginationFocus` takes over from there. Safe to call on every
528
+ * render: a no-op once every button already has some tabindex value.
529
+ */
530
+ _initPaginationTabIndex() {
531
+ this._paginationButtons().forEach((t, e) => {
532
+ t.hasAttribute("tabindex") || t.setAttribute("tabindex", e === this.activeIndex ? "0" : "-1");
533
+ });
534
+ }
535
+ render() {
536
+ const t = this._canStep(-1), e = this._canStep(1);
537
+ return h`
538
+ <div
539
+ id="track"
540
+ part="track"
541
+ tabindex="0"
542
+ class=${this._dragging ? "dragging" : d}
543
+ aria-busy=${this._busy ? "true" : "false"}
544
+ aria-atomic="true"
545
+ @scroll=${this._onScroll}
546
+ @click=${{ handleEvent: this._onTrackClickCapture, capture: !0 }}
547
+ ${v(this._onNavigate, { navigationAxis: "horizontal" })}
548
+ >
549
+ <slot @slotchange=${this._onSlotChange}></slot>
550
+ </div>
551
+
552
+ ${this.navigation ? h`
553
+ <div id="navigation" part="navigation">
554
+ <button
555
+ type="button"
556
+ part="navigation-button navigation-button-previous"
557
+ aria-label="Previous slide"
558
+ aria-disabled=${t ? "false" : "true"}
559
+ @click=${() => {
560
+ t && this._step(-1, "button");
561
+ }}
562
+ >
563
+ <span aria-hidden="true">
564
+ <slot name="previous-icon">
565
+ <svg
566
+ viewBox="0 0 6 10"
567
+ width="10"
568
+ height="16"
569
+ fill="none"
570
+ stroke="currentColor"
571
+ stroke-width="1.5"
572
+ stroke-linecap="round"
573
+ stroke-linejoin="round"
574
+ >
575
+ <polyline points="5,1 1,5 5,9" />
576
+ </svg>
577
+ </slot>
578
+ </span>
579
+ </button>
580
+ <button
581
+ type="button"
582
+ part="navigation-button navigation-button-next"
583
+ aria-label="Next slide"
584
+ aria-disabled=${e ? "false" : "true"}
585
+ @click=${() => {
586
+ e && this._step(1, "button");
587
+ }}
588
+ >
589
+ <span aria-hidden="true">
590
+ <slot name="next-icon">
591
+ <svg
592
+ viewBox="0 0 6 10"
593
+ width="10"
594
+ height="16"
595
+ fill="none"
596
+ stroke="currentColor"
597
+ stroke-width="1.5"
598
+ stroke-linecap="round"
599
+ stroke-linejoin="round"
600
+ >
601
+ <polyline points="1,1 5,5 1,9" />
602
+ </svg>
603
+ </slot>
604
+ </span>
605
+ </button>
606
+ </div>
607
+ ` : d}
608
+ ${this.pagination ? h`
609
+ <div
610
+ id="pagination"
611
+ part="pagination"
612
+ role="group"
613
+ aria-label="Choose slide to display"
614
+ @focusin=${this._onPaginationFocus}
615
+ ${v(this._onPaginationNavigate, { navigationAxis: "horizontal" })}
616
+ >
617
+ ${this._items.map(
618
+ (s, i) => h`
619
+ <button
620
+ type="button"
621
+ data-pagination-item
622
+ part="pagination-item${i === this.activeIndex ? " pagination-item-active" : ""}"
623
+ aria-label="Go to slide ${i + 1}"
624
+ aria-current=${i === this.activeIndex ? "true" : "false"}
625
+ aria-disabled=${i === this.activeIndex ? "true" : "false"}
626
+ @click=${() => this._setActiveIndex(i, "button")}
627
+ ></button>
628
+ `
629
+ )}
630
+ </div>
631
+ ` : d}
632
+ `;
633
+ }
634
+ };
635
+ _.styles = w;
636
+ let r = _;
637
+ o([
638
+ b("#track")
639
+ ], r.prototype, "_trackEl", 2);
640
+ o([
641
+ b("slot:not([name])")
642
+ ], r.prototype, "_slotEl", 2);
643
+ o([
644
+ l({ type: Boolean, reflect: !0 })
645
+ ], r.prototype, "loop", 2);
646
+ o([
647
+ l({ type: Boolean, reflect: !0 })
648
+ ], r.prototype, "navigation", 2);
649
+ o([
650
+ l({ type: Boolean, reflect: !0 })
651
+ ], r.prototype, "pagination", 2);
652
+ o([
653
+ l({ type: Boolean, reflect: !0, attribute: "mouse-dragging" })
654
+ ], r.prototype, "mouseDragging", 2);
655
+ o([
656
+ x()
657
+ ], r.prototype, "_dragging", 2);
658
+ o([
659
+ x()
660
+ ], r.prototype, "_busy", 2);
661
+ o([
662
+ l({ type: Number, attribute: "active-index" })
663
+ ], r.prototype, "activeIndex", 1);
664
+ o([
665
+ l({ type: Number, attribute: "default-active-index" })
666
+ ], r.prototype, "defaultActiveIndex", 1);
667
+ export {
668
+ r as R,
669
+ u as a
670
+ };
671
+ //# sourceMappingURL=rc-carousel-DTE5ztw_.js.map