@asante-org/atlascopco-vt-litesitegenerator 3.8.13 → 3.8.15

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,247 @@ 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
+ var directChildren = function (container, selector) {
2497
+ return container ? Array.from(container.children).filter(function (el) {
2498
+ return el instanceof HTMLElement && el.matches(selector);
2499
+ }) : [];
2500
+ };
2263
2501
  var LsgCarouselController = /** @class */function () {
2264
2502
  function LsgCarouselController(viewport, elements, options) {
2503
+ var _a;
2265
2504
  if (elements === void 0) {
2266
2505
  elements = {};
2267
2506
  }
@@ -2269,8 +2508,10 @@ var LsgCarouselController = /** @class */function () {
2269
2508
  options = {};
2270
2509
  }
2271
2510
  var _this = this;
2272
- var _a, _b, _c, _d;
2273
- this.embla = null;
2511
+ var _b, _c, _d, _e, _f, _g, _h, _j;
2512
+ this.media = null;
2513
+ this.content = null;
2514
+ this.syncing = false;
2274
2515
  /**
2275
2516
  * Embla automatically falls back to non-looping mode when there is not enough
2276
2517
  * slide content to create a safe loop (most noticeable with two-up layouts and
@@ -2278,82 +2519,271 @@ var LsgCarouselController = /** @class */function () {
2278
2519
  * when there is no native previous snap, jump to the final authored snap.
2279
2520
  */
2280
2521
  this.scrollPrev = function () {
2281
- if (!_this.embla) return;
2282
- if (_this.embla.canScrollPrev()) {
2283
- _this.embla.scrollPrev();
2522
+ if (!_this.media) return;
2523
+ if (_this.media.canScrollPrev()) {
2524
+ _this.media.scrollPrev();
2284
2525
  return;
2285
2526
  }
2286
- var lastSnap = _this.embla.scrollSnapList().length - 1;
2287
- if (lastSnap >= 0) _this.embla.scrollTo(lastSnap);
2527
+ var lastSnap = _this.media.scrollSnapList().length - 1;
2528
+ if (lastSnap >= 0) _this.media.scrollTo(lastSnap);
2288
2529
  };
2289
2530
  /** See `scrollPrev` for why the explicit wrap fallback is required. */
2290
2531
  this.scrollNext = function () {
2291
- if (!_this.embla) return;
2292
- if (_this.embla.canScrollNext()) {
2293
- _this.embla.scrollNext();
2532
+ if (!_this.media) return;
2533
+ if (_this.media.canScrollNext()) {
2534
+ _this.media.scrollNext();
2294
2535
  return;
2295
2536
  }
2296
- if (_this.embla.scrollSnapList().length > 0) _this.embla.scrollTo(0);
2537
+ if (_this.media.scrollSnapList().length > 0) _this.media.scrollTo(0);
2297
2538
  };
2539
+ /**
2540
+ * The text track never initiates navigation, but it owns its own resize and
2541
+ * mutation observers. If one instance re-measures without the other, this pulls
2542
+ * the media track back onto the text track's index rather than letting them drift.
2543
+ */
2544
+ this.handleContentSelect = function () {
2545
+ if (!_this.media || !_this.content || _this.syncing) return;
2546
+ var target = _this.content.selectedScrollSnap();
2547
+ if (_this.media.selectedScrollSnap() === target) return;
2548
+ _this.syncing = true;
2549
+ try {
2550
+ _this.media.scrollTo(target, true);
2551
+ } finally {
2552
+ _this.syncing = false;
2553
+ }
2554
+ };
2555
+ /** Reader-initiated navigation: repaint state, then crossfade the text. */
2556
+ this.handleMediaSelect = function () {
2557
+ _this.syncSelectedState();
2558
+ _this.pushToContent();
2559
+ };
2560
+ /**
2561
+ * Re-measure events (breakpoint flip, resize, authored items added/removed).
2562
+ * The text is realigned with a hard cut because nothing moved for the reader.
2563
+ */
2564
+ this.handleResync = function () {
2565
+ _this.syncSelectedState();
2566
+ _this.pushToContent(true);
2567
+ };
2568
+ /** Repaints dot/arrow/a11y state from the media index. Never navigates. */
2298
2569
  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;
2570
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2571
+ var selected = (_b = (_a = _this.media) === null || _a === void 0 ? void 0 : _a.selectedScrollSnap()) !== null && _b !== void 0 ? _b : 0;
2572
+ var snapCount = (_d = (_c = _this.media) === null || _c === void 0 ? void 0 : _c.scrollSnapList().length) !== null && _d !== void 0 ? _d : 0;
2301
2573
  _this.paginationBtns.forEach(function (button, index) {
2302
2574
  var active = index === selected;
2303
2575
  button.classList.toggle("lsg-carousel__dot--active", active);
2304
2576
  button.setAttribute("aria-current", active ? "true" : "false");
2305
2577
  });
2578
+ // Cosmetic only. Looping plus the cyclic wrap above mean both arrows always
2579
+ // act, so `disabled`/`aria-disabled` would misreport them to assistive tech.
2580
+ // Themes use these classes to mute the leading/trailing half of the pill.
2581
+ (_e = _this.els.prevBtn) === null || _e === void 0 ? void 0 : _e.classList.toggle("lsg-carousel__arrow--at-start", selected === 0);
2582
+ (_f = _this.els.nextBtn) === null || _f === void 0 ? void 0 : _f.classList.toggle("lsg-carousel__arrow--at-end", snapCount > 0 && selected === snapCount - 1);
2583
+ // Which slide is in focus, and which neighbour sits on each side of it. The
2584
+ // neighbours only wrap when Embla is really looping: with too few slides it
2585
+ // falls back to a plain track (see scrollPrev), where slide 0 has nothing
2586
+ // to its left. `options` is the effective set, after that fallback.
2587
+ var count = _this.mediaSlides.length;
2588
+ var looping = (_h = (_g = _this.media) === null || _g === void 0 ? void 0 : _g.internalEngine().options.loop) !== null && _h !== void 0 ? _h : false;
2589
+ var prev = looping ? (selected - 1 + count) % count : selected - 1;
2590
+ var next = looping ? (selected + 1) % count : selected + 1;
2591
+ _this.mediaSlides.forEach(function (slide, index) {
2592
+ slide.classList.toggle(SLIDE_ACTIVE, index === selected);
2593
+ slide.classList.toggle(SLIDE_PREV, index === prev);
2594
+ slide.classList.toggle(SLIDE_NEXT, index === next);
2595
+ });
2596
+ // The fade plugin only sets opacity and pointer-events, so without this every
2597
+ // faded-out text block keeps its heading in the accessibility tree and its CTA
2598
+ // in the tab order - N copies of the same landmark for a sighted-equivalent
2599
+ // view of one.
2600
+ _this.contentSlides.forEach(function (slide, index) {
2601
+ var active = index === selected;
2602
+ slide.toggleAttribute("inert", !active);
2603
+ if (active) slide.removeAttribute("aria-hidden");else slide.setAttribute("aria-hidden", "true");
2604
+ });
2306
2605
  };
2307
2606
  this.els = elements;
2607
+ this.mediaViewport = viewport;
2308
2608
  this.paginationBtns = elements.paginationBtns ? Array.from(elements.paginationBtns) : [];
2309
- this.embla = EmblaCarousel(viewport, {
2310
- align: "start",
2609
+ var mediaTrack = viewport.firstElementChild;
2610
+ var mediaSlides = directChildren(mediaTrack, MEDIA_ITEM_SELECTOR);
2611
+ this.mediaSlides = mediaSlides;
2612
+ var align = (_b = options.align) !== null && _b !== void 0 ? _b : "start";
2613
+ // Embla resolves `container` as `root.children[0]` and `slides` as its
2614
+ // element children. Passing both explicitly instead makes the instance
2615
+ // immune to AEM injecting its own nodes (e.g. the edit-mode
2616
+ // `div.new.section.aem-Grid-newComponent` drop target) into the track.
2617
+ this.media = EmblaCarousel(viewport, {
2618
+ // `center` loops natively from three items as long as a slide is at least
2619
+ // half the viewport wide: Embla's canLoop() needs the OTHER slides to
2620
+ // cover the viewport. LsgCarousel.scss sizes the full-width slides to
2621
+ // --lsg-carousel-center-basis (50%) for exactly this reason.
2622
+ align: align,
2311
2623
  // Every authored media item owns a pagination dot. `trimSnaps` can remove
2312
2624
  // the final snap when two-up/peek layouts do not have enough overflow,
2313
2625
  // leaving the last authored item only partially reachable. Keep all snaps
2314
2626
  // so dot/item indexes stay 1:1 for 2, 3, 4, ... media items.
2627
+ //
2628
+ // This is ALSO load-bearing for the text/media sync: `containSnaps` is
2629
+ // `!loop && !!containScroll`, so with this `false` the snap list is
2630
+ // always `snapsAligned` - one snap per slide, whatever the slide's
2631
+ // flex-basis. That is the only reason a 380px peek track and a 100%-wide
2632
+ // crossfade track can share one index. Reverting this to `trimSnaps`
2633
+ // drops trailing snaps on the media track alone and desyncs the tail.
2315
2634
  containScroll: false,
2316
- direction: (_a = options.direction) !== null && _a !== void 0 ? _a : "ltr",
2317
- loop: (_b = options.loop) !== null && _b !== void 0 ? _b : true,
2635
+ direction: (_c = options.direction) !== null && _c !== void 0 ? _c : "ltr",
2636
+ loop: (_d = options.loop) !== null && _d !== void 0 ? _d : true,
2318
2637
  skipSnaps: false,
2319
- slidesToScroll: 1
2638
+ slidesToScroll: 1,
2639
+ container: mediaTrack !== null && mediaTrack !== void 0 ? mediaTrack : undefined,
2640
+ slides: mediaSlides.length ? mediaSlides : undefined,
2641
+ // All layouts collapse to the same mobile stack, which is never
2642
+ // mirrored and never centred. Embla restores the selected index when a
2643
+ // breakpoint re-activates, so the flip is not visible to the reader.
2644
+ breakpoints: (_a = {}, _a[MOBILE_MEDIA_QUERY$1] = {
2645
+ direction: "ltr",
2646
+ align: "start"
2647
+ }, _a)
2320
2648
  });
2649
+ var contentViewport = (_e = elements.contentViewport) !== null && _e !== void 0 ? _e : null;
2650
+ var contentTrack = contentViewport === null || contentViewport === void 0 ? void 0 : contentViewport.firstElementChild;
2651
+ this.contentSlides = directChildren(contentTrack, CONTENT_ITEM_SELECTOR);
2652
+ // Unequal counts are the one remaining way the two tracks can desync, and a
2653
+ // text block silently paired with the wrong image is worse than no text
2654
+ // animation at all. Degrade to media-only rather than risk it.
2655
+ var countsMatch = mediaSlides.length > 0 && this.contentSlides.length === mediaSlides.length;
2656
+ if (contentViewport && contentTrack && countsMatch) {
2657
+ this.content = EmblaCarousel(contentViewport, {
2658
+ align: "start",
2659
+ // Must match the media track's snap arithmetic - see above.
2660
+ containScroll: false,
2661
+ // The text crossfades in place, so there is nothing to mirror.
2662
+ direction: "ltr",
2663
+ // Inert on a fade instance: the plugin calls `disableScroll()`, which
2664
+ // deactivates the translate and every loop point. Set for config
2665
+ // symmetry with the media track, not for behaviour.
2666
+ loop: (_f = options.loop) !== null && _f !== void 0 ? _f : true,
2667
+ skipSnaps: false,
2668
+ slidesToScroll: 1,
2669
+ // The reader drags the media, never the text. Without this the text
2670
+ // track would fight the media track for the same pointer.
2671
+ watchDrag: false,
2672
+ container: contentTrack,
2673
+ slides: this.contentSlides
2674
+ }, [Fade()]);
2675
+ }
2321
2676
  this.paginationHandlers = this.paginationBtns.map(function (_, index) {
2322
2677
  return function () {
2323
2678
  var _a;
2324
- (_a = _this.embla) === null || _a === void 0 ? void 0 : _a.scrollTo(index);
2679
+ (_a = _this.media) === null || _a === void 0 ? void 0 : _a.scrollTo(index);
2325
2680
  };
2326
2681
  });
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);
2682
+ // Focus mode: clicking a peeking neighbour centres it instead of reaching the
2683
+ // media inside, so a half-hidden video cannot start playing off-centre.
2684
+ // Capture phase, and Embla's own capture listener on the viewport runs first
2685
+ // and swallows the click that ends a drag, so only real clicks get here.
2686
+ this.slideClickHandlers = align === "center" ? mediaSlides.map(function (_, index) {
2687
+ return function (event) {
2688
+ if (!_this.media || _this.media.selectedScrollSnap() === index) return;
2689
+ event.preventDefault();
2690
+ event.stopPropagation();
2691
+ _this.media.scrollTo(index);
2692
+ };
2693
+ }) : [];
2694
+ (_g = this.els.prevBtn) === null || _g === void 0 ? void 0 : _g.addEventListener("click", this.scrollPrev);
2695
+ (_h = this.els.nextBtn) === null || _h === void 0 ? void 0 : _h.addEventListener("click", this.scrollNext);
2329
2696
  this.paginationBtns.forEach(function (button, index) {
2330
2697
  button.addEventListener("click", _this.paginationHandlers[index]);
2331
2698
  });
2332
- this.embla.on("select", this.syncSelectedState);
2333
- this.embla.on("reInit", this.syncSelectedState);
2334
- this.syncSelectedState();
2699
+ this.slideClickHandlers.forEach(function (handler, index) {
2700
+ mediaSlides[index].addEventListener("click", handler, true);
2701
+ });
2702
+ this.media.on("select", this.handleMediaSelect);
2703
+ this.media.on("reInit", this.handleResync);
2704
+ this.media.on("slidesChanged", this.handleResync);
2705
+ (_j = this.content) === null || _j === void 0 ? void 0 : _j.on("select", this.handleContentSelect);
2706
+ // Initial alignment is a hard cut - there is nothing to crossfade from.
2707
+ this.handleResync();
2335
2708
  }
2709
+ /**
2710
+ * Drive the text track from the media index.
2711
+ *
2712
+ * `jump` skips the crossfade (Embla sets duration 0, and the fade plugin then
2713
+ * snaps opacity instantly). Use it only for re-measure events, never for
2714
+ * reader-initiated navigation, or the text hard-cuts instead of fading.
2715
+ */
2716
+ LsgCarouselController.prototype.pushToContent = function (jump) {
2717
+ if (jump === void 0) {
2718
+ jump = false;
2719
+ }
2720
+ if (!this.media || !this.content || this.syncing) return;
2721
+ var target = this.media.selectedScrollSnap();
2722
+ if (this.content.selectedScrollSnap() === target) return;
2723
+ this.syncing = true;
2724
+ try {
2725
+ this.content.scrollTo(target, jump);
2726
+ } finally {
2727
+ this.syncing = false;
2728
+ }
2729
+ };
2336
2730
  Object.defineProperty(LsgCarouselController.prototype, "emblaApi", {
2337
2731
  get: function () {
2338
- return this.embla;
2732
+ return this.media;
2733
+ },
2734
+ enumerable: false,
2735
+ configurable: true
2736
+ });
2737
+ Object.defineProperty(LsgCarouselController.prototype, "contentApi", {
2738
+ /** Text track handle. Exposed for the index-parity checks in the test plan. */
2739
+ get: function () {
2740
+ return this.content;
2339
2741
  },
2340
2742
  enumerable: false,
2341
2743
  configurable: true
2342
2744
  });
2343
2745
  LsgCarouselController.prototype.destroy = function () {
2344
2746
  var _this = this;
2345
- var _a, _b;
2747
+ var _a, _b, _c, _d;
2346
2748
  (_a = this.els.prevBtn) === null || _a === void 0 ? void 0 : _a.removeEventListener("click", this.scrollPrev);
2347
2749
  (_b = this.els.nextBtn) === null || _b === void 0 ? void 0 : _b.removeEventListener("click", this.scrollNext);
2348
2750
  this.paginationBtns.forEach(function (button, index) {
2349
2751
  button.removeEventListener("click", _this.paginationHandlers[index]);
2350
2752
  });
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;
2753
+ this.slideClickHandlers.forEach(function (handler, index) {
2754
+ _this.mediaSlides[index].removeEventListener("click", handler, true);
2755
+ });
2756
+ // Content first: its plugin restores the inline opacity/transform it wrote,
2757
+ // and it reads `internalEngine()` while doing so.
2758
+ if (this.content) {
2759
+ this.content.off("select", this.handleContentSelect);
2760
+ this.content.destroy();
2761
+ this.content = null;
2762
+ }
2763
+ if (this.media) {
2764
+ this.media.off("select", this.handleMediaSelect);
2765
+ this.media.off("reInit", this.handleResync);
2766
+ this.media.off("slidesChanged", this.handleResync);
2767
+ this.media.destroy();
2768
+ this.media = null;
2356
2769
  }
2770
+ // Embla restores what Embla wrote. Everything below is ours, and leaving it
2771
+ // behind would make server-rendered markup re-initialise into a stale state.
2772
+ this.paginationBtns.forEach(function (button) {
2773
+ button.classList.remove("lsg-carousel__dot--active");
2774
+ button.removeAttribute("aria-current");
2775
+ });
2776
+ (_c = this.els.prevBtn) === null || _c === void 0 ? void 0 : _c.classList.remove("lsg-carousel__arrow--at-start");
2777
+ (_d = this.els.nextBtn) === null || _d === void 0 ? void 0 : _d.classList.remove("lsg-carousel__arrow--at-end");
2778
+ // SLIDE_ACTIVE stays off too: the server/React markup puts it on slide 0,
2779
+ // so re-initialising adds it back there.
2780
+ this.mediaSlides.forEach(function (slide) {
2781
+ slide.classList.remove(SLIDE_ACTIVE, SLIDE_PREV, SLIDE_NEXT);
2782
+ });
2783
+ this.contentSlides.forEach(function (slide) {
2784
+ slide.removeAttribute("inert");
2785
+ slide.removeAttribute("aria-hidden");
2786
+ });
2357
2787
  };
2358
2788
  return LsgCarouselController;
2359
2789
  }();
@@ -2365,24 +2795,28 @@ var mediaTypeFor = function (item) {
2365
2795
  var directionForLayout = function (layout) {
2366
2796
  return layout === "media-left" ? "rtl" : "ltr";
2367
2797
  };
2798
+ var hasText = function (item) {
2799
+ return Boolean(item.preTitle || item.heading || item.body || item.cta);
2800
+ };
2368
2801
  var LsgCarousel = function (_a) {
2369
- var _b;
2370
- var heading = _a.heading,
2802
+ var _b = _a.items,
2803
+ items = _b === void 0 ? [] : _b,
2804
+ _c = _a.layout,
2805
+ layout = _c === void 0 ? "media-right" : _c,
2806
+ background = _a.background,
2807
+ accent = _a.accent,
2808
+ _d = _a.headingTag,
2809
+ headingTag = _d === void 0 ? "h2" : _d,
2810
+ _e = _a.ariaLabel,
2811
+ ariaLabel = _e === void 0 ? "Media carousel" : _e,
2812
+ _f = _a.className,
2813
+ className = _f === void 0 ? "" : _f,
2814
+ heading = _a.heading,
2371
2815
  preTitle = _a.preTitle,
2372
2816
  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);
2817
+ cta = _a.cta;
2385
2818
  var viewportRef = useRef(null);
2819
+ var contentViewportRef = useRef(null);
2386
2820
  var prevBtnRef = useRef(null);
2387
2821
  var nextBtnRef = useRef(null);
2388
2822
  var dotsRef = useRef(null);
@@ -2390,71 +2824,109 @@ var LsgCarousel = function (_a) {
2390
2824
  var HeadingTag = headingTag;
2391
2825
  var isCarousel = items.length > 1;
2392
2826
  var carouselDirection = directionForLayout(layout);
2827
+ var carouselAlign = alignForLayout(layout);
2828
+ // Legacy single-content-block callers passed the text alongside `items`. Fold
2829
+ // it onto the first item so their carousel still renders its copy.
2830
+ var resolvedItems = useMemo(function () {
2831
+ var legacy = {
2832
+ preTitle: preTitle,
2833
+ heading: heading,
2834
+ body: body,
2835
+ cta: cta
2836
+ };
2837
+ var hasLegacy = Boolean(legacy.preTitle || legacy.heading || legacy.body || legacy.cta);
2838
+ if (!hasLegacy || !items.length || hasText(items[0])) return items;
2839
+ return items.map(function (item, index) {
2840
+ return index === 0 ? __assign(__assign({}, legacy), item) : item;
2841
+ });
2842
+ }, [items, preTitle, heading, body, cta]);
2393
2843
  useEffect(function () {
2394
2844
  var _a, _b;
2395
2845
  if (!viewportRef.current || !isCarousel) return;
2396
2846
  controllerRef.current = new LsgCarouselController(viewportRef.current, {
2397
2847
  prevBtn: prevBtnRef.current,
2398
2848
  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 : []
2849
+ paginationBtns: (_b = (_a = dotsRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll("[data-lsg-carousel-dot]")) !== null && _b !== void 0 ? _b : [],
2850
+ contentViewport: contentViewportRef.current
2400
2851
  }, {
2401
2852
  loop: true,
2402
- direction: carouselDirection
2853
+ direction: carouselDirection,
2854
+ align: carouselAlign
2403
2855
  });
2404
2856
  return function () {
2405
2857
  var _a;
2406
2858
  (_a = controllerRef.current) === null || _a === void 0 ? void 0 : _a.destroy();
2407
2859
  controllerRef.current = null;
2408
2860
  };
2409
- }, [carouselDirection, isCarousel, items.length]);
2410
- var rootClasses = ["lsg-carousel", "lsg-carousel--".concat(layout), className].filter(Boolean).join(" ");
2861
+ }, [carouselAlign, carouselDirection, isCarousel, resolvedItems.length]);
2862
+ var rootClasses = ["lsg-carousel", "lsg-carousel--".concat(layout),
2863
+ // In AEM this class arrives on the decoration div instead; the SCSS only
2864
+ // sets inherited custom properties, so either position works.
2865
+ background ? "lsg-carousel--bg-".concat(background) : "", accent ? "lsg-carousel--accent-".concat(accent) : "", className].filter(Boolean).join(" ");
2866
+ var keyFor = function (item, index) {
2867
+ var _a, _b, _c;
2868
+ 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);
2869
+ };
2411
2870
  return /*#__PURE__*/React.createElement("section", {
2412
- ref: rootRef,
2413
2871
  className: rootClasses,
2414
2872
  "data-lsg-is": "carousel",
2415
2873
  "data-lsg-carousel-layout": layout,
2416
2874
  "data-lsg-carousel-direction": carouselDirection,
2417
- "data-lsg-carousel-count": items.length,
2875
+ "data-lsg-carousel-count": resolvedItems.length,
2418
2876
  "aria-label": ariaLabel
2419
2877
  }, /*#__PURE__*/React.createElement("div", {
2420
2878
  className: "lsg-carousel__shell"
2421
2879
  }, /*#__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", {
2880
+ className: "lsg-carousel__content",
2881
+ ref: contentViewportRef,
2882
+ "data-lsg-carousel-content-viewport": true
2883
+ }, /*#__PURE__*/React.createElement("div", {
2884
+ className: "lsg-carousel__content-track"
2885
+ }, resolvedItems.map(function (item, index) {
2886
+ var _a;
2887
+ return /*#__PURE__*/React.createElement("div", {
2888
+ key: keyFor(item, index),
2889
+ className: "lsg-carousel__content-slide",
2890
+ "data-lsg-carousel-content-item": true
2891
+ }, item.preTitle && /*#__PURE__*/React.createElement("div", {
2892
+ className: "lsg-carousel__pre-title"
2893
+ }, /*#__PURE__*/React.createElement("span", {
2894
+ className: "lsg-carousel__pre-title-marker",
2895
+ "aria-hidden": "true"
2896
+ }), /*#__PURE__*/React.createElement("span", null, item.preTitle)), item.heading && /*#__PURE__*/React.createElement(HeadingTag, {
2897
+ className: "lsg-carousel__heading"
2898
+ }, item.heading), item.body && /*#__PURE__*/React.createElement("p", {
2899
+ className: "lsg-carousel__body"
2900
+ }, item.body), item.cta && /*#__PURE__*/React.createElement("div", {
2901
+ className: "lsg-carousel__actions"
2902
+ }, /*#__PURE__*/React.createElement(LsgButton, {
2903
+ className: "lsg-carousel__cta",
2904
+ href: item.cta.href,
2905
+ external: item.cta.external,
2906
+ variant: (_a = item.cta.variant) !== null && _a !== void 0 ? _a : "primary",
2907
+ onClick: item.cta.onClick
2908
+ }, item.cta.label)));
2909
+ }))), /*#__PURE__*/React.createElement("div", {
2441
2910
  className: "lsg-carousel__media-column"
2442
2911
  }, /*#__PURE__*/React.createElement("div", {
2443
2912
  className: "lsg-carousel__viewport",
2444
2913
  ref: viewportRef,
2445
- dir: carouselDirection
2914
+ dir: carouselDirection,
2915
+ "data-lsg-carousel-media-viewport": true
2446
2916
  }, /*#__PURE__*/React.createElement("div", {
2447
2917
  className: "lsg-carousel__track",
2448
2918
  role: "list"
2449
- }, items.map(function (item, index) {
2450
- var _a, _b, _c, _d, _e, _f, _g;
2919
+ }, resolvedItems.map(function (item, index) {
2920
+ var _a, _b, _c, _d;
2451
2921
  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
2922
  return /*#__PURE__*/React.createElement("article", {
2454
- key: key,
2455
- className: "lsg-carousel__slide",
2923
+ key: keyFor(item, index)
2924
+ // Slide 0 starts active, as the first dot does, so the
2925
+ // full-width focus mode is not all-shrunk before JS runs.
2926
+ ,
2927
+ className: "lsg-carousel__slide".concat(index === 0 ? " lsg-carousel__slide--active" : ""),
2456
2928
  role: "listitem",
2457
- "aria-label": "Slide ".concat(index + 1, " of ").concat(items.length),
2929
+ "aria-label": "Slide ".concat(index + 1, " of ").concat(resolvedItems.length),
2458
2930
  "data-lsg-carousel-item": true
2459
2931
  }, /*#__PURE__*/React.createElement("div", {
2460
2932
  className: "lsg-carousel__media"
@@ -2462,10 +2934,10 @@ var LsgCarousel = function (_a) {
2462
2934
  videoUrl: item.videoUrl,
2463
2935
  imageUrl: item.videoPoster,
2464
2936
  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,
2937
+ controls: (_a = item.videoControls) !== null && _a !== void 0 ? _a : true,
2938
+ autoplay: (_b = item.videoAutoPlay) !== null && _b !== void 0 ? _b : false,
2939
+ muted: (_c = item.videoMuted) !== null && _c !== void 0 ? _c : true,
2940
+ loop: (_d = item.videoLoop) !== null && _d !== void 0 ? _d : false,
2469
2941
  displayMode: "content",
2470
2942
  showTags: false,
2471
2943
  className: "lsg-carousel__video-component"
@@ -2480,8 +2952,10 @@ var LsgCarousel = function (_a) {
2480
2952
  className: "lsg-carousel__media-label"
2481
2953
  }, item.label)));
2482
2954
  }))), isCarousel && /*#__PURE__*/React.createElement("div", {
2483
- className: "lsg-carousel__navigation",
2955
+ className: "lsg-carousel__controls",
2484
2956
  "aria-label": "Carousel navigation"
2957
+ }, /*#__PURE__*/React.createElement("div", {
2958
+ className: "lsg-carousel__nav"
2485
2959
  }, /*#__PURE__*/React.createElement("button", {
2486
2960
  ref: prevBtnRef,
2487
2961
  className: "lsg-carousel__arrow lsg-carousel__arrow--prev",
@@ -2491,10 +2965,19 @@ var LsgCarousel = function (_a) {
2491
2965
  }, /*#__PURE__*/React.createElement("span", {
2492
2966
  className: "lsg-carousel__arrow-icon",
2493
2967
  "aria-hidden": "true"
2494
- })), /*#__PURE__*/React.createElement("div", {
2968
+ })), /*#__PURE__*/React.createElement("button", {
2969
+ ref: nextBtnRef,
2970
+ className: "lsg-carousel__arrow lsg-carousel__arrow--next",
2971
+ type: "button",
2972
+ "aria-label": "Next slide",
2973
+ "data-lsg-carousel-next": true
2974
+ }, /*#__PURE__*/React.createElement("span", {
2975
+ className: "lsg-carousel__arrow-icon",
2976
+ "aria-hidden": "true"
2977
+ }))), /*#__PURE__*/React.createElement("div", {
2495
2978
  className: "lsg-carousel__pagination",
2496
2979
  ref: dotsRef
2497
- }, items.map(function (item, index) {
2980
+ }, resolvedItems.map(function (item, index) {
2498
2981
  var _a;
2499
2982
  return /*#__PURE__*/React.createElement("button", {
2500
2983
  key: (_a = item.id) !== null && _a !== void 0 ? _a : "dot-".concat(index),
@@ -2504,15 +2987,6 @@ var LsgCarousel = function (_a) {
2504
2987
  "aria-current": index === 0 ? "true" : "false",
2505
2988
  "data-lsg-carousel-dot": true
2506
2989
  });
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
2990
  }))))));
2517
2991
  };
2518
2992
 
@@ -4225,227 +4699,6 @@ var LsgFooter = function (_a) {
4225
4699
  }))))), bottomTopBar, bottomBar);
4226
4700
  };
4227
4701
 
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
4702
  var LsgHeroController = /** @class */function () {
4450
4703
  function LsgHeroController(viewport, elements, options) {
4451
4704
  if (elements === void 0) {