@vanduo-oss/framework 1.3.3 → 1.3.5

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.
@@ -1,4 +1,4 @@
1
- /*! Vanduo v1.3.3 | Built: 2026-04-10T21:45:12.664Z | git:281f4f6 | development */
1
+ /*! Vanduo v1.3.5 | Built: 2026-04-15T18:39:53.955Z | git:3ca4f62 | development */
2
2
 
3
3
  // js/utils/lifecycle.js
4
4
  (function() {
@@ -107,7 +107,7 @@
107
107
  // js/vanduo.js
108
108
  (function() {
109
109
  "use strict";
110
- const VANDUO_VERSION = true ? "1.3.3" : "0.0.0-dev";
110
+ const VANDUO_VERSION = true ? "1.3.5" : "0.0.0-dev";
111
111
  const Vanduo2 = {
112
112
  version: VANDUO_VERSION,
113
113
  components: {},
@@ -2157,6 +2157,31 @@
2157
2157
  this.initNavbar(navbar);
2158
2158
  });
2159
2159
  },
2160
+ /**
2161
+ * Initialize scroll-aware glass/transparent behaviour for a navbar.
2162
+ * Adds/removes `.vd-navbar-scrolled` when the page scrolls past a threshold.
2163
+ * Threshold: `data-scroll-threshold` attribute (px) or the navbar's own height.
2164
+ * @param {HTMLElement} navbar - Navbar element
2165
+ * @returns {Function|null} Cleanup function, or null if not applicable
2166
+ */
2167
+ initScrollWatcher: function(navbar) {
2168
+ const isGlass = navbar.classList.contains("vd-navbar-glass");
2169
+ const isTransparent = navbar.classList.contains("vd-navbar-transparent");
2170
+ if (!isGlass && !isTransparent) {
2171
+ return null;
2172
+ }
2173
+ const getThreshold = () => {
2174
+ const attr = parseInt(navbar.dataset.scrollThreshold, 10);
2175
+ return isNaN(attr) ? navbar.offsetHeight || 60 : attr;
2176
+ };
2177
+ const onScroll = () => {
2178
+ const scrolled = window.scrollY > getThreshold();
2179
+ navbar.classList.toggle("vd-navbar-scrolled", scrolled);
2180
+ };
2181
+ onScroll();
2182
+ window.addEventListener("scroll", onScroll, { passive: true });
2183
+ return () => window.removeEventListener("scroll", onScroll);
2184
+ },
2160
2185
  /**
2161
2186
  * Initialize a single navbar
2162
2187
  * @param {HTMLElement} navbar - Navbar element
@@ -2165,10 +2190,17 @@
2165
2190
  const toggle = navbar.querySelector(".vd-navbar-toggle, .vd-navbar-burger");
2166
2191
  const menu = navbar.querySelector(".vd-navbar-menu");
2167
2192
  const overlay = navbar.querySelector(".vd-navbar-overlay") || this.createOverlay(navbar);
2193
+ const cleanupFunctions = [];
2194
+ const scrollWatcherCleanup = this.initScrollWatcher(navbar);
2195
+ if (scrollWatcherCleanup) {
2196
+ cleanupFunctions.push(scrollWatcherCleanup);
2197
+ }
2168
2198
  if (!toggle || !menu) {
2199
+ if (cleanupFunctions.length) {
2200
+ this.instances.set(navbar, { toggle: null, menu: null, overlay: null, cleanup: cleanupFunctions });
2201
+ }
2169
2202
  return;
2170
2203
  }
2171
- const cleanupFunctions = [];
2172
2204
  const toggleClickHandler = (e) => {
2173
2205
  e.preventDefault();
2174
2206
  e.stopPropagation();
@@ -6479,6 +6511,178 @@
6479
6511
  window.VanduoLazyLoad = VanduoLazyLoad;
6480
6512
  })();
6481
6513
 
6514
+ // js/components/glass.js
6515
+ (function() {
6516
+ "use strict";
6517
+ const GlassScroll = {
6518
+ /** @type {Map<Element, IntersectionObserver>} */
6519
+ observers: /* @__PURE__ */ new Map(),
6520
+ init: function() {
6521
+ document.querySelectorAll("[data-glass-scroll]").forEach((el) => {
6522
+ if (this.observers.has(el)) return;
6523
+ this.initElement(el);
6524
+ });
6525
+ },
6526
+ /**
6527
+ * Wire up a single scroll-activated glass element.
6528
+ * @param {HTMLElement} el
6529
+ */
6530
+ initElement: function(el) {
6531
+ const sentinelSelector = el.dataset.glassSentinel;
6532
+ let sentinel;
6533
+ if (sentinelSelector) {
6534
+ sentinel = document.querySelector(sentinelSelector);
6535
+ }
6536
+ if (!sentinel) {
6537
+ sentinel = el.previousElementSibling;
6538
+ }
6539
+ if (!sentinel) {
6540
+ el.classList.add("is-glass-active");
6541
+ return;
6542
+ }
6543
+ const observer = new IntersectionObserver(
6544
+ (entries) => {
6545
+ entries.forEach((entry) => {
6546
+ el.classList.toggle("is-glass-active", !entry.isIntersecting);
6547
+ });
6548
+ },
6549
+ { threshold: 0, rootMargin: "0px" }
6550
+ );
6551
+ observer.observe(sentinel);
6552
+ this.observers.set(el, observer);
6553
+ },
6554
+ /**
6555
+ * Disconnect and remove a single element's observer.
6556
+ * @param {HTMLElement} el
6557
+ */
6558
+ destroy: function(el) {
6559
+ const observer = this.observers.get(el);
6560
+ if (observer) {
6561
+ observer.disconnect();
6562
+ this.observers.delete(el);
6563
+ }
6564
+ },
6565
+ destroyAll: function() {
6566
+ this.observers.forEach((observer, el) => this.destroy(el));
6567
+ }
6568
+ };
6569
+ if (typeof window.Vanduo !== "undefined") {
6570
+ window.Vanduo.register("glassScroll", GlassScroll);
6571
+ }
6572
+ window.VanduoGlassScroll = GlassScroll;
6573
+ })();
6574
+
6575
+ // js/components/morph.js
6576
+ (function() {
6577
+ "use strict";
6578
+ const MORPH_DURATION_MS = 750;
6579
+ const Morph = {
6580
+ instances: /* @__PURE__ */ new Map(),
6581
+ init: function() {
6582
+ const elements = document.querySelectorAll(".vd-morph, [data-vd-morph]");
6583
+ elements.forEach(function(el) {
6584
+ if (Morph.instances.has(el)) return;
6585
+ if (el.getAttribute("data-vd-morph") === "manual") return;
6586
+ Morph.initInstance(el);
6587
+ });
6588
+ },
6589
+ initInstance: function(el) {
6590
+ Morph._ensureLayers(el);
6591
+ const cleanup = [];
6592
+ let morphing = false;
6593
+ const handleClick = function(e) {
6594
+ if (morphing) return;
6595
+ Morph._runMorph(el, e, function() {
6596
+ morphing = false;
6597
+ });
6598
+ morphing = true;
6599
+ };
6600
+ el.addEventListener("click", handleClick);
6601
+ cleanup.push(function() {
6602
+ el.removeEventListener("click", handleClick);
6603
+ });
6604
+ this.instances.set(el, { cleanup });
6605
+ },
6606
+ morph: function(el) {
6607
+ if (!el) return;
6608
+ if (!this.instances.has(el)) this.initInstance(el);
6609
+ this._runMorph(el, null, null);
6610
+ },
6611
+ destroy: function(el) {
6612
+ const instance = this.instances.get(el);
6613
+ if (!instance) return;
6614
+ instance.cleanup.forEach(function(fn) {
6615
+ fn();
6616
+ });
6617
+ this.instances.delete(el);
6618
+ },
6619
+ destroyAll: function() {
6620
+ this.instances.forEach(function(_, el) {
6621
+ Morph.destroy(el);
6622
+ });
6623
+ },
6624
+ /* ── Internal helpers ── */
6625
+ _ensureLayers: function(el) {
6626
+ if (!el.querySelector(".vd-morph-wave")) {
6627
+ const wave = document.createElement("span");
6628
+ wave.className = "vd-morph-wave";
6629
+ wave.setAttribute("aria-hidden", "true");
6630
+ el.insertBefore(wave, el.firstChild);
6631
+ }
6632
+ if (!el.querySelector(".vd-morph-shine")) {
6633
+ const shine = document.createElement("span");
6634
+ shine.className = "vd-morph-shine";
6635
+ shine.setAttribute("aria-hidden", "true");
6636
+ const waveEl = el.querySelector(".vd-morph-wave");
6637
+ if (waveEl && waveEl.nextSibling) {
6638
+ el.insertBefore(shine, waveEl.nextSibling);
6639
+ } else {
6640
+ el.insertBefore(shine, el.firstChild);
6641
+ }
6642
+ }
6643
+ },
6644
+ _runMorph: function(el, pointerEvent, onComplete) {
6645
+ const wave = el.querySelector(".vd-morph-wave");
6646
+ if (wave) {
6647
+ const rect = el.getBoundingClientRect();
6648
+ const cx = rect.left + rect.width / 2;
6649
+ const cy = rect.top + rect.height / 2;
6650
+ const px = pointerEvent ? pointerEvent.clientX || cx : cx;
6651
+ const py = pointerEvent ? pointerEvent.clientY || cy : cy;
6652
+ wave.style.left = px - rect.left + "px";
6653
+ wave.style.top = py - rect.top + "px";
6654
+ }
6655
+ el.classList.add("is-morphing");
6656
+ let duration = MORPH_DURATION_MS;
6657
+ const custom = getComputedStyle(el).getPropertyValue("--morph-duration");
6658
+ if (custom) {
6659
+ const parsed = parseFloat(custom);
6660
+ if (!isNaN(parsed)) duration = parsed * (custom.indexOf("ms") !== -1 ? 1 : 1e3);
6661
+ }
6662
+ setTimeout(function() {
6663
+ el.classList.remove("is-morphing");
6664
+ const current = el.querySelector(".vd-morph-current");
6665
+ const next = el.querySelector(".vd-morph-next");
6666
+ if (current && next) {
6667
+ current.classList.remove("vd-morph-current");
6668
+ current.classList.add("vd-morph-next");
6669
+ next.classList.remove("vd-morph-next");
6670
+ next.classList.add("vd-morph-current");
6671
+ }
6672
+ el.classList.add("morph-done");
6673
+ setTimeout(function() {
6674
+ el.classList.remove("morph-done");
6675
+ }, 350);
6676
+ if (typeof onComplete === "function") onComplete();
6677
+ }, duration);
6678
+ }
6679
+ };
6680
+ if (typeof window.Vanduo !== "undefined") {
6681
+ window.Vanduo.register("morph", Morph);
6682
+ }
6683
+ window.VanduoMorph = Morph;
6684
+ })();
6685
+
6482
6686
  // js/components/flow.js
6483
6687
  (function() {
6484
6688
  "use strict";