@asante-org/atlascopco-vt-litesitegenerator 3.8.14 → 3.8.16

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/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useId, useState, useRef, useEffect, useCallback, useMemo, forwardRef, useImperativeHandle } from 'react';
1
+ import React, { useId, useState, useRef, useMemo, useEffect, useCallback, forwardRef, useImperativeHandle } from 'react';
2
2
 
3
3
  function _extends() {
4
4
  return _extends = Object.assign ? Object.assign.bind() : function (n) {
@@ -2260,8 +2260,257 @@ function EmblaCarousel(root, userOptions, userPlugins) {
2260
2260
  }
2261
2261
  EmblaCarousel.globalOptions = undefined;
2262
2262
 
2263
+ function clampNumber(number, min, max) {
2264
+ return Math.min(Math.max(number, min), max);
2265
+ }
2266
+ function isNumber(value) {
2267
+ return typeof value === 'number' && !isNaN(value);
2268
+ }
2269
+ function Fade(userOptions = {}) {
2270
+ const fullOpacity = 1;
2271
+ const noOpacity = 0;
2272
+ const fadeFriction = 0.68;
2273
+ let emblaApi;
2274
+ let opacities = [];
2275
+ let fadeToNextDistance;
2276
+ let distanceFromPointerDown = 0;
2277
+ let fadeVelocity = 0;
2278
+ let progress = 0;
2279
+ let shouldFadePair = false;
2280
+ let defaultSettledBehaviour;
2281
+ let defaultProgressBehaviour;
2282
+ function init(emblaApiInstance) {
2283
+ emblaApi = emblaApiInstance;
2284
+ const selectedSnap = emblaApi.selectedScrollSnap();
2285
+ const {
2286
+ scrollBody,
2287
+ containerRect,
2288
+ axis
2289
+ } = emblaApi.internalEngine();
2290
+ const containerSize = axis.measureSize(containerRect);
2291
+ fadeToNextDistance = clampNumber(containerSize * 0.75, 200, 500);
2292
+ shouldFadePair = false;
2293
+ opacities = emblaApi.scrollSnapList().map((_, index) => index === selectedSnap ? fullOpacity : noOpacity);
2294
+ defaultSettledBehaviour = scrollBody.settled;
2295
+ defaultProgressBehaviour = emblaApi.scrollProgress;
2296
+ scrollBody.settled = settled;
2297
+ emblaApi.scrollProgress = scrollProgress;
2298
+ emblaApi.on('select', select).on('slideFocus', fadeToSelectedSnapInstantly).on('pointerDown', pointerDown).on('pointerUp', pointerUp);
2299
+ disableScroll();
2300
+ fadeToSelectedSnapInstantly();
2301
+ }
2302
+ function destroy() {
2303
+ const {
2304
+ scrollBody
2305
+ } = emblaApi.internalEngine();
2306
+ scrollBody.settled = defaultSettledBehaviour;
2307
+ emblaApi.scrollProgress = defaultProgressBehaviour;
2308
+ emblaApi.off('select', select).off('slideFocus', fadeToSelectedSnapInstantly).off('pointerDown', pointerDown).off('pointerUp', pointerUp);
2309
+ emblaApi.slideNodes().forEach(slideNode => {
2310
+ const slideStyle = slideNode.style;
2311
+ slideStyle.opacity = '';
2312
+ slideStyle.transform = '';
2313
+ slideStyle.pointerEvents = '';
2314
+ if (!slideNode.getAttribute('style')) slideNode.removeAttribute('style');
2315
+ });
2316
+ }
2317
+ function fadeToSelectedSnapInstantly() {
2318
+ const selectedSnap = emblaApi.selectedScrollSnap();
2319
+ setOpacities(selectedSnap, fullOpacity);
2320
+ }
2321
+ function pointerUp() {
2322
+ shouldFadePair = false;
2323
+ }
2324
+ function pointerDown() {
2325
+ shouldFadePair = false;
2326
+ distanceFromPointerDown = 0;
2327
+ fadeVelocity = 0;
2328
+ }
2329
+ function select() {
2330
+ const duration = emblaApi.internalEngine().scrollBody.duration();
2331
+ fadeVelocity = duration ? 0 : fullOpacity;
2332
+ shouldFadePair = true;
2333
+ if (!duration) fadeToSelectedSnapInstantly();
2334
+ }
2335
+ function getSlideTransform(position) {
2336
+ const {
2337
+ axis
2338
+ } = emblaApi.internalEngine();
2339
+ const translateAxis = axis.scroll.toUpperCase();
2340
+ return `translate${translateAxis}(${axis.direction(position)}px)`;
2341
+ }
2342
+ function disableScroll() {
2343
+ const {
2344
+ translate,
2345
+ slideLooper
2346
+ } = emblaApi.internalEngine();
2347
+ translate.clear();
2348
+ translate.toggleActive(false);
2349
+ slideLooper.loopPoints.forEach(({
2350
+ translate
2351
+ }) => {
2352
+ translate.clear();
2353
+ translate.toggleActive(false);
2354
+ });
2355
+ }
2356
+ function lockExcessiveScroll(fadeIndex) {
2357
+ const {
2358
+ scrollSnaps,
2359
+ location,
2360
+ target
2361
+ } = emblaApi.internalEngine();
2362
+ if (!isNumber(fadeIndex) || opacities[fadeIndex] < 0.5) return;
2363
+ location.set(scrollSnaps[fadeIndex]);
2364
+ target.set(location);
2365
+ }
2366
+ function setOpacities(fadeIndex, velocity) {
2367
+ const scrollSnaps = emblaApi.scrollSnapList();
2368
+ scrollSnaps.forEach((_, indexA) => {
2369
+ const absVelocity = Math.abs(velocity);
2370
+ const currentOpacity = opacities[indexA];
2371
+ const isFadeIndex = indexA === fadeIndex;
2372
+ const nextOpacity = isFadeIndex ? currentOpacity + absVelocity : currentOpacity - absVelocity;
2373
+ const clampedOpacity = clampNumber(nextOpacity, noOpacity, fullOpacity);
2374
+ opacities[indexA] = clampedOpacity;
2375
+ const fadePair = isFadeIndex && shouldFadePair;
2376
+ const indexB = emblaApi.previousScrollSnap();
2377
+ if (fadePair) opacities[indexB] = 1 - clampedOpacity;
2378
+ if (isFadeIndex) setProgress(fadeIndex, clampedOpacity);
2379
+ setOpacity(indexA);
2380
+ });
2381
+ }
2382
+ function setOpacity(index) {
2383
+ const slidesInSnap = emblaApi.internalEngine().slideRegistry[index];
2384
+ const {
2385
+ scrollSnaps,
2386
+ containerRect
2387
+ } = emblaApi.internalEngine();
2388
+ const opacity = opacities[index];
2389
+ slidesInSnap.forEach(slideIndex => {
2390
+ const slideStyle = emblaApi.slideNodes()[slideIndex].style;
2391
+ const roundedOpacity = parseFloat(opacity.toFixed(2));
2392
+ const hasOpacity = roundedOpacity > noOpacity;
2393
+ const position = hasOpacity ? scrollSnaps[index] : containerRect.width + 2;
2394
+ const transform = getSlideTransform(position);
2395
+ if (hasOpacity) slideStyle.transform = transform;
2396
+ slideStyle.opacity = roundedOpacity.toString();
2397
+ slideStyle.pointerEvents = opacity > 0.5 ? 'auto' : 'none';
2398
+ if (!hasOpacity) slideStyle.transform = transform;
2399
+ });
2400
+ }
2401
+ function setProgress(fadeIndex, opacity) {
2402
+ const {
2403
+ index,
2404
+ dragHandler,
2405
+ scrollSnaps
2406
+ } = emblaApi.internalEngine();
2407
+ const pointerDown = dragHandler.pointerDown();
2408
+ const snapFraction = 1 / (scrollSnaps.length - 1);
2409
+ let indexA = fadeIndex;
2410
+ let indexB = pointerDown ? emblaApi.selectedScrollSnap() : emblaApi.previousScrollSnap();
2411
+ if (pointerDown && indexA === indexB) {
2412
+ const reverseSign = Math.sign(distanceFromPointerDown) * -1;
2413
+ indexA = indexB;
2414
+ indexB = index.clone().set(indexB).add(reverseSign).get();
2415
+ }
2416
+ const currentPosition = indexB * snapFraction;
2417
+ const diffPosition = (indexA - indexB) * snapFraction;
2418
+ progress = currentPosition + diffPosition * opacity;
2419
+ }
2420
+ function getFadeIndex() {
2421
+ const {
2422
+ dragHandler,
2423
+ index,
2424
+ scrollBody
2425
+ } = emblaApi.internalEngine();
2426
+ const selectedSnap = emblaApi.selectedScrollSnap();
2427
+ if (!dragHandler.pointerDown()) return selectedSnap;
2428
+ const directionSign = Math.sign(scrollBody.velocity());
2429
+ const distanceSign = Math.sign(distanceFromPointerDown);
2430
+ const nextSnap = index.clone().set(selectedSnap).add(directionSign * -1).get();
2431
+ if (!directionSign || !distanceSign) return null;
2432
+ return distanceSign === directionSign ? nextSnap : selectedSnap;
2433
+ }
2434
+ function fade(emblaApi) {
2435
+ const {
2436
+ dragHandler,
2437
+ scrollBody
2438
+ } = emblaApi.internalEngine();
2439
+ const pointerDown = dragHandler.pointerDown();
2440
+ const velocity = scrollBody.velocity();
2441
+ const duration = scrollBody.duration();
2442
+ const fadeIndex = getFadeIndex();
2443
+ const noFadeIndex = !isNumber(fadeIndex);
2444
+ if (pointerDown) {
2445
+ if (!velocity) return;
2446
+ distanceFromPointerDown += velocity;
2447
+ fadeVelocity = Math.abs(velocity / fadeToNextDistance);
2448
+ lockExcessiveScroll(fadeIndex);
2449
+ }
2450
+ if (!pointerDown) {
2451
+ if (!duration || noFadeIndex) return;
2452
+ fadeVelocity += (fullOpacity - opacities[fadeIndex]) / duration;
2453
+ fadeVelocity *= fadeFriction;
2454
+ }
2455
+ if (noFadeIndex) return;
2456
+ setOpacities(fadeIndex, fadeVelocity);
2457
+ }
2458
+ function settled() {
2459
+ const {
2460
+ target,
2461
+ location
2462
+ } = emblaApi.internalEngine();
2463
+ const diffToTarget = target.get() - location.get();
2464
+ const notReachedTarget = Math.abs(diffToTarget) >= 1;
2465
+ const fadeIndex = getFadeIndex();
2466
+ const noFadeIndex = !isNumber(fadeIndex);
2467
+ fade(emblaApi);
2468
+ if (noFadeIndex || notReachedTarget) return false;
2469
+ return opacities[fadeIndex] > 0.999;
2470
+ }
2471
+ function scrollProgress() {
2472
+ return progress;
2473
+ }
2474
+ const self = {
2475
+ name: 'fade',
2476
+ options: userOptions,
2477
+ init,
2478
+ destroy
2479
+ };
2480
+ return self;
2481
+ }
2482
+ Fade.globalOptions = undefined;
2483
+
2484
+ var alignForLayout = function (layout) {
2485
+ return layout === "full-width" ? "center" : "start";
2486
+ };
2487
+ var MOBILE_MEDIA_QUERY$1 = "(max-width: 767px)";
2488
+ /** Slide state classes. CSS uses them to size the full-width focus mode. */
2489
+ var SLIDE_ACTIVE = "lsg-carousel__slide--active";
2490
+ var SLIDE_PREV = "lsg-carousel__slide--prev";
2491
+ var SLIDE_NEXT = "lsg-carousel__slide--next";
2492
+ /** Media track slides. Also the dot/aria-label source of truth. */
2493
+ var MEDIA_ITEM_SELECTOR = "[data-lsg-carousel-item]";
2494
+ /** Text track slides, one per media slide. */
2495
+ var CONTENT_ITEM_SELECTOR = "[data-lsg-carousel-content-item]";
2496
+ /**
2497
+ * A text slide's "head" is its leading run of titles: the pre-title and heading
2498
+ * in Storybook, or the leading LSG Title components in AEM (the same
2499
+ * positional mapping LsgCarousel.scss uses).
2500
+ */
2501
+ var HEAD_SELECTOR = ".lsg-carousel__pre-title, .lsg-carousel__heading, .cmp-lsg-title";
2502
+ /** Marks the last element of each slide's head. */
2503
+ var HEAD_END_ATTR = "data-lsg-carousel-head-end";
2504
+ /** Set on the root once the head/media measurements below are in place. */
2505
+ var MEASURED_ATTR = "data-lsg-carousel-measured";
2506
+ var directChildren = function (container, selector) {
2507
+ return container ? Array.from(container.children).filter(function (el) {
2508
+ return el instanceof HTMLElement && el.matches(selector);
2509
+ }) : [];
2510
+ };
2263
2511
  var LsgCarouselController = /** @class */function () {
2264
2512
  function LsgCarouselController(viewport, elements, options) {
2513
+ var _a;
2265
2514
  if (elements === void 0) {
2266
2515
  elements = {};
2267
2516
  }
@@ -2269,8 +2518,11 @@ var LsgCarouselController = /** @class */function () {
2269
2518
  options = {};
2270
2519
  }
2271
2520
  var _this = this;
2272
- var _a, _b, _c, _d;
2273
- this.embla = null;
2521
+ var _b, _c, _d, _e, _f, _g, _h, _j;
2522
+ this.media = null;
2523
+ this.content = null;
2524
+ this.syncing = false;
2525
+ this.headObserver = null;
2274
2526
  /**
2275
2527
  * Embla automatically falls back to non-looping mode when there is not enough
2276
2528
  * slide content to create a safe loop (most noticeable with two-up layouts and
@@ -2278,82 +2530,337 @@ var LsgCarouselController = /** @class */function () {
2278
2530
  * when there is no native previous snap, jump to the final authored snap.
2279
2531
  */
2280
2532
  this.scrollPrev = function () {
2281
- if (!_this.embla) return;
2282
- if (_this.embla.canScrollPrev()) {
2283
- _this.embla.scrollPrev();
2533
+ if (!_this.media) return;
2534
+ if (_this.media.canScrollPrev()) {
2535
+ _this.media.scrollPrev();
2284
2536
  return;
2285
2537
  }
2286
- var lastSnap = _this.embla.scrollSnapList().length - 1;
2287
- if (lastSnap >= 0) _this.embla.scrollTo(lastSnap);
2538
+ var lastSnap = _this.media.scrollSnapList().length - 1;
2539
+ if (lastSnap >= 0) _this.media.scrollTo(lastSnap);
2288
2540
  };
2289
2541
  /** See `scrollPrev` for why the explicit wrap fallback is required. */
2290
2542
  this.scrollNext = function () {
2291
- if (!_this.embla) return;
2292
- if (_this.embla.canScrollNext()) {
2293
- _this.embla.scrollNext();
2543
+ if (!_this.media) return;
2544
+ if (_this.media.canScrollNext()) {
2545
+ _this.media.scrollNext();
2294
2546
  return;
2295
2547
  }
2296
- if (_this.embla.scrollSnapList().length > 0) _this.embla.scrollTo(0);
2548
+ if (_this.media.scrollSnapList().length > 0) _this.media.scrollTo(0);
2549
+ };
2550
+ /**
2551
+ * The text track never initiates navigation, but it owns its own resize and
2552
+ * mutation observers. If one instance re-measures without the other, this pulls
2553
+ * the media track back onto the text track's index rather than letting them drift.
2554
+ */
2555
+ this.handleContentSelect = function () {
2556
+ if (!_this.media || !_this.content || _this.syncing) return;
2557
+ var target = _this.content.selectedScrollSnap();
2558
+ if (_this.media.selectedScrollSnap() === target) return;
2559
+ _this.syncing = true;
2560
+ try {
2561
+ _this.media.scrollTo(target, true);
2562
+ } finally {
2563
+ _this.syncing = false;
2564
+ }
2565
+ };
2566
+ /** Reader-initiated navigation: repaint state, then crossfade the text. */
2567
+ this.handleMediaSelect = function () {
2568
+ _this.syncSelectedState();
2569
+ _this.pushToContent();
2570
+ };
2571
+ /**
2572
+ * Re-measure events (breakpoint flip, resize, authored items added/removed).
2573
+ * The text is realigned with a hard cut because nothing moved for the reader.
2574
+ */
2575
+ this.handleResync = function () {
2576
+ _this.syncSelectedState();
2577
+ _this.pushToContent(true);
2297
2578
  };
2579
+ /** Repaints dot/arrow/a11y state from the media index. Never navigates. */
2298
2580
  this.syncSelectedState = function () {
2299
- var _a, _b;
2300
- var selected = (_b = (_a = _this.embla) === null || _a === void 0 ? void 0 : _a.selectedScrollSnap()) !== null && _b !== void 0 ? _b : 0;
2581
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2582
+ var selected = (_b = (_a = _this.media) === null || _a === void 0 ? void 0 : _a.selectedScrollSnap()) !== null && _b !== void 0 ? _b : 0;
2583
+ var snapCount = (_d = (_c = _this.media) === null || _c === void 0 ? void 0 : _c.scrollSnapList().length) !== null && _d !== void 0 ? _d : 0;
2301
2584
  _this.paginationBtns.forEach(function (button, index) {
2302
2585
  var active = index === selected;
2303
2586
  button.classList.toggle("lsg-carousel__dot--active", active);
2304
2587
  button.setAttribute("aria-current", active ? "true" : "false");
2305
2588
  });
2589
+ // Cosmetic only. Looping plus the cyclic wrap above mean both arrows always
2590
+ // act, so `disabled`/`aria-disabled` would misreport them to assistive tech.
2591
+ // Themes use these classes to mute the leading/trailing half of the pill.
2592
+ (_e = _this.els.prevBtn) === null || _e === void 0 ? void 0 : _e.classList.toggle("lsg-carousel__arrow--at-start", selected === 0);
2593
+ (_f = _this.els.nextBtn) === null || _f === void 0 ? void 0 : _f.classList.toggle("lsg-carousel__arrow--at-end", snapCount > 0 && selected === snapCount - 1);
2594
+ // Which slide is in focus, and which neighbour sits on each side of it. The
2595
+ // neighbours only wrap when Embla is really looping: with too few slides it
2596
+ // falls back to a plain track (see scrollPrev), where slide 0 has nothing
2597
+ // to its left. `options` is the effective set, after that fallback.
2598
+ var count = _this.mediaSlides.length;
2599
+ var looping = (_h = (_g = _this.media) === null || _g === void 0 ? void 0 : _g.internalEngine().options.loop) !== null && _h !== void 0 ? _h : false;
2600
+ var prev = looping ? (selected - 1 + count) % count : selected - 1;
2601
+ var next = looping ? (selected + 1) % count : selected + 1;
2602
+ _this.mediaSlides.forEach(function (slide, index) {
2603
+ slide.classList.toggle(SLIDE_ACTIVE, index === selected);
2604
+ slide.classList.toggle(SLIDE_PREV, index === prev);
2605
+ slide.classList.toggle(SLIDE_NEXT, index === next);
2606
+ });
2607
+ // The fade plugin only sets opacity and pointer-events, so without this every
2608
+ // faded-out text block keeps its heading in the accessibility tree and its CTA
2609
+ // in the tab order - N copies of the same landmark for a sighted-equivalent
2610
+ // view of one.
2611
+ _this.contentSlides.forEach(function (slide, index) {
2612
+ var active = index === selected;
2613
+ slide.toggleAttribute("inert", !active);
2614
+ if (active) slide.removeAttribute("aria-hidden");else slide.setAttribute("aria-hidden", "true");
2615
+ });
2616
+ };
2617
+ /**
2618
+ * Publishes the geometry a theme needs to place the media column between each
2619
+ * slide's head (pre-title + heading) and its body:
2620
+ *
2621
+ * root --lsg-carousel-head-max tallest head across all slides
2622
+ * --lsg-carousel-media-block media column height (cards + dots)
2623
+ * slide --lsg-carousel-head-height this slide's own head height
2624
+ *
2625
+ * Every slide pads its head up to the tallest one, so the media never moves
2626
+ * as the text crossfades between headings of different lengths. Themes that
2627
+ * do not opt in ignore these. MEASURED_ATTR is only set when at least one
2628
+ * slide has a head, so a carousel without titles keeps the plain stack.
2629
+ */
2630
+ this.measureSplit = function () {
2631
+ if (!_this.root || !_this.mediaColumn) return;
2632
+ var max = 0;
2633
+ _this.contentSlides.forEach(function (slide, index) {
2634
+ var end = _this.headEnds[index];
2635
+ // Rect deltas, not offsetTop: the slide is not an offset parent. The fade
2636
+ // plugin only translates on X, so the vertical delta is unaffected.
2637
+ var height = end ? Math.ceil(end.getBoundingClientRect().bottom - slide.getBoundingClientRect().top) : 0;
2638
+ slide.style.setProperty("--lsg-carousel-head-height", "".concat(height, "px"));
2639
+ max = Math.max(max, height);
2640
+ });
2641
+ _this.root.style.setProperty("--lsg-carousel-head-max", "".concat(max, "px"));
2642
+ _this.root.style.setProperty("--lsg-carousel-media-block", "".concat(Math.ceil(_this.mediaColumn.getBoundingClientRect().height), "px"));
2643
+ _this.root.toggleAttribute(MEASURED_ATTR, max > 0);
2306
2644
  };
2307
2645
  this.els = elements;
2646
+ this.mediaViewport = viewport;
2308
2647
  this.paginationBtns = elements.paginationBtns ? Array.from(elements.paginationBtns) : [];
2309
- this.embla = EmblaCarousel(viewport, {
2310
- align: "start",
2648
+ var mediaTrack = viewport.firstElementChild;
2649
+ var mediaSlides = directChildren(mediaTrack, MEDIA_ITEM_SELECTOR);
2650
+ this.mediaSlides = mediaSlides;
2651
+ var align = (_b = options.align) !== null && _b !== void 0 ? _b : "start";
2652
+ // Embla resolves `container` as `root.children[0]` and `slides` as its
2653
+ // element children. Passing both explicitly instead makes the instance
2654
+ // immune to AEM injecting its own nodes (e.g. the edit-mode
2655
+ // `div.new.section.aem-Grid-newComponent` drop target) into the track.
2656
+ this.media = EmblaCarousel(viewport, {
2657
+ // `center` loops natively from three items as long as a slide is at least
2658
+ // half the viewport wide: Embla's canLoop() needs the OTHER slides to
2659
+ // cover the viewport. LsgCarousel.scss sizes the full-width slides to
2660
+ // --lsg-carousel-center-basis (50%) for exactly this reason.
2661
+ align: align,
2311
2662
  // Every authored media item owns a pagination dot. `trimSnaps` can remove
2312
2663
  // the final snap when two-up/peek layouts do not have enough overflow,
2313
2664
  // leaving the last authored item only partially reachable. Keep all snaps
2314
2665
  // so dot/item indexes stay 1:1 for 2, 3, 4, ... media items.
2666
+ //
2667
+ // This is ALSO load-bearing for the text/media sync: `containSnaps` is
2668
+ // `!loop && !!containScroll`, so with this `false` the snap list is
2669
+ // always `snapsAligned` - one snap per slide, whatever the slide's
2670
+ // flex-basis. That is the only reason a 380px peek track and a 100%-wide
2671
+ // crossfade track can share one index. Reverting this to `trimSnaps`
2672
+ // drops trailing snaps on the media track alone and desyncs the tail.
2315
2673
  containScroll: false,
2316
- direction: (_a = options.direction) !== null && _a !== void 0 ? _a : "ltr",
2317
- loop: (_b = options.loop) !== null && _b !== void 0 ? _b : true,
2674
+ direction: (_c = options.direction) !== null && _c !== void 0 ? _c : "ltr",
2675
+ loop: (_d = options.loop) !== null && _d !== void 0 ? _d : true,
2318
2676
  skipSnaps: false,
2319
- slidesToScroll: 1
2677
+ slidesToScroll: 1,
2678
+ container: mediaTrack !== null && mediaTrack !== void 0 ? mediaTrack : undefined,
2679
+ slides: mediaSlides.length ? mediaSlides : undefined,
2680
+ // All layouts collapse to the same mobile stack, which is never
2681
+ // mirrored and never centred. Embla restores the selected index when a
2682
+ // breakpoint re-activates, so the flip is not visible to the reader.
2683
+ breakpoints: (_a = {}, _a[MOBILE_MEDIA_QUERY$1] = {
2684
+ direction: "ltr",
2685
+ align: "start"
2686
+ }, _a)
2687
+ });
2688
+ var contentViewport = (_e = elements.contentViewport) !== null && _e !== void 0 ? _e : null;
2689
+ var contentTrack = contentViewport === null || contentViewport === void 0 ? void 0 : contentViewport.firstElementChild;
2690
+ this.contentSlides = directChildren(contentTrack, CONTENT_ITEM_SELECTOR);
2691
+ // Unequal counts are the one remaining way the two tracks can desync, and a
2692
+ // text block silently paired with the wrong image is worse than no text
2693
+ // animation at all. Degrade to media-only rather than risk it.
2694
+ var countsMatch = mediaSlides.length > 0 && this.contentSlides.length === mediaSlides.length;
2695
+ if (contentViewport && contentTrack && countsMatch) {
2696
+ this.content = EmblaCarousel(contentViewport, {
2697
+ align: "start",
2698
+ // Must match the media track's snap arithmetic - see above.
2699
+ containScroll: false,
2700
+ // The text crossfades in place, so there is nothing to mirror.
2701
+ direction: "ltr",
2702
+ // Inert on a fade instance: the plugin calls `disableScroll()`, which
2703
+ // deactivates the translate and every loop point. Set for config
2704
+ // symmetry with the media track, not for behaviour.
2705
+ loop: (_f = options.loop) !== null && _f !== void 0 ? _f : true,
2706
+ skipSnaps: false,
2707
+ slidesToScroll: 1,
2708
+ // The reader drags the media, never the text. Without this the text
2709
+ // track would fight the media track for the same pointer.
2710
+ watchDrag: false,
2711
+ container: contentTrack,
2712
+ slides: this.contentSlides
2713
+ }, [Fade()]);
2714
+ }
2715
+ // Head/media measurements for themes that split the text around the media
2716
+ // on mobile (theme 3). Only paired tracks qualify: the split lays one track
2717
+ // inside the other, which only makes sense when they show the same item.
2718
+ this.root = viewport.closest(".lsg-carousel");
2719
+ this.mediaColumn = viewport.closest(".lsg-carousel__media-column");
2720
+ var heads = [];
2721
+ this.headEnds = this.contentSlides.map(function (slide) {
2722
+ var end = null;
2723
+ for (var _i = 0, _a = Array.from(slide.children); _i < _a.length; _i++) {
2724
+ var child = _a[_i];
2725
+ if (!(child instanceof HTMLElement) || !child.matches(HEAD_SELECTOR)) break;
2726
+ heads.push(child);
2727
+ end = child;
2728
+ }
2729
+ end === null || end === void 0 ? void 0 : end.setAttribute(HEAD_END_ATTR, "");
2730
+ return end;
2320
2731
  });
2732
+ if (this.content && this.root && this.mediaColumn && typeof ResizeObserver !== "undefined") {
2733
+ // Heads re-wrap with the viewport width and again when web fonts land; the
2734
+ // media column follows the breakpoint's card height. Neither box's size
2735
+ // depends on the variables written below, so this cannot feed back.
2736
+ this.headObserver = new ResizeObserver(this.measureSplit);
2737
+ this.headObserver.observe(this.mediaColumn);
2738
+ heads.forEach(function (head) {
2739
+ var _a;
2740
+ return (_a = _this.headObserver) === null || _a === void 0 ? void 0 : _a.observe(head);
2741
+ });
2742
+ this.measureSplit();
2743
+ }
2321
2744
  this.paginationHandlers = this.paginationBtns.map(function (_, index) {
2322
2745
  return function () {
2323
2746
  var _a;
2324
- (_a = _this.embla) === null || _a === void 0 ? void 0 : _a.scrollTo(index);
2747
+ (_a = _this.media) === null || _a === void 0 ? void 0 : _a.scrollTo(index);
2325
2748
  };
2326
2749
  });
2327
- (_c = this.els.prevBtn) === null || _c === void 0 ? void 0 : _c.addEventListener("click", this.scrollPrev);
2328
- (_d = this.els.nextBtn) === null || _d === void 0 ? void 0 : _d.addEventListener("click", this.scrollNext);
2750
+ // Focus mode: clicking a peeking neighbour centres it instead of reaching the
2751
+ // media inside, so a half-hidden video cannot start playing off-centre.
2752
+ // Capture phase, and Embla's own capture listener on the viewport runs first
2753
+ // and swallows the click that ends a drag, so only real clicks get here.
2754
+ this.slideClickHandlers = align === "center" ? mediaSlides.map(function (_, index) {
2755
+ return function (event) {
2756
+ if (!_this.media || _this.media.selectedScrollSnap() === index) return;
2757
+ event.preventDefault();
2758
+ event.stopPropagation();
2759
+ _this.media.scrollTo(index);
2760
+ };
2761
+ }) : [];
2762
+ (_g = this.els.prevBtn) === null || _g === void 0 ? void 0 : _g.addEventListener("click", this.scrollPrev);
2763
+ (_h = this.els.nextBtn) === null || _h === void 0 ? void 0 : _h.addEventListener("click", this.scrollNext);
2329
2764
  this.paginationBtns.forEach(function (button, index) {
2330
2765
  button.addEventListener("click", _this.paginationHandlers[index]);
2331
2766
  });
2332
- this.embla.on("select", this.syncSelectedState);
2333
- this.embla.on("reInit", this.syncSelectedState);
2334
- this.syncSelectedState();
2767
+ this.slideClickHandlers.forEach(function (handler, index) {
2768
+ mediaSlides[index].addEventListener("click", handler, true);
2769
+ });
2770
+ this.media.on("select", this.handleMediaSelect);
2771
+ this.media.on("reInit", this.handleResync);
2772
+ this.media.on("slidesChanged", this.handleResync);
2773
+ (_j = this.content) === null || _j === void 0 ? void 0 : _j.on("select", this.handleContentSelect);
2774
+ // Initial alignment is a hard cut - there is nothing to crossfade from.
2775
+ this.handleResync();
2335
2776
  }
2777
+ /**
2778
+ * Drive the text track from the media index.
2779
+ *
2780
+ * `jump` skips the crossfade (Embla sets duration 0, and the fade plugin then
2781
+ * snaps opacity instantly). Use it only for re-measure events, never for
2782
+ * reader-initiated navigation, or the text hard-cuts instead of fading.
2783
+ */
2784
+ LsgCarouselController.prototype.pushToContent = function (jump) {
2785
+ if (jump === void 0) {
2786
+ jump = false;
2787
+ }
2788
+ if (!this.media || !this.content || this.syncing) return;
2789
+ var target = this.media.selectedScrollSnap();
2790
+ if (this.content.selectedScrollSnap() === target) return;
2791
+ this.syncing = true;
2792
+ try {
2793
+ this.content.scrollTo(target, jump);
2794
+ } finally {
2795
+ this.syncing = false;
2796
+ }
2797
+ };
2336
2798
  Object.defineProperty(LsgCarouselController.prototype, "emblaApi", {
2337
2799
  get: function () {
2338
- return this.embla;
2800
+ return this.media;
2801
+ },
2802
+ enumerable: false,
2803
+ configurable: true
2804
+ });
2805
+ Object.defineProperty(LsgCarouselController.prototype, "contentApi", {
2806
+ /** Text track handle. Exposed for the index-parity checks in the test plan. */
2807
+ get: function () {
2808
+ return this.content;
2339
2809
  },
2340
2810
  enumerable: false,
2341
2811
  configurable: true
2342
2812
  });
2343
2813
  LsgCarouselController.prototype.destroy = function () {
2344
2814
  var _this = this;
2345
- var _a, _b;
2815
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2346
2816
  (_a = this.els.prevBtn) === null || _a === void 0 ? void 0 : _a.removeEventListener("click", this.scrollPrev);
2347
2817
  (_b = this.els.nextBtn) === null || _b === void 0 ? void 0 : _b.removeEventListener("click", this.scrollNext);
2348
2818
  this.paginationBtns.forEach(function (button, index) {
2349
2819
  button.removeEventListener("click", _this.paginationHandlers[index]);
2350
2820
  });
2351
- if (this.embla) {
2352
- this.embla.off("select", this.syncSelectedState);
2353
- this.embla.off("reInit", this.syncSelectedState);
2354
- this.embla.destroy();
2355
- this.embla = null;
2821
+ this.slideClickHandlers.forEach(function (handler, index) {
2822
+ _this.mediaSlides[index].removeEventListener("click", handler, true);
2823
+ });
2824
+ // Content first: its plugin restores the inline opacity/transform it wrote,
2825
+ // and it reads `internalEngine()` while doing so.
2826
+ if (this.content) {
2827
+ this.content.off("select", this.handleContentSelect);
2828
+ this.content.destroy();
2829
+ this.content = null;
2830
+ }
2831
+ if (this.media) {
2832
+ this.media.off("select", this.handleMediaSelect);
2833
+ this.media.off("reInit", this.handleResync);
2834
+ this.media.off("slidesChanged", this.handleResync);
2835
+ this.media.destroy();
2836
+ this.media = null;
2356
2837
  }
2838
+ // Embla restores what Embla wrote. Everything below is ours, and leaving it
2839
+ // behind would make server-rendered markup re-initialise into a stale state.
2840
+ this.paginationBtns.forEach(function (button) {
2841
+ button.classList.remove("lsg-carousel__dot--active");
2842
+ button.removeAttribute("aria-current");
2843
+ });
2844
+ (_c = this.els.prevBtn) === null || _c === void 0 ? void 0 : _c.classList.remove("lsg-carousel__arrow--at-start");
2845
+ (_d = this.els.nextBtn) === null || _d === void 0 ? void 0 : _d.classList.remove("lsg-carousel__arrow--at-end");
2846
+ // SLIDE_ACTIVE stays off too: the server/React markup puts it on slide 0,
2847
+ // so re-initialising adds it back there.
2848
+ this.mediaSlides.forEach(function (slide) {
2849
+ slide.classList.remove(SLIDE_ACTIVE, SLIDE_PREV, SLIDE_NEXT);
2850
+ });
2851
+ this.contentSlides.forEach(function (slide) {
2852
+ slide.removeAttribute("inert");
2853
+ slide.removeAttribute("aria-hidden");
2854
+ slide.style.removeProperty("--lsg-carousel-head-height");
2855
+ });
2856
+ (_e = this.headObserver) === null || _e === void 0 ? void 0 : _e.disconnect();
2857
+ this.headObserver = null;
2858
+ this.headEnds.forEach(function (end) {
2859
+ return end === null || end === void 0 ? void 0 : end.removeAttribute(HEAD_END_ATTR);
2860
+ });
2861
+ (_f = this.root) === null || _f === void 0 ? void 0 : _f.style.removeProperty("--lsg-carousel-head-max");
2862
+ (_g = this.root) === null || _g === void 0 ? void 0 : _g.style.removeProperty("--lsg-carousel-media-block");
2863
+ (_h = this.root) === null || _h === void 0 ? void 0 : _h.removeAttribute(MEASURED_ATTR);
2357
2864
  };
2358
2865
  return LsgCarouselController;
2359
2866
  }();
@@ -2365,24 +2872,28 @@ var mediaTypeFor = function (item) {
2365
2872
  var directionForLayout = function (layout) {
2366
2873
  return layout === "media-left" ? "rtl" : "ltr";
2367
2874
  };
2875
+ var hasText = function (item) {
2876
+ return Boolean(item.preTitle || item.heading || item.body || item.cta);
2877
+ };
2368
2878
  var LsgCarousel = function (_a) {
2369
- var _b;
2370
- var heading = _a.heading,
2879
+ var _b = _a.items,
2880
+ items = _b === void 0 ? [] : _b,
2881
+ _c = _a.layout,
2882
+ layout = _c === void 0 ? "media-right" : _c,
2883
+ background = _a.background,
2884
+ accent = _a.accent,
2885
+ _d = _a.headingTag,
2886
+ headingTag = _d === void 0 ? "h2" : _d,
2887
+ _e = _a.ariaLabel,
2888
+ ariaLabel = _e === void 0 ? "Media carousel" : _e,
2889
+ _f = _a.className,
2890
+ className = _f === void 0 ? "" : _f,
2891
+ heading = _a.heading,
2371
2892
  preTitle = _a.preTitle,
2372
2893
  body = _a.body,
2373
- cta = _a.cta,
2374
- _c = _a.items,
2375
- items = _c === void 0 ? [] : _c,
2376
- _d = _a.layout,
2377
- layout = _d === void 0 ? "media-right" : _d,
2378
- _e = _a.headingTag,
2379
- headingTag = _e === void 0 ? "h2" : _e,
2380
- _f = _a.ariaLabel,
2381
- ariaLabel = _f === void 0 ? "Media carousel" : _f,
2382
- _g = _a.className,
2383
- className = _g === void 0 ? "" : _g;
2384
- var rootRef = useRef(null);
2894
+ cta = _a.cta;
2385
2895
  var viewportRef = useRef(null);
2896
+ var contentViewportRef = useRef(null);
2386
2897
  var prevBtnRef = useRef(null);
2387
2898
  var nextBtnRef = useRef(null);
2388
2899
  var dotsRef = useRef(null);
@@ -2390,71 +2901,109 @@ var LsgCarousel = function (_a) {
2390
2901
  var HeadingTag = headingTag;
2391
2902
  var isCarousel = items.length > 1;
2392
2903
  var carouselDirection = directionForLayout(layout);
2904
+ var carouselAlign = alignForLayout(layout);
2905
+ // Legacy single-content-block callers passed the text alongside `items`. Fold
2906
+ // it onto the first item so their carousel still renders its copy.
2907
+ var resolvedItems = useMemo(function () {
2908
+ var legacy = {
2909
+ preTitle: preTitle,
2910
+ heading: heading,
2911
+ body: body,
2912
+ cta: cta
2913
+ };
2914
+ var hasLegacy = Boolean(legacy.preTitle || legacy.heading || legacy.body || legacy.cta);
2915
+ if (!hasLegacy || !items.length || hasText(items[0])) return items;
2916
+ return items.map(function (item, index) {
2917
+ return index === 0 ? __assign(__assign({}, legacy), item) : item;
2918
+ });
2919
+ }, [items, preTitle, heading, body, cta]);
2393
2920
  useEffect(function () {
2394
2921
  var _a, _b;
2395
2922
  if (!viewportRef.current || !isCarousel) return;
2396
2923
  controllerRef.current = new LsgCarouselController(viewportRef.current, {
2397
2924
  prevBtn: prevBtnRef.current,
2398
2925
  nextBtn: nextBtnRef.current,
2399
- paginationBtns: (_b = (_a = dotsRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll("[data-lsg-carousel-dot]")) !== null && _b !== void 0 ? _b : []
2926
+ paginationBtns: (_b = (_a = dotsRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll("[data-lsg-carousel-dot]")) !== null && _b !== void 0 ? _b : [],
2927
+ contentViewport: contentViewportRef.current
2400
2928
  }, {
2401
2929
  loop: true,
2402
- direction: carouselDirection
2930
+ direction: carouselDirection,
2931
+ align: carouselAlign
2403
2932
  });
2404
2933
  return function () {
2405
2934
  var _a;
2406
2935
  (_a = controllerRef.current) === null || _a === void 0 ? void 0 : _a.destroy();
2407
2936
  controllerRef.current = null;
2408
2937
  };
2409
- }, [carouselDirection, isCarousel, items.length]);
2410
- var rootClasses = ["lsg-carousel", "lsg-carousel--".concat(layout), className].filter(Boolean).join(" ");
2938
+ }, [carouselAlign, carouselDirection, isCarousel, resolvedItems.length]);
2939
+ var rootClasses = ["lsg-carousel", "lsg-carousel--".concat(layout),
2940
+ // In AEM this class arrives on the decoration div instead; the SCSS only
2941
+ // sets inherited custom properties, so either position works.
2942
+ background ? "lsg-carousel--bg-".concat(background) : "", accent ? "lsg-carousel--accent-".concat(accent) : "", className].filter(Boolean).join(" ");
2943
+ var keyFor = function (item, index) {
2944
+ var _a, _b, _c;
2945
+ return (_a = item.id) !== null && _a !== void 0 ? _a : "".concat(mediaTypeFor(item), "-").concat((_c = (_b = item.imageUrl) !== null && _b !== void 0 ? _b : item.videoUrl) !== null && _c !== void 0 ? _c : index, "-").concat(index);
2946
+ };
2411
2947
  return /*#__PURE__*/React.createElement("section", {
2412
- ref: rootRef,
2413
2948
  className: rootClasses,
2414
2949
  "data-lsg-is": "carousel",
2415
2950
  "data-lsg-carousel-layout": layout,
2416
2951
  "data-lsg-carousel-direction": carouselDirection,
2417
- "data-lsg-carousel-count": items.length,
2952
+ "data-lsg-carousel-count": resolvedItems.length,
2418
2953
  "aria-label": ariaLabel
2419
2954
  }, /*#__PURE__*/React.createElement("div", {
2420
2955
  className: "lsg-carousel__shell"
2421
2956
  }, /*#__PURE__*/React.createElement("div", {
2422
- className: "lsg-carousel__content"
2423
- }, preTitle && /*#__PURE__*/React.createElement("div", {
2424
- className: "lsg-carousel__pre-title"
2425
- }, /*#__PURE__*/React.createElement("span", {
2426
- className: "lsg-carousel__pre-title-marker",
2427
- "aria-hidden": "true"
2428
- }), /*#__PURE__*/React.createElement("span", null, preTitle)), /*#__PURE__*/React.createElement(HeadingTag, {
2429
- className: "lsg-carousel__heading"
2430
- }, heading), body && /*#__PURE__*/React.createElement("p", {
2431
- className: "lsg-carousel__body"
2432
- }, body), cta && /*#__PURE__*/React.createElement("div", {
2433
- className: "lsg-carousel__actions"
2434
- }, /*#__PURE__*/React.createElement(LsgButton, {
2435
- className: "lsg-carousel__cta",
2436
- href: cta.href,
2437
- external: cta.external,
2438
- variant: (_b = cta.variant) !== null && _b !== void 0 ? _b : "primary",
2439
- onClick: cta.onClick
2440
- }, cta.label))), /*#__PURE__*/React.createElement("div", {
2957
+ className: "lsg-carousel__content",
2958
+ ref: contentViewportRef,
2959
+ "data-lsg-carousel-content-viewport": true
2960
+ }, /*#__PURE__*/React.createElement("div", {
2961
+ className: "lsg-carousel__content-track"
2962
+ }, resolvedItems.map(function (item, index) {
2963
+ var _a;
2964
+ return /*#__PURE__*/React.createElement("div", {
2965
+ key: keyFor(item, index),
2966
+ className: "lsg-carousel__content-slide",
2967
+ "data-lsg-carousel-content-item": true
2968
+ }, item.preTitle && /*#__PURE__*/React.createElement("div", {
2969
+ className: "lsg-carousel__pre-title"
2970
+ }, /*#__PURE__*/React.createElement("span", {
2971
+ className: "lsg-carousel__pre-title-marker",
2972
+ "aria-hidden": "true"
2973
+ }), /*#__PURE__*/React.createElement("span", null, item.preTitle)), item.heading && /*#__PURE__*/React.createElement(HeadingTag, {
2974
+ className: "lsg-carousel__heading"
2975
+ }, item.heading), item.body && /*#__PURE__*/React.createElement("p", {
2976
+ className: "lsg-carousel__body"
2977
+ }, item.body), item.cta && /*#__PURE__*/React.createElement("div", {
2978
+ className: "lsg-carousel__actions"
2979
+ }, /*#__PURE__*/React.createElement(LsgButton, {
2980
+ className: "lsg-carousel__cta",
2981
+ href: item.cta.href,
2982
+ external: item.cta.external,
2983
+ variant: (_a = item.cta.variant) !== null && _a !== void 0 ? _a : "primary",
2984
+ onClick: item.cta.onClick
2985
+ }, item.cta.label)));
2986
+ }))), /*#__PURE__*/React.createElement("div", {
2441
2987
  className: "lsg-carousel__media-column"
2442
2988
  }, /*#__PURE__*/React.createElement("div", {
2443
2989
  className: "lsg-carousel__viewport",
2444
2990
  ref: viewportRef,
2445
- dir: carouselDirection
2991
+ dir: carouselDirection,
2992
+ "data-lsg-carousel-media-viewport": true
2446
2993
  }, /*#__PURE__*/React.createElement("div", {
2447
2994
  className: "lsg-carousel__track",
2448
2995
  role: "list"
2449
- }, items.map(function (item, index) {
2450
- var _a, _b, _c, _d, _e, _f, _g;
2996
+ }, resolvedItems.map(function (item, index) {
2997
+ var _a, _b, _c, _d;
2451
2998
  var type = mediaTypeFor(item);
2452
- var key = (_a = item.id) !== null && _a !== void 0 ? _a : "".concat(type, "-").concat((_c = (_b = item.imageUrl) !== null && _b !== void 0 ? _b : item.videoUrl) !== null && _c !== void 0 ? _c : index, "-").concat(index);
2453
2999
  return /*#__PURE__*/React.createElement("article", {
2454
- key: key,
2455
- className: "lsg-carousel__slide",
3000
+ key: keyFor(item, index)
3001
+ // Slide 0 starts active, as the first dot does, so the
3002
+ // full-width focus mode is not all-shrunk before JS runs.
3003
+ ,
3004
+ className: "lsg-carousel__slide".concat(index === 0 ? " lsg-carousel__slide--active" : ""),
2456
3005
  role: "listitem",
2457
- "aria-label": "Slide ".concat(index + 1, " of ").concat(items.length),
3006
+ "aria-label": "Slide ".concat(index + 1, " of ").concat(resolvedItems.length),
2458
3007
  "data-lsg-carousel-item": true
2459
3008
  }, /*#__PURE__*/React.createElement("div", {
2460
3009
  className: "lsg-carousel__media"
@@ -2462,10 +3011,10 @@ var LsgCarousel = function (_a) {
2462
3011
  videoUrl: item.videoUrl,
2463
3012
  imageUrl: item.videoPoster,
2464
3013
  imageAlt: item.imageAlt,
2465
- controls: (_d = item.videoControls) !== null && _d !== void 0 ? _d : true,
2466
- autoplay: (_e = item.videoAutoPlay) !== null && _e !== void 0 ? _e : false,
2467
- muted: (_f = item.videoMuted) !== null && _f !== void 0 ? _f : true,
2468
- loop: (_g = item.videoLoop) !== null && _g !== void 0 ? _g : false,
3014
+ controls: (_a = item.videoControls) !== null && _a !== void 0 ? _a : true,
3015
+ autoplay: (_b = item.videoAutoPlay) !== null && _b !== void 0 ? _b : false,
3016
+ muted: (_c = item.videoMuted) !== null && _c !== void 0 ? _c : true,
3017
+ loop: (_d = item.videoLoop) !== null && _d !== void 0 ? _d : false,
2469
3018
  displayMode: "content",
2470
3019
  showTags: false,
2471
3020
  className: "lsg-carousel__video-component"
@@ -2480,8 +3029,10 @@ var LsgCarousel = function (_a) {
2480
3029
  className: "lsg-carousel__media-label"
2481
3030
  }, item.label)));
2482
3031
  }))), isCarousel && /*#__PURE__*/React.createElement("div", {
2483
- className: "lsg-carousel__navigation",
3032
+ className: "lsg-carousel__controls",
2484
3033
  "aria-label": "Carousel navigation"
3034
+ }, /*#__PURE__*/React.createElement("div", {
3035
+ className: "lsg-carousel__nav"
2485
3036
  }, /*#__PURE__*/React.createElement("button", {
2486
3037
  ref: prevBtnRef,
2487
3038
  className: "lsg-carousel__arrow lsg-carousel__arrow--prev",
@@ -2491,10 +3042,19 @@ var LsgCarousel = function (_a) {
2491
3042
  }, /*#__PURE__*/React.createElement("span", {
2492
3043
  className: "lsg-carousel__arrow-icon",
2493
3044
  "aria-hidden": "true"
2494
- })), /*#__PURE__*/React.createElement("div", {
3045
+ })), /*#__PURE__*/React.createElement("button", {
3046
+ ref: nextBtnRef,
3047
+ className: "lsg-carousel__arrow lsg-carousel__arrow--next",
3048
+ type: "button",
3049
+ "aria-label": "Next slide",
3050
+ "data-lsg-carousel-next": true
3051
+ }, /*#__PURE__*/React.createElement("span", {
3052
+ className: "lsg-carousel__arrow-icon",
3053
+ "aria-hidden": "true"
3054
+ }))), /*#__PURE__*/React.createElement("div", {
2495
3055
  className: "lsg-carousel__pagination",
2496
3056
  ref: dotsRef
2497
- }, items.map(function (item, index) {
3057
+ }, resolvedItems.map(function (item, index) {
2498
3058
  var _a;
2499
3059
  return /*#__PURE__*/React.createElement("button", {
2500
3060
  key: (_a = item.id) !== null && _a !== void 0 ? _a : "dot-".concat(index),
@@ -2504,15 +3064,6 @@ var LsgCarousel = function (_a) {
2504
3064
  "aria-current": index === 0 ? "true" : "false",
2505
3065
  "data-lsg-carousel-dot": true
2506
3066
  });
2507
- })), /*#__PURE__*/React.createElement("button", {
2508
- ref: nextBtnRef,
2509
- className: "lsg-carousel__arrow lsg-carousel__arrow--next",
2510
- type: "button",
2511
- "aria-label": "Next slide",
2512
- "data-lsg-carousel-next": true
2513
- }, /*#__PURE__*/React.createElement("span", {
2514
- className: "lsg-carousel__arrow-icon",
2515
- "aria-hidden": "true"
2516
3067
  }))))));
2517
3068
  };
2518
3069
 
@@ -4225,227 +4776,6 @@ var LsgFooter = function (_a) {
4225
4776
  }))))), bottomTopBar, bottomBar);
4226
4777
  };
4227
4778
 
4228
- function clampNumber(number, min, max) {
4229
- return Math.min(Math.max(number, min), max);
4230
- }
4231
- function isNumber(value) {
4232
- return typeof value === 'number' && !isNaN(value);
4233
- }
4234
- function Fade(userOptions = {}) {
4235
- const fullOpacity = 1;
4236
- const noOpacity = 0;
4237
- const fadeFriction = 0.68;
4238
- let emblaApi;
4239
- let opacities = [];
4240
- let fadeToNextDistance;
4241
- let distanceFromPointerDown = 0;
4242
- let fadeVelocity = 0;
4243
- let progress = 0;
4244
- let shouldFadePair = false;
4245
- let defaultSettledBehaviour;
4246
- let defaultProgressBehaviour;
4247
- function init(emblaApiInstance) {
4248
- emblaApi = emblaApiInstance;
4249
- const selectedSnap = emblaApi.selectedScrollSnap();
4250
- const {
4251
- scrollBody,
4252
- containerRect,
4253
- axis
4254
- } = emblaApi.internalEngine();
4255
- const containerSize = axis.measureSize(containerRect);
4256
- fadeToNextDistance = clampNumber(containerSize * 0.75, 200, 500);
4257
- shouldFadePair = false;
4258
- opacities = emblaApi.scrollSnapList().map((_, index) => index === selectedSnap ? fullOpacity : noOpacity);
4259
- defaultSettledBehaviour = scrollBody.settled;
4260
- defaultProgressBehaviour = emblaApi.scrollProgress;
4261
- scrollBody.settled = settled;
4262
- emblaApi.scrollProgress = scrollProgress;
4263
- emblaApi.on('select', select).on('slideFocus', fadeToSelectedSnapInstantly).on('pointerDown', pointerDown).on('pointerUp', pointerUp);
4264
- disableScroll();
4265
- fadeToSelectedSnapInstantly();
4266
- }
4267
- function destroy() {
4268
- const {
4269
- scrollBody
4270
- } = emblaApi.internalEngine();
4271
- scrollBody.settled = defaultSettledBehaviour;
4272
- emblaApi.scrollProgress = defaultProgressBehaviour;
4273
- emblaApi.off('select', select).off('slideFocus', fadeToSelectedSnapInstantly).off('pointerDown', pointerDown).off('pointerUp', pointerUp);
4274
- emblaApi.slideNodes().forEach(slideNode => {
4275
- const slideStyle = slideNode.style;
4276
- slideStyle.opacity = '';
4277
- slideStyle.transform = '';
4278
- slideStyle.pointerEvents = '';
4279
- if (!slideNode.getAttribute('style')) slideNode.removeAttribute('style');
4280
- });
4281
- }
4282
- function fadeToSelectedSnapInstantly() {
4283
- const selectedSnap = emblaApi.selectedScrollSnap();
4284
- setOpacities(selectedSnap, fullOpacity);
4285
- }
4286
- function pointerUp() {
4287
- shouldFadePair = false;
4288
- }
4289
- function pointerDown() {
4290
- shouldFadePair = false;
4291
- distanceFromPointerDown = 0;
4292
- fadeVelocity = 0;
4293
- }
4294
- function select() {
4295
- const duration = emblaApi.internalEngine().scrollBody.duration();
4296
- fadeVelocity = duration ? 0 : fullOpacity;
4297
- shouldFadePair = true;
4298
- if (!duration) fadeToSelectedSnapInstantly();
4299
- }
4300
- function getSlideTransform(position) {
4301
- const {
4302
- axis
4303
- } = emblaApi.internalEngine();
4304
- const translateAxis = axis.scroll.toUpperCase();
4305
- return `translate${translateAxis}(${axis.direction(position)}px)`;
4306
- }
4307
- function disableScroll() {
4308
- const {
4309
- translate,
4310
- slideLooper
4311
- } = emblaApi.internalEngine();
4312
- translate.clear();
4313
- translate.toggleActive(false);
4314
- slideLooper.loopPoints.forEach(({
4315
- translate
4316
- }) => {
4317
- translate.clear();
4318
- translate.toggleActive(false);
4319
- });
4320
- }
4321
- function lockExcessiveScroll(fadeIndex) {
4322
- const {
4323
- scrollSnaps,
4324
- location,
4325
- target
4326
- } = emblaApi.internalEngine();
4327
- if (!isNumber(fadeIndex) || opacities[fadeIndex] < 0.5) return;
4328
- location.set(scrollSnaps[fadeIndex]);
4329
- target.set(location);
4330
- }
4331
- function setOpacities(fadeIndex, velocity) {
4332
- const scrollSnaps = emblaApi.scrollSnapList();
4333
- scrollSnaps.forEach((_, indexA) => {
4334
- const absVelocity = Math.abs(velocity);
4335
- const currentOpacity = opacities[indexA];
4336
- const isFadeIndex = indexA === fadeIndex;
4337
- const nextOpacity = isFadeIndex ? currentOpacity + absVelocity : currentOpacity - absVelocity;
4338
- const clampedOpacity = clampNumber(nextOpacity, noOpacity, fullOpacity);
4339
- opacities[indexA] = clampedOpacity;
4340
- const fadePair = isFadeIndex && shouldFadePair;
4341
- const indexB = emblaApi.previousScrollSnap();
4342
- if (fadePair) opacities[indexB] = 1 - clampedOpacity;
4343
- if (isFadeIndex) setProgress(fadeIndex, clampedOpacity);
4344
- setOpacity(indexA);
4345
- });
4346
- }
4347
- function setOpacity(index) {
4348
- const slidesInSnap = emblaApi.internalEngine().slideRegistry[index];
4349
- const {
4350
- scrollSnaps,
4351
- containerRect
4352
- } = emblaApi.internalEngine();
4353
- const opacity = opacities[index];
4354
- slidesInSnap.forEach(slideIndex => {
4355
- const slideStyle = emblaApi.slideNodes()[slideIndex].style;
4356
- const roundedOpacity = parseFloat(opacity.toFixed(2));
4357
- const hasOpacity = roundedOpacity > noOpacity;
4358
- const position = hasOpacity ? scrollSnaps[index] : containerRect.width + 2;
4359
- const transform = getSlideTransform(position);
4360
- if (hasOpacity) slideStyle.transform = transform;
4361
- slideStyle.opacity = roundedOpacity.toString();
4362
- slideStyle.pointerEvents = opacity > 0.5 ? 'auto' : 'none';
4363
- if (!hasOpacity) slideStyle.transform = transform;
4364
- });
4365
- }
4366
- function setProgress(fadeIndex, opacity) {
4367
- const {
4368
- index,
4369
- dragHandler,
4370
- scrollSnaps
4371
- } = emblaApi.internalEngine();
4372
- const pointerDown = dragHandler.pointerDown();
4373
- const snapFraction = 1 / (scrollSnaps.length - 1);
4374
- let indexA = fadeIndex;
4375
- let indexB = pointerDown ? emblaApi.selectedScrollSnap() : emblaApi.previousScrollSnap();
4376
- if (pointerDown && indexA === indexB) {
4377
- const reverseSign = Math.sign(distanceFromPointerDown) * -1;
4378
- indexA = indexB;
4379
- indexB = index.clone().set(indexB).add(reverseSign).get();
4380
- }
4381
- const currentPosition = indexB * snapFraction;
4382
- const diffPosition = (indexA - indexB) * snapFraction;
4383
- progress = currentPosition + diffPosition * opacity;
4384
- }
4385
- function getFadeIndex() {
4386
- const {
4387
- dragHandler,
4388
- index,
4389
- scrollBody
4390
- } = emblaApi.internalEngine();
4391
- const selectedSnap = emblaApi.selectedScrollSnap();
4392
- if (!dragHandler.pointerDown()) return selectedSnap;
4393
- const directionSign = Math.sign(scrollBody.velocity());
4394
- const distanceSign = Math.sign(distanceFromPointerDown);
4395
- const nextSnap = index.clone().set(selectedSnap).add(directionSign * -1).get();
4396
- if (!directionSign || !distanceSign) return null;
4397
- return distanceSign === directionSign ? nextSnap : selectedSnap;
4398
- }
4399
- function fade(emblaApi) {
4400
- const {
4401
- dragHandler,
4402
- scrollBody
4403
- } = emblaApi.internalEngine();
4404
- const pointerDown = dragHandler.pointerDown();
4405
- const velocity = scrollBody.velocity();
4406
- const duration = scrollBody.duration();
4407
- const fadeIndex = getFadeIndex();
4408
- const noFadeIndex = !isNumber(fadeIndex);
4409
- if (pointerDown) {
4410
- if (!velocity) return;
4411
- distanceFromPointerDown += velocity;
4412
- fadeVelocity = Math.abs(velocity / fadeToNextDistance);
4413
- lockExcessiveScroll(fadeIndex);
4414
- }
4415
- if (!pointerDown) {
4416
- if (!duration || noFadeIndex) return;
4417
- fadeVelocity += (fullOpacity - opacities[fadeIndex]) / duration;
4418
- fadeVelocity *= fadeFriction;
4419
- }
4420
- if (noFadeIndex) return;
4421
- setOpacities(fadeIndex, fadeVelocity);
4422
- }
4423
- function settled() {
4424
- const {
4425
- target,
4426
- location
4427
- } = emblaApi.internalEngine();
4428
- const diffToTarget = target.get() - location.get();
4429
- const notReachedTarget = Math.abs(diffToTarget) >= 1;
4430
- const fadeIndex = getFadeIndex();
4431
- const noFadeIndex = !isNumber(fadeIndex);
4432
- fade(emblaApi);
4433
- if (noFadeIndex || notReachedTarget) return false;
4434
- return opacities[fadeIndex] > 0.999;
4435
- }
4436
- function scrollProgress() {
4437
- return progress;
4438
- }
4439
- const self = {
4440
- name: 'fade',
4441
- options: userOptions,
4442
- init,
4443
- destroy
4444
- };
4445
- return self;
4446
- }
4447
- Fade.globalOptions = undefined;
4448
-
4449
4779
  var LsgHeroController = /** @class */function () {
4450
4780
  function LsgHeroController(viewport, elements, options) {
4451
4781
  if (elements === void 0) {