@widelab-nc/widelab 1.1.40 → 1.1.42

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@widelab-nc/widelab",
3
- "version": "1.1.40",
3
+ "version": "1.1.42",
4
4
  "description": "Widelab starter template based on Finsweet template + add-ons.",
5
5
  "homepage": "https://widelab.co",
6
6
  "license": "ISC",
package/src/index.js CHANGED
@@ -448,7 +448,42 @@ if (infoBar) {
448
448
  });
449
449
  }
450
450
 
451
+ let articleModalOpen;
452
+ function articleModalOpenAnimation() {
453
+ const articleModalWrapper = document.querySelector('.article-modal_main-wrapper');
454
+ const articleModalContent = document.querySelector('.article-modal_content-wrapper');
455
+ articleModalOpen = gsap.timeline({paused: true});
456
+ articleModalOpen.to(articleModalWrapper, {
457
+ autoAlpha: 1,
458
+ duration: 0.3,
459
+ ease: 'loader2',
460
+ })
461
+ articleModalOpen.from(articleModalContent, {
462
+ x: '100%',
463
+ duration: 0.4,
464
+ ease: 'loader2',
465
+ }, '-=0.2')
466
+ }
467
+
468
+ window.openArticleModal = function() {
469
+ articleModalOpen.play();
470
+ document.body.classList.add('is-scroll-off');
471
+ lenis.stop();
472
+ }
451
473
 
474
+ window.closeArticleModal = function() {
475
+ articleModalOpen.timeScale(1).reverse();
476
+ document.body.classList.remove('is-scroll-off');
477
+ lenis.start();
478
+ }
479
+
480
+ window.closeArticleModalOnEsc = function(e) {
481
+ document.addEventListener('keydown', function(event) {
482
+ if (event.key === "Escape") {
483
+ window.closeArticleModal();
484
+ }
485
+ });
486
+ }
452
487
 
453
488
  // Safe new version that uses the same classes and structure
454
489
  window.customCursorTextUpdate = function(el) {
@@ -478,59 +513,175 @@ window.customCursorTextUpdate = function(el) {
478
513
  el.addEventListener('pointerleave', maybeDeactivate);
479
514
  };
480
515
 
516
+ window.stickyServices = function (e) {
517
+ var wrapper = e;
518
+ if (!wrapper || wrapper.dataset.stickyServicesInit === 'true') return;
519
+ wrapper.dataset.stickyServicesInit = 'true';
481
520
 
521
+ var navItems = wrapper.querySelectorAll('.services_item-wrapper.is-animated');
522
+ var stackItems = wrapper.querySelectorAll('.services-stack-img');
482
523
 
524
+ var count = Math.min(navItems.length, stackItems.length);
525
+ if (count === 0) return;
483
526
 
484
- // Hero animation on load
485
- window.heroAnimationOnLoad = function(e) {
486
- const videoMask = e.querySelector('.hero_headline_ract-mask');
487
- const heroAnimation = gsap.timeline({}),
488
- homeHeroHeading = new SplitText('#heroHeading', {
489
- type: 'lines',
490
- linesClass: 'hero-line',
491
- }),
492
- homeHeroHeadingMask = new SplitText('#heroHeading', {
493
- type: 'lines',
494
- linesClass: 'hero-line-mask',
495
- });
496
- heroAnimation.from('.navbar', {
497
- opacity: 0,
498
- duration: 0.6,
527
+ var currentActive = -1;
528
+ var ticking = false;
529
+
530
+ function setActive(index) {
531
+ if (index === currentActive) return;
532
+ currentActive = index;
533
+
534
+ for (var i = 0; i < navItems.length; i++) {
535
+ navItems[i].classList.toggle('is-active', i === index);
536
+ }
537
+ for (var j = 0; j < stackItems.length; j++) {
538
+ stackItems[j].classList.toggle('is-active', j === index);
539
+ }
540
+ }
541
+
542
+ function update() {
543
+ ticking = false;
544
+
545
+ var rect = wrapper.getBoundingClientRect();
546
+ var total = wrapper.offsetHeight - window.innerHeight;
547
+
548
+ // Wrapper not tall enough to scroll through (e.g. very tall viewport) — keep first item active.
549
+ if (total <= 0) {
550
+ setActive(0);
551
+ return;
552
+ }
553
+
554
+ var scrolled = -rect.top;
555
+ var progress = Math.min(1, Math.max(0, scrolled / total));
556
+ var activeIndex = Math.min(count - 1, Math.floor(progress * count));
557
+
558
+ setActive(activeIndex);
559
+ }
560
+
561
+ function onScrollOrResize() {
562
+ if (!ticking) {
563
+ requestAnimationFrame(update);
564
+ ticking = true;
565
+ }
566
+ }
567
+
568
+ window.addEventListener('scroll', onScrollOrResize, { passive: true });
569
+ window.addEventListener('resize', onScrollOrResize);
570
+
571
+ update(); // set correct state immediately on load (handles page-load mid-scroll / refresh position)
572
+ };
573
+
574
+
575
+ window.heroAnimationOnLoad = function(e) {
576
+ const videoMask = e.querySelector('.hero_headline_ract-mask');
577
+ const heroAnimation = gsap.timeline({}),
578
+ homeHeroHeading = new SplitText('#heroHeading', {
579
+ type: 'lines',
580
+ linesClass: 'hero-line',
581
+ }),
582
+ homeHeroHeadingMask = new SplitText('#heroHeading', {
583
+ type: 'lines',
584
+ linesClass: 'hero-line-mask',
585
+ });
586
+
587
+ heroAnimation.from('.navbar', {
588
+ opacity: 0,
589
+ duration: 0.6,
590
+ ease: 'loader2',
591
+ }, '>+0.4');
592
+
593
+ heroAnimation.from(
594
+ '.hero-line',
595
+ {
596
+ duration: 1.2,
597
+ y: '100%',
598
+ stagger: 0.2,
499
599
  ease: 'loader2',
500
600
  },
501
- '>+0.4');
502
- heroAnimation.from(
503
- '.hero-line',
504
- {
505
- duration: 1.2,
506
- y: '100%',
507
- stagger: 0.2,
508
- ease: 'loader2',
509
- },
510
- '>-0.5'
511
- );
512
- if (videoMask) {
513
- heroAnimation.to(
514
- '.hero_headline_ract-mask',
515
- {
516
- width: '0%',
517
- duration: 0.6,
518
- ease: 'loader2',
519
- },
520
- '>-0.6'
521
- );
522
- }
523
- heroAnimation.from(
524
- '#heroBottomWrapper',
601
+ '>-0.5'
602
+ );
603
+
604
+ if (videoMask) {
605
+ heroAnimation.to(
606
+ '.hero_headline_ract-mask',
525
607
  {
526
- opacity: 0,
527
- duration: 1,
608
+ width: '0%',
609
+ duration: 0.6,
528
610
  ease: 'loader2',
529
611
  },
530
- '>-0.4'
612
+ '>-0.6'
531
613
  );
532
614
  }
533
615
 
616
+ heroAnimation.from(
617
+ '#heroBottomWrapper',
618
+ {
619
+ opacity: 0,
620
+ duration: 1,
621
+ ease: 'loader2',
622
+ },
623
+ '>-0.4'
624
+ );
625
+
626
+ heroAnimation.call(() => {
627
+ const rotateEl = e.querySelector('[rotate-text]');
628
+ if (!rotateEl) return;
629
+
630
+ const attrValue = rotateEl.getAttribute('rotate-text') || '';
631
+ const extraPhrases = attrValue
632
+ .split(',')
633
+ .map(s => s.trim())
634
+ .filter(Boolean);
635
+
636
+ const defaultText = rotateEl.textContent.trim();
637
+ const phrases = [defaultText, ...extraPhrases];
638
+
639
+ if (phrases.length < 2) return;
640
+
641
+ rotateEl.style.display = 'inline-block';
642
+ rotateEl.style.overflow = 'hidden';
643
+ rotateEl.style.verticalAlign = 'bottom';
644
+ rotateEl.style.position = 'relative';
645
+
646
+ let currentSpan = document.createElement('span');
647
+ currentSpan.style.display = 'inline-block';
648
+ currentSpan.textContent = phrases[0];
649
+ rotateEl.textContent = '';
650
+ rotateEl.appendChild(currentSpan);
651
+
652
+ let current = 0;
653
+ const DURATION = 1.2;
654
+ const EASE = 'loader2';
655
+ const PAUSE = 2200;
656
+
657
+ function rotateTo(nextIndex) {
658
+ const incoming = document.createElement('span');
659
+ incoming.style.cssText = 'display:inline-block; position:absolute; left:0; top:0; white-space:nowrap;';
660
+ incoming.textContent = phrases[nextIndex];
661
+ rotateEl.appendChild(incoming);
662
+
663
+ const outgoing = currentSpan;
664
+
665
+ const tl = gsap.timeline({
666
+ onComplete: () => {
667
+ rotateEl.removeChild(outgoing);
668
+ incoming.style.position = 'static';
669
+ currentSpan = incoming;
670
+ current = nextIndex;
671
+ setTimeout(() => rotateTo((current + 1) % phrases.length), PAUSE);
672
+ }
673
+ });
674
+
675
+ tl.to(outgoing, { y: '-100%', duration: DURATION, ease: EASE }, 0);
676
+ tl.fromTo(incoming, { y: '100%' }, { y: '0%', duration: DURATION, ease: EASE }, 0);
677
+ }
678
+
679
+ setTimeout(() => rotateTo(1), PAUSE);
680
+ });
681
+ };
682
+
683
+
684
+
534
685
 
535
686
  // Case study mask animation + pill fade-in (full, iOS-safe, L->R reveal)
536
687
  window.caseStudyInteraction = function (e) {
@@ -2156,6 +2307,59 @@ window.contactUsHide = function(e) {
2156
2307
  }));
2157
2308
  }
2158
2309
 
2310
+
2311
+
2312
+ window.expandablePadSticky = function (e) {
2313
+ var wrapper = e;
2314
+ if (!wrapper || wrapper.dataset.expandablePadStickyInit === 'true') return;
2315
+ wrapper.dataset.expandablePadStickyInit = 'true';
2316
+
2317
+ var items = wrapper.querySelectorAll('.service_expanded');
2318
+ var count = items.length;
2319
+ if (count === 0) return;
2320
+
2321
+ var currentActive = -1;
2322
+ var ticking = false;
2323
+
2324
+ function setActive(index) {
2325
+ if (index === currentActive) return;
2326
+ currentActive = index;
2327
+ for (var i = 0; i < items.length; i++) {
2328
+ var isTarget = i === index;
2329
+ items[i].classList.toggle('is-active', isTarget);
2330
+ items[i].style.height = isTarget ? items[i].scrollHeight + 'px' : '0px';
2331
+ }
2332
+ }
2333
+
2334
+ function update() {
2335
+ ticking = false;
2336
+ var rect = wrapper.getBoundingClientRect();
2337
+ var total = wrapper.offsetHeight - window.innerHeight;
2338
+ if (total <= 0) { setActive(0); return; }
2339
+ var scrolled = -rect.top;
2340
+ var progress = Math.min(1, Math.max(0, scrolled / total));
2341
+ var activeIndex = Math.min(count - 1, Math.floor(progress * (count + 1)));
2342
+ setActive(activeIndex);
2343
+ }
2344
+
2345
+ function onScrollOrResize() {
2346
+ if (!ticking) { requestAnimationFrame(update); ticking = true; }
2347
+ }
2348
+
2349
+ function onResize() {
2350
+ onScrollOrResize();
2351
+ if (currentActive !== -1) {
2352
+ items[currentActive].style.height = items[currentActive].scrollHeight + 'px';
2353
+ }
2354
+ }
2355
+
2356
+ window.addEventListener('scroll', onScrollOrResize, { passive: true });
2357
+ window.addEventListener('resize', onResize);
2358
+ update();
2359
+ };
2360
+
2361
+
2362
+
2159
2363
  // Refresh Gsap scrollTriggers
2160
2364
  window.scrollTriggerRefresh = function() {
2161
2365
  function refreshScrollTrigger() {
@@ -2207,7 +2411,6 @@ window.closeInfoBar = function() {
2207
2411
  lenis.destroy();
2208
2412
  }
2209
2413
  customGsapEasing();
2210
-
2211
2414
  reloadOnBack();
2212
2415
  revealFooter();
2213
2416
  pageLoadAnimation();
@@ -2217,4 +2420,5 @@ window.closeInfoBar = function() {
2217
2420
  disableScrollMenu();
2218
2421
  scrollToHashOnLoad();
2219
2422
  initClientLogosAnimation();
2220
- });
2423
+ articleModalOpenAnimation();
2424
+ });