@vanduo-oss/framework 1.3.4 → 1.3.7
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/LICENSE +0 -14
- package/README.md +34 -176
- package/css/components/cards.css +11 -1
- package/css/components/datepicker.css +10 -1
- package/css/components/expanding-cards.css +215 -0
- package/css/components/spotlight.css +8 -3
- package/css/components/timeline.css +47 -0
- package/css/components/tooltips.css +8 -8
- package/css/effects/morph.css +247 -0
- package/css/vanduo.css +2 -0
- package/dist/build-info.json +3 -3
- package/dist/vanduo.cjs.js +754 -59
- package/dist/vanduo.cjs.js.map +3 -3
- package/dist/vanduo.cjs.min.js +4 -4
- package/dist/vanduo.cjs.min.js.map +4 -4
- package/dist/vanduo.css +477 -14
- package/dist/vanduo.css.map +1 -1
- package/dist/vanduo.esm.js +754 -59
- package/dist/vanduo.esm.js.map +3 -3
- package/dist/vanduo.esm.min.js +4 -4
- package/dist/vanduo.esm.min.js.map +4 -4
- package/dist/vanduo.js +754 -59
- package/dist/vanduo.js.map +3 -3
- package/dist/vanduo.min.css +2 -2
- package/dist/vanduo.min.css.map +1 -1
- package/dist/vanduo.min.js +4 -4
- package/dist/vanduo.min.js.map +4 -4
- package/js/components/datepicker.js +392 -70
- package/js/components/expanding-cards.js +136 -0
- package/js/components/morph.js +134 -0
- package/js/components/timeline.js +226 -0
- package/js/index.js +4 -1
- package/package.json +1 -1
package/dist/vanduo.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Vanduo v1.3.
|
|
1
|
+
/*! Vanduo v1.3.7 | Built: 2026-04-18T12:05:32.603Z | git:20b2d08 | 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.
|
|
110
|
+
const VANDUO_VERSION = true ? "1.3.7" : "0.0.0-dev";
|
|
111
111
|
const Vanduo2 = {
|
|
112
112
|
version: VANDUO_VERSION,
|
|
113
113
|
components: {},
|
|
@@ -6572,6 +6572,410 @@
|
|
|
6572
6572
|
window.VanduoGlassScroll = GlassScroll;
|
|
6573
6573
|
})();
|
|
6574
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
|
+
if (typeof onComplete === "function") onComplete();
|
|
6673
|
+
}, duration);
|
|
6674
|
+
}
|
|
6675
|
+
};
|
|
6676
|
+
if (typeof window.Vanduo !== "undefined") {
|
|
6677
|
+
window.Vanduo.register("morph", Morph);
|
|
6678
|
+
}
|
|
6679
|
+
window.VanduoMorph = Morph;
|
|
6680
|
+
})();
|
|
6681
|
+
|
|
6682
|
+
// js/components/expanding-cards.js
|
|
6683
|
+
(function() {
|
|
6684
|
+
"use strict";
|
|
6685
|
+
const ExpandingCards = {
|
|
6686
|
+
instances: /* @__PURE__ */ new Map(),
|
|
6687
|
+
init: function() {
|
|
6688
|
+
document.querySelectorAll(".vd-expanding-cards").forEach(function(el) {
|
|
6689
|
+
if (el.getAttribute("data-vd-expanding-cards") === "manual") return;
|
|
6690
|
+
if (ExpandingCards.instances.has(el)) return;
|
|
6691
|
+
ExpandingCards.initContainer(el);
|
|
6692
|
+
});
|
|
6693
|
+
},
|
|
6694
|
+
initContainer: function(container) {
|
|
6695
|
+
const cleanup = [];
|
|
6696
|
+
const getCards = function() {
|
|
6697
|
+
return Array.prototype.slice.call(container.querySelectorAll(".vd-expanding-card"));
|
|
6698
|
+
};
|
|
6699
|
+
const setActive = function(card) {
|
|
6700
|
+
const cards = getCards();
|
|
6701
|
+
if (!card || cards.indexOf(card) === -1) return;
|
|
6702
|
+
cards.forEach(function(c) {
|
|
6703
|
+
c.classList.toggle("is-active", c === card);
|
|
6704
|
+
});
|
|
6705
|
+
card.focus({ preventScroll: true });
|
|
6706
|
+
};
|
|
6707
|
+
const onClick = function(e) {
|
|
6708
|
+
const t = e.target;
|
|
6709
|
+
const card = t.closest ? t.closest(".vd-expanding-card") : null;
|
|
6710
|
+
if (!card || !container.contains(card)) return;
|
|
6711
|
+
setActive(card);
|
|
6712
|
+
};
|
|
6713
|
+
const onKeydown = function(e) {
|
|
6714
|
+
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight" && e.key !== "Home" && e.key !== "End") {
|
|
6715
|
+
return;
|
|
6716
|
+
}
|
|
6717
|
+
const cards = getCards().filter(function(c) {
|
|
6718
|
+
return c.offsetParent !== null || c.getClientRects().length > 0;
|
|
6719
|
+
});
|
|
6720
|
+
if (!cards.length) return;
|
|
6721
|
+
const activeEl = document.activeElement;
|
|
6722
|
+
let idx = cards.indexOf(activeEl);
|
|
6723
|
+
if (idx < 0) {
|
|
6724
|
+
idx = cards.findIndex(function(c) {
|
|
6725
|
+
return c.classList.contains("is-active");
|
|
6726
|
+
});
|
|
6727
|
+
}
|
|
6728
|
+
if (idx < 0) idx = 0;
|
|
6729
|
+
if (e.key === "ArrowLeft") {
|
|
6730
|
+
e.preventDefault();
|
|
6731
|
+
setActive(cards[Math.max(0, idx - 1)]);
|
|
6732
|
+
} else if (e.key === "ArrowRight") {
|
|
6733
|
+
e.preventDefault();
|
|
6734
|
+
setActive(cards[Math.min(cards.length - 1, idx + 1)]);
|
|
6735
|
+
} else if (e.key === "Home") {
|
|
6736
|
+
e.preventDefault();
|
|
6737
|
+
setActive(cards[0]);
|
|
6738
|
+
} else if (e.key === "End") {
|
|
6739
|
+
e.preventDefault();
|
|
6740
|
+
setActive(cards[cards.length - 1]);
|
|
6741
|
+
}
|
|
6742
|
+
};
|
|
6743
|
+
container.addEventListener("click", onClick);
|
|
6744
|
+
cleanup.push(function() {
|
|
6745
|
+
container.removeEventListener("click", onClick);
|
|
6746
|
+
});
|
|
6747
|
+
container.addEventListener("keydown", onKeydown);
|
|
6748
|
+
cleanup.push(function() {
|
|
6749
|
+
container.removeEventListener("keydown", onKeydown);
|
|
6750
|
+
});
|
|
6751
|
+
getCards().forEach(function(card) {
|
|
6752
|
+
if (!card.hasAttribute("tabindex")) {
|
|
6753
|
+
card.setAttribute("tabindex", "0");
|
|
6754
|
+
}
|
|
6755
|
+
card.setAttribute("role", "button");
|
|
6756
|
+
if (!card.hasAttribute("aria-pressed")) {
|
|
6757
|
+
card.setAttribute("aria-pressed", card.classList.contains("is-active") ? "true" : "false");
|
|
6758
|
+
}
|
|
6759
|
+
});
|
|
6760
|
+
const syncAria = function() {
|
|
6761
|
+
getCards().forEach(function(card) {
|
|
6762
|
+
card.setAttribute("aria-pressed", card.classList.contains("is-active") ? "true" : "false");
|
|
6763
|
+
});
|
|
6764
|
+
};
|
|
6765
|
+
const mo = new MutationObserver(syncAria);
|
|
6766
|
+
mo.observe(container, { attributes: true, subtree: true, attributeFilter: ["class"] });
|
|
6767
|
+
cleanup.push(function() {
|
|
6768
|
+
mo.disconnect();
|
|
6769
|
+
});
|
|
6770
|
+
syncAria();
|
|
6771
|
+
ExpandingCards.instances.set(container, { cleanup });
|
|
6772
|
+
},
|
|
6773
|
+
destroy: function(container) {
|
|
6774
|
+
const inst = this.instances.get(container);
|
|
6775
|
+
if (!inst) return;
|
|
6776
|
+
inst.cleanup.forEach(function(fn) {
|
|
6777
|
+
fn();
|
|
6778
|
+
});
|
|
6779
|
+
this.instances.delete(container);
|
|
6780
|
+
},
|
|
6781
|
+
destroyAll: function() {
|
|
6782
|
+
this.instances.forEach(function(_, el) {
|
|
6783
|
+
ExpandingCards.destroy(el);
|
|
6784
|
+
});
|
|
6785
|
+
}
|
|
6786
|
+
};
|
|
6787
|
+
if (typeof window.Vanduo !== "undefined") {
|
|
6788
|
+
window.Vanduo.register("expandingCards", ExpandingCards);
|
|
6789
|
+
}
|
|
6790
|
+
window.VanduoExpandingCards = ExpandingCards;
|
|
6791
|
+
})();
|
|
6792
|
+
|
|
6793
|
+
// js/components/timeline.js
|
|
6794
|
+
(function() {
|
|
6795
|
+
"use strict";
|
|
6796
|
+
const STAGGER_MS = 140;
|
|
6797
|
+
const MAX_STAGGER_INDEX = 7;
|
|
6798
|
+
const PLAY_INTERVAL_MS = 800;
|
|
6799
|
+
function countRevealedPrefix(items) {
|
|
6800
|
+
let count = 0;
|
|
6801
|
+
for (let i = 0; i < items.length; i++) {
|
|
6802
|
+
if (!items[i].classList.contains("is-revealed")) break;
|
|
6803
|
+
count++;
|
|
6804
|
+
}
|
|
6805
|
+
return count;
|
|
6806
|
+
}
|
|
6807
|
+
function findPlaybackControls(container) {
|
|
6808
|
+
return container.parentElement || document.body;
|
|
6809
|
+
}
|
|
6810
|
+
function initPlayback(container, items, cleanup) {
|
|
6811
|
+
items.forEach(function(item) {
|
|
6812
|
+
item.classList.remove("is-revealed");
|
|
6813
|
+
});
|
|
6814
|
+
const scope = findPlaybackControls(container);
|
|
6815
|
+
const prevBtn = scope.querySelector("[data-vd-timeline-prev]");
|
|
6816
|
+
const nextBtn = scope.querySelector("[data-vd-timeline-next]");
|
|
6817
|
+
const playBtn = scope.querySelector("[data-vd-timeline-play]");
|
|
6818
|
+
const pauseBtn = scope.querySelector("[data-vd-timeline-pause]");
|
|
6819
|
+
let playTimer = null;
|
|
6820
|
+
function updateNavButtons() {
|
|
6821
|
+
const k = countRevealedPrefix(items);
|
|
6822
|
+
const n = items.length;
|
|
6823
|
+
if (prevBtn) {
|
|
6824
|
+
const atStart = k === 0;
|
|
6825
|
+
prevBtn.disabled = atStart;
|
|
6826
|
+
prevBtn.setAttribute("aria-disabled", atStart ? "true" : "false");
|
|
6827
|
+
}
|
|
6828
|
+
if (nextBtn) {
|
|
6829
|
+
const atEnd = k >= n;
|
|
6830
|
+
nextBtn.disabled = atEnd;
|
|
6831
|
+
nextBtn.setAttribute("aria-disabled", atEnd ? "true" : "false");
|
|
6832
|
+
}
|
|
6833
|
+
if (playBtn) {
|
|
6834
|
+
playBtn.setAttribute("aria-pressed", playTimer ? "true" : "false");
|
|
6835
|
+
}
|
|
6836
|
+
if (pauseBtn) {
|
|
6837
|
+
pauseBtn.disabled = !playTimer;
|
|
6838
|
+
}
|
|
6839
|
+
}
|
|
6840
|
+
function stepNext() {
|
|
6841
|
+
const k = countRevealedPrefix(items);
|
|
6842
|
+
if (k < items.length) {
|
|
6843
|
+
items[k].classList.add("is-revealed");
|
|
6844
|
+
}
|
|
6845
|
+
updateNavButtons();
|
|
6846
|
+
}
|
|
6847
|
+
function stepPrev() {
|
|
6848
|
+
const k = countRevealedPrefix(items);
|
|
6849
|
+
if (k > 0) {
|
|
6850
|
+
items[k - 1].classList.remove("is-revealed");
|
|
6851
|
+
}
|
|
6852
|
+
updateNavButtons();
|
|
6853
|
+
}
|
|
6854
|
+
function play() {
|
|
6855
|
+
if (playTimer) return;
|
|
6856
|
+
playTimer = setInterval(function() {
|
|
6857
|
+
if (countRevealedPrefix(items) >= items.length) {
|
|
6858
|
+
pause();
|
|
6859
|
+
return;
|
|
6860
|
+
}
|
|
6861
|
+
stepNext();
|
|
6862
|
+
}, PLAY_INTERVAL_MS);
|
|
6863
|
+
updateNavButtons();
|
|
6864
|
+
}
|
|
6865
|
+
function pause() {
|
|
6866
|
+
if (playTimer) {
|
|
6867
|
+
clearInterval(playTimer);
|
|
6868
|
+
playTimer = null;
|
|
6869
|
+
}
|
|
6870
|
+
updateNavButtons();
|
|
6871
|
+
}
|
|
6872
|
+
function addClick(el, fn) {
|
|
6873
|
+
if (!el) return;
|
|
6874
|
+
const handler = function(e) {
|
|
6875
|
+
e.preventDefault();
|
|
6876
|
+
fn();
|
|
6877
|
+
};
|
|
6878
|
+
el.addEventListener("click", handler);
|
|
6879
|
+
cleanup.push(function() {
|
|
6880
|
+
el.removeEventListener("click", handler);
|
|
6881
|
+
});
|
|
6882
|
+
}
|
|
6883
|
+
addClick(prevBtn, stepPrev);
|
|
6884
|
+
addClick(nextBtn, stepNext);
|
|
6885
|
+
addClick(playBtn, play);
|
|
6886
|
+
addClick(pauseBtn, pause);
|
|
6887
|
+
cleanup.push(function() {
|
|
6888
|
+
pause();
|
|
6889
|
+
});
|
|
6890
|
+
updateNavButtons();
|
|
6891
|
+
return {
|
|
6892
|
+
stepNext,
|
|
6893
|
+
stepPrev,
|
|
6894
|
+
play,
|
|
6895
|
+
pause
|
|
6896
|
+
};
|
|
6897
|
+
}
|
|
6898
|
+
const Timeline = {
|
|
6899
|
+
instances: /* @__PURE__ */ new Map(),
|
|
6900
|
+
init: function() {
|
|
6901
|
+
document.querySelectorAll(".vd-timeline.vd-timeline-animated").forEach(function(el) {
|
|
6902
|
+
if (Timeline.instances.has(el)) return;
|
|
6903
|
+
Timeline.initInstance(el);
|
|
6904
|
+
});
|
|
6905
|
+
},
|
|
6906
|
+
reinit: function() {
|
|
6907
|
+
Timeline.destroyAll();
|
|
6908
|
+
Timeline.init();
|
|
6909
|
+
},
|
|
6910
|
+
initInstance: function(container) {
|
|
6911
|
+
const cleanup = [];
|
|
6912
|
+
const items = Array.prototype.filter.call(container.children, function(child) {
|
|
6913
|
+
return child.classList && child.classList.contains("vd-timeline-item");
|
|
6914
|
+
});
|
|
6915
|
+
items.forEach(function(item, i) {
|
|
6916
|
+
const idx = Math.min(i, MAX_STAGGER_INDEX);
|
|
6917
|
+
item.style.setProperty("--vd-timeline-reveal-delay", idx * STAGGER_MS + "ms");
|
|
6918
|
+
});
|
|
6919
|
+
const reducedMotion = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
6920
|
+
if (reducedMotion) {
|
|
6921
|
+
items.forEach(function(item) {
|
|
6922
|
+
item.classList.add("is-revealed");
|
|
6923
|
+
});
|
|
6924
|
+
Timeline.instances.set(container, { cleanup });
|
|
6925
|
+
return;
|
|
6926
|
+
}
|
|
6927
|
+
const playback = container.classList && container.classList.contains("vd-timeline-playback");
|
|
6928
|
+
if (playback) {
|
|
6929
|
+
const playbackApi = initPlayback(container, items, cleanup);
|
|
6930
|
+
Timeline.instances.set(container, { cleanup, playback: playbackApi });
|
|
6931
|
+
return;
|
|
6932
|
+
}
|
|
6933
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
6934
|
+
items.forEach(function(item) {
|
|
6935
|
+
item.classList.add("is-revealed");
|
|
6936
|
+
});
|
|
6937
|
+
Timeline.instances.set(container, { cleanup });
|
|
6938
|
+
return;
|
|
6939
|
+
}
|
|
6940
|
+
const observer = new IntersectionObserver(function(entries) {
|
|
6941
|
+
entries.forEach(function(entry) {
|
|
6942
|
+
if (!entry.isIntersecting) return;
|
|
6943
|
+
entry.target.classList.add("is-revealed");
|
|
6944
|
+
observer.unobserve(entry.target);
|
|
6945
|
+
});
|
|
6946
|
+
}, {
|
|
6947
|
+
root: null,
|
|
6948
|
+
rootMargin: "0px 0px -10% 0px",
|
|
6949
|
+
threshold: 0.15
|
|
6950
|
+
});
|
|
6951
|
+
items.forEach(function(item) {
|
|
6952
|
+
observer.observe(item);
|
|
6953
|
+
});
|
|
6954
|
+
cleanup.push(function() {
|
|
6955
|
+
observer.disconnect();
|
|
6956
|
+
});
|
|
6957
|
+
Timeline.instances.set(container, { cleanup });
|
|
6958
|
+
},
|
|
6959
|
+
destroy: function(container) {
|
|
6960
|
+
const inst = this.instances.get(container);
|
|
6961
|
+
if (!inst) return;
|
|
6962
|
+
inst.cleanup.forEach(function(fn) {
|
|
6963
|
+
fn();
|
|
6964
|
+
});
|
|
6965
|
+
this.instances.delete(container);
|
|
6966
|
+
},
|
|
6967
|
+
destroyAll: function() {
|
|
6968
|
+
this.instances.forEach(function(_, el) {
|
|
6969
|
+
Timeline.destroy(el);
|
|
6970
|
+
});
|
|
6971
|
+
}
|
|
6972
|
+
};
|
|
6973
|
+
if (typeof window.Vanduo !== "undefined") {
|
|
6974
|
+
window.Vanduo.register("timeline", Timeline);
|
|
6975
|
+
}
|
|
6976
|
+
window.VanduoTimeline = Timeline;
|
|
6977
|
+
})();
|
|
6978
|
+
|
|
6575
6979
|
// js/components/flow.js
|
|
6576
6980
|
(function() {
|
|
6577
6981
|
"use strict";
|
|
@@ -7588,6 +7992,115 @@
|
|
|
7588
7992
|
"use strict";
|
|
7589
7993
|
const DAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
7590
7994
|
const MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
7995
|
+
function escapeRegexChar(c) {
|
|
7996
|
+
return c.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7997
|
+
}
|
|
7998
|
+
function buildParseFormat(format) {
|
|
7999
|
+
let regex = "^";
|
|
8000
|
+
const order = [];
|
|
8001
|
+
let i = 0;
|
|
8002
|
+
while (i < format.length) {
|
|
8003
|
+
const slice = format.slice(i);
|
|
8004
|
+
if (slice.toLowerCase().startsWith("yyyy")) {
|
|
8005
|
+
regex += "(\\d{4})";
|
|
8006
|
+
order.push("y");
|
|
8007
|
+
i += 4;
|
|
8008
|
+
} else if (slice.toLowerCase().startsWith("mm")) {
|
|
8009
|
+
regex += "(\\d{2})";
|
|
8010
|
+
order.push("m");
|
|
8011
|
+
i += 2;
|
|
8012
|
+
} else if (slice.toLowerCase().startsWith("dd")) {
|
|
8013
|
+
regex += "(\\d{2})";
|
|
8014
|
+
order.push("d");
|
|
8015
|
+
i += 2;
|
|
8016
|
+
} else {
|
|
8017
|
+
regex += escapeRegexChar(format[i]);
|
|
8018
|
+
i++;
|
|
8019
|
+
}
|
|
8020
|
+
}
|
|
8021
|
+
regex += "$";
|
|
8022
|
+
return { regex: new RegExp(regex), order };
|
|
8023
|
+
}
|
|
8024
|
+
function parseDateFromFormat(value, format) {
|
|
8025
|
+
if (!value || !format) return null;
|
|
8026
|
+
const { regex, order } = buildParseFormat(format);
|
|
8027
|
+
const m = value.trim().match(regex);
|
|
8028
|
+
if (!m) return null;
|
|
8029
|
+
let y;
|
|
8030
|
+
let mo;
|
|
8031
|
+
let d;
|
|
8032
|
+
let ci = 1;
|
|
8033
|
+
for (let k = 0; k < order.length; k++) {
|
|
8034
|
+
const part = order[k];
|
|
8035
|
+
const v = parseInt(m[ci++], 10);
|
|
8036
|
+
if (Number.isNaN(v)) return null;
|
|
8037
|
+
if (part === "y") y = v;
|
|
8038
|
+
else if (part === "m") mo = v - 1;
|
|
8039
|
+
else if (part === "d") d = v;
|
|
8040
|
+
}
|
|
8041
|
+
if (y === void 0 || mo === void 0 || d === void 0) return null;
|
|
8042
|
+
const dt = new Date(y, mo, d);
|
|
8043
|
+
if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== d) return null;
|
|
8044
|
+
return dt;
|
|
8045
|
+
}
|
|
8046
|
+
function formatDate(d, format) {
|
|
8047
|
+
const yyyy = String(d.getFullYear());
|
|
8048
|
+
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
8049
|
+
const dd = String(d.getDate()).padStart(2, "0");
|
|
8050
|
+
let out = "";
|
|
8051
|
+
let i = 0;
|
|
8052
|
+
while (i < format.length) {
|
|
8053
|
+
const slice = format.slice(i);
|
|
8054
|
+
if (slice.toLowerCase().startsWith("yyyy")) {
|
|
8055
|
+
out += yyyy;
|
|
8056
|
+
i += 4;
|
|
8057
|
+
} else if (slice.toLowerCase().startsWith("mm")) {
|
|
8058
|
+
out += mm;
|
|
8059
|
+
i += 2;
|
|
8060
|
+
} else if (slice.toLowerCase().startsWith("dd")) {
|
|
8061
|
+
out += dd;
|
|
8062
|
+
i += 2;
|
|
8063
|
+
} else {
|
|
8064
|
+
out += format[i];
|
|
8065
|
+
i++;
|
|
8066
|
+
}
|
|
8067
|
+
}
|
|
8068
|
+
return out;
|
|
8069
|
+
}
|
|
8070
|
+
function dateKey(d) {
|
|
8071
|
+
return d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0") + "-" + String(d.getDate()).padStart(2, "0");
|
|
8072
|
+
}
|
|
8073
|
+
function addDays(d, n) {
|
|
8074
|
+
const x = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
8075
|
+
x.setDate(x.getDate() + n);
|
|
8076
|
+
return x;
|
|
8077
|
+
}
|
|
8078
|
+
function addMonthsClamped(d, n) {
|
|
8079
|
+
return new Date(d.getFullYear(), d.getMonth() + n, d.getDate());
|
|
8080
|
+
}
|
|
8081
|
+
function parseYmdLocal(ymd) {
|
|
8082
|
+
if (!ymd || typeof ymd !== "string") return null;
|
|
8083
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(ymd.trim());
|
|
8084
|
+
if (!m) return null;
|
|
8085
|
+
const y = +m[1];
|
|
8086
|
+
const mo = +m[2] - 1;
|
|
8087
|
+
const day = +m[3];
|
|
8088
|
+
const dt = new Date(y, mo, day);
|
|
8089
|
+
if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== day) return null;
|
|
8090
|
+
return dt;
|
|
8091
|
+
}
|
|
8092
|
+
function startOfWeekSunday(d) {
|
|
8093
|
+
const x = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
8094
|
+
const day = x.getDay();
|
|
8095
|
+
x.setDate(x.getDate() - day);
|
|
8096
|
+
return x;
|
|
8097
|
+
}
|
|
8098
|
+
function endOfWeekSunday(d) {
|
|
8099
|
+
const x = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
8100
|
+
const day = x.getDay();
|
|
8101
|
+
x.setDate(x.getDate() + (6 - day));
|
|
8102
|
+
return x;
|
|
8103
|
+
}
|
|
7591
8104
|
const Datepicker = {
|
|
7592
8105
|
instances: /* @__PURE__ */ new Map(),
|
|
7593
8106
|
init: function() {
|
|
@@ -7599,28 +8112,70 @@
|
|
|
7599
8112
|
},
|
|
7600
8113
|
initInstance: function(input) {
|
|
7601
8114
|
const cleanup = [];
|
|
7602
|
-
const format = input.getAttribute("data-vd-datepicker-format") || "
|
|
8115
|
+
const format = input.getAttribute("data-vd-datepicker-format") || "YYYY-MM-DD";
|
|
7603
8116
|
const minStr = input.getAttribute("data-vd-datepicker-min");
|
|
7604
8117
|
const maxStr = input.getAttribute("data-vd-datepicker-max");
|
|
7605
|
-
const minDate = minStr ?
|
|
7606
|
-
const maxDate = maxStr ?
|
|
8118
|
+
const minDate = minStr ? parseYmdLocal(minStr) : null;
|
|
8119
|
+
const maxDate = maxStr ? parseYmdLocal(maxStr) : null;
|
|
7607
8120
|
const today = /* @__PURE__ */ new Date();
|
|
7608
8121
|
let viewYear = today.getFullYear();
|
|
7609
8122
|
let viewMonth = today.getMonth();
|
|
7610
8123
|
let selectedDate = null;
|
|
7611
8124
|
let viewMode = "days";
|
|
8125
|
+
let focusedDate = null;
|
|
8126
|
+
let skipNextFocusOpen = false;
|
|
8127
|
+
const isDisabled = (d) => {
|
|
8128
|
+
if (minDate) {
|
|
8129
|
+
const t = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
8130
|
+
if (t < minDate.getTime()) return true;
|
|
8131
|
+
}
|
|
8132
|
+
if (maxDate) {
|
|
8133
|
+
const t = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
8134
|
+
if (t > maxDate.getTime()) return true;
|
|
8135
|
+
}
|
|
8136
|
+
return false;
|
|
8137
|
+
};
|
|
8138
|
+
const ensureMonthInRange = (y, m) => {
|
|
8139
|
+
if (!minDate && !maxDate) return { y, m };
|
|
8140
|
+
const first = new Date(y, m, 1);
|
|
8141
|
+
const last = new Date(y, m + 1, 0);
|
|
8142
|
+
if (minDate && last.getTime() < minDate.getTime()) {
|
|
8143
|
+
return { y: minDate.getFullYear(), m: minDate.getMonth() };
|
|
8144
|
+
}
|
|
8145
|
+
if (maxDate && first.getTime() > maxDate.getTime()) {
|
|
8146
|
+
return { y: maxDate.getFullYear(), m: maxDate.getMonth() };
|
|
8147
|
+
}
|
|
8148
|
+
return { y, m };
|
|
8149
|
+
};
|
|
8150
|
+
const firstSelectableInMonth = (y, m) => {
|
|
8151
|
+
const last = new Date(y, m + 1, 0).getDate();
|
|
8152
|
+
for (let day = 1; day <= last; day++) {
|
|
8153
|
+
const dt = new Date(y, m, day);
|
|
8154
|
+
if (!isDisabled(dt)) return dt;
|
|
8155
|
+
}
|
|
8156
|
+
return new Date(y, m, 1);
|
|
8157
|
+
};
|
|
7612
8158
|
if (input.value) {
|
|
7613
|
-
const
|
|
7614
|
-
|
|
8159
|
+
const trimmed = input.value.trim();
|
|
8160
|
+
let parsed = parseDateFromFormat(trimmed, format);
|
|
8161
|
+
if (!parsed) {
|
|
8162
|
+
const fallback = new Date(trimmed);
|
|
8163
|
+
if (!isNaN(fallback.getTime())) parsed = fallback;
|
|
8164
|
+
}
|
|
8165
|
+
if (parsed) {
|
|
7615
8166
|
selectedDate = parsed;
|
|
7616
8167
|
viewYear = parsed.getFullYear();
|
|
7617
8168
|
viewMonth = parsed.getMonth();
|
|
7618
8169
|
}
|
|
7619
8170
|
}
|
|
8171
|
+
const clampedInit = ensureMonthInRange(viewYear, viewMonth);
|
|
8172
|
+
viewYear = clampedInit.y;
|
|
8173
|
+
viewMonth = clampedInit.m;
|
|
7620
8174
|
const popup = document.createElement("div");
|
|
7621
8175
|
popup.className = "vd-datepicker-popup";
|
|
7622
8176
|
popup.setAttribute("role", "dialog");
|
|
7623
8177
|
popup.setAttribute("aria-label", "Choose date");
|
|
8178
|
+
popup.tabIndex = -1;
|
|
7624
8179
|
const wrapper = document.createElement("div");
|
|
7625
8180
|
wrapper.className = "vd-suggest-wrapper";
|
|
7626
8181
|
wrapper.style.position = "relative";
|
|
@@ -7628,18 +8183,80 @@
|
|
|
7628
8183
|
input.parentNode.insertBefore(wrapper, input);
|
|
7629
8184
|
wrapper.appendChild(input);
|
|
7630
8185
|
wrapper.appendChild(popup);
|
|
7631
|
-
const
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
8186
|
+
const isSameDay = (a, b) => a && b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
8187
|
+
const selectDate = (date) => {
|
|
8188
|
+
selectedDate = date;
|
|
8189
|
+
viewYear = date.getFullYear();
|
|
8190
|
+
viewMonth = date.getMonth();
|
|
8191
|
+
input.value = formatDate(date, format);
|
|
8192
|
+
skipNextFocusOpen = true;
|
|
8193
|
+
close();
|
|
8194
|
+
input.dispatchEvent(new CustomEvent("datepicker:select", {
|
|
8195
|
+
detail: { date, formatted: input.value },
|
|
8196
|
+
bubbles: true
|
|
8197
|
+
}));
|
|
8198
|
+
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
8199
|
+
input.focus();
|
|
7636
8200
|
};
|
|
7637
|
-
const
|
|
7638
|
-
if (
|
|
7639
|
-
|
|
7640
|
-
|
|
8201
|
+
const focusFocusedDay = () => {
|
|
8202
|
+
if (viewMode !== "days" || !focusedDate) return;
|
|
8203
|
+
const key = dateKey(focusedDate);
|
|
8204
|
+
const btn = popup.querySelector('[data-vd-date="' + key + '"]');
|
|
8205
|
+
if (btn && !btn.classList.contains("is-outside") && btn.getAttribute("aria-disabled") !== "true") {
|
|
8206
|
+
btn.focus();
|
|
8207
|
+
}
|
|
8208
|
+
};
|
|
8209
|
+
const skipDisabled = (d, stepDir, maxSteps) => {
|
|
8210
|
+
let x = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
8211
|
+
const step = stepDir > 0 ? 1 : -1;
|
|
8212
|
+
for (let i = 0; i < maxSteps; i++) {
|
|
8213
|
+
if (!isDisabled(x)) return x;
|
|
8214
|
+
x = addDays(x, step);
|
|
8215
|
+
}
|
|
8216
|
+
return d;
|
|
8217
|
+
};
|
|
8218
|
+
const createDayBtn = (day, outside, date) => {
|
|
8219
|
+
const btn = document.createElement("button");
|
|
8220
|
+
btn.type = "button";
|
|
8221
|
+
btn.className = "vd-datepicker-day";
|
|
8222
|
+
btn.textContent = day;
|
|
8223
|
+
btn.setAttribute("role", "gridcell");
|
|
8224
|
+
if (outside) {
|
|
8225
|
+
btn.classList.add("is-outside");
|
|
8226
|
+
btn.tabIndex = -1;
|
|
8227
|
+
btn.setAttribute("aria-disabled", "true");
|
|
8228
|
+
return btn;
|
|
8229
|
+
}
|
|
8230
|
+
btn.setAttribute("data-vd-date", dateKey(date));
|
|
8231
|
+
if (date && isSameDay(date, today)) btn.classList.add("is-today");
|
|
8232
|
+
if (date && isSameDay(date, selectedDate)) btn.classList.add("is-selected");
|
|
8233
|
+
if (date && isDisabled(date)) {
|
|
8234
|
+
btn.classList.add("is-disabled");
|
|
8235
|
+
btn.setAttribute("aria-disabled", "true");
|
|
8236
|
+
btn.tabIndex = -1;
|
|
8237
|
+
return btn;
|
|
8238
|
+
}
|
|
8239
|
+
if (date) {
|
|
8240
|
+
const isFocused = focusedDate && isSameDay(date, focusedDate);
|
|
8241
|
+
btn.tabIndex = isFocused ? 0 : -1;
|
|
8242
|
+
btn.addEventListener("click", () => {
|
|
8243
|
+
selectedDate = date;
|
|
8244
|
+
viewYear = date.getFullYear();
|
|
8245
|
+
viewMonth = date.getMonth();
|
|
8246
|
+
focusedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
8247
|
+
input.value = formatDate(date, format);
|
|
8248
|
+
skipNextFocusOpen = true;
|
|
8249
|
+
close();
|
|
8250
|
+
input.dispatchEvent(new CustomEvent("datepicker:select", {
|
|
8251
|
+
detail: { date, formatted: input.value },
|
|
8252
|
+
bubbles: true
|
|
8253
|
+
}));
|
|
8254
|
+
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
8255
|
+
input.focus();
|
|
8256
|
+
});
|
|
8257
|
+
}
|
|
8258
|
+
return btn;
|
|
7641
8259
|
};
|
|
7642
|
-
const isSameDay = (a, b) => a && b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
7643
8260
|
const render = () => {
|
|
7644
8261
|
popup.innerHTML = "";
|
|
7645
8262
|
const header = document.createElement("div");
|
|
@@ -7709,35 +8326,53 @@
|
|
|
7709
8326
|
header.appendChild(nextBtn);
|
|
7710
8327
|
popup.appendChild(header);
|
|
7711
8328
|
if (viewMode === "days") {
|
|
8329
|
+
const gridWrap = document.createElement("div");
|
|
8330
|
+
gridWrap.className = "vd-datepicker-grid";
|
|
8331
|
+
gridWrap.setAttribute("role", "grid");
|
|
8332
|
+
gridWrap.setAttribute("aria-label", "Calendar");
|
|
7712
8333
|
const weekdays = document.createElement("div");
|
|
7713
8334
|
weekdays.className = "vd-datepicker-weekdays";
|
|
7714
|
-
|
|
8335
|
+
weekdays.setAttribute("role", "row");
|
|
8336
|
+
DAYS.forEach(function(d) {
|
|
7715
8337
|
const span = document.createElement("span");
|
|
8338
|
+
span.setAttribute("role", "columnheader");
|
|
8339
|
+
span.setAttribute("aria-label", d);
|
|
7716
8340
|
span.textContent = d;
|
|
7717
8341
|
weekdays.appendChild(span);
|
|
7718
8342
|
});
|
|
7719
|
-
|
|
7720
|
-
const grid = document.createElement("div");
|
|
7721
|
-
grid.className = "vd-datepicker-days";
|
|
8343
|
+
gridWrap.appendChild(weekdays);
|
|
7722
8344
|
const firstDay = new Date(viewYear, viewMonth, 1).getDay();
|
|
7723
8345
|
const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();
|
|
7724
8346
|
const daysInPrev = new Date(viewYear, viewMonth, 0).getDate();
|
|
8347
|
+
const cells = [];
|
|
7725
8348
|
for (let i = firstDay - 1; i >= 0; i--) {
|
|
7726
|
-
const
|
|
7727
|
-
|
|
8349
|
+
const dayNum = daysInPrev - i;
|
|
8350
|
+
const prevMonth = viewMonth === 0 ? 11 : viewMonth - 1;
|
|
8351
|
+
const prevYear = viewMonth === 0 ? viewYear - 1 : viewYear;
|
|
8352
|
+
const date = new Date(prevYear, prevMonth, dayNum);
|
|
8353
|
+
cells.push({ day: dayNum, outside: true, date });
|
|
7728
8354
|
}
|
|
7729
8355
|
for (let d = 1; d <= daysInMonth; d++) {
|
|
7730
8356
|
const date = new Date(viewYear, viewMonth, d);
|
|
7731
|
-
|
|
7732
|
-
grid.appendChild(btn);
|
|
8357
|
+
cells.push({ day: d, outside: false, date });
|
|
7733
8358
|
}
|
|
7734
8359
|
const totalCells = firstDay + daysInMonth;
|
|
7735
8360
|
const remaining = totalCells % 7 === 0 ? 0 : 7 - totalCells % 7;
|
|
7736
8361
|
for (let i = 1; i <= remaining; i++) {
|
|
7737
|
-
const
|
|
7738
|
-
|
|
8362
|
+
const date = new Date(viewYear, viewMonth + 1, i);
|
|
8363
|
+
cells.push({ day: i, outside: true, date });
|
|
7739
8364
|
}
|
|
7740
|
-
|
|
8365
|
+
for (let r = 0; r < cells.length; r += 7) {
|
|
8366
|
+
const row = document.createElement("div");
|
|
8367
|
+
row.className = "vd-datepicker-row";
|
|
8368
|
+
row.setAttribute("role", "row");
|
|
8369
|
+
for (let c = 0; c < 7; c++) {
|
|
8370
|
+
const cell = cells[r + c];
|
|
8371
|
+
row.appendChild(createDayBtn(cell.day, cell.outside, cell.date));
|
|
8372
|
+
}
|
|
8373
|
+
gridWrap.appendChild(row);
|
|
8374
|
+
}
|
|
8375
|
+
popup.appendChild(gridWrap);
|
|
7741
8376
|
} else if (viewMode === "months") {
|
|
7742
8377
|
const grid = document.createElement("div");
|
|
7743
8378
|
grid.className = "vd-datepicker-months";
|
|
@@ -7778,65 +8413,125 @@
|
|
|
7778
8413
|
popup.appendChild(grid);
|
|
7779
8414
|
}
|
|
7780
8415
|
};
|
|
7781
|
-
const
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
if (
|
|
7787
|
-
|
|
7788
|
-
btn.tabIndex = -1;
|
|
7789
|
-
return btn;
|
|
8416
|
+
const handleGridKeydown = (e) => {
|
|
8417
|
+
if (!popup.classList.contains("is-open") || viewMode !== "days") return;
|
|
8418
|
+
const grid = popup.querySelector(".vd-datepicker-grid");
|
|
8419
|
+
if (!grid || !grid.contains(e.target)) return;
|
|
8420
|
+
const key = e.key;
|
|
8421
|
+
if (key !== "ArrowLeft" && key !== "ArrowRight" && key !== "ArrowUp" && key !== "ArrowDown" && key !== "Home" && key !== "End" && key !== "PageUp" && key !== "PageDown" && key !== "Enter" && key !== " " && key !== "Escape") {
|
|
8422
|
+
return;
|
|
7790
8423
|
}
|
|
7791
|
-
if (
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
8424
|
+
if (key === "Escape") {
|
|
8425
|
+
e.preventDefault();
|
|
8426
|
+
e.stopPropagation();
|
|
8427
|
+
skipNextFocusOpen = true;
|
|
8428
|
+
close();
|
|
8429
|
+
input.focus();
|
|
8430
|
+
return;
|
|
7796
8431
|
}
|
|
7797
|
-
if (
|
|
7798
|
-
|
|
7799
|
-
selectedDate = date;
|
|
7800
|
-
viewYear = date.getFullYear();
|
|
7801
|
-
viewMonth = date.getMonth();
|
|
7802
|
-
input.value = formatDate(date);
|
|
7803
|
-
close();
|
|
7804
|
-
input.dispatchEvent(new CustomEvent("datepicker:select", {
|
|
7805
|
-
detail: { date, formatted: input.value },
|
|
7806
|
-
bubbles: true
|
|
7807
|
-
}));
|
|
7808
|
-
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
7809
|
-
});
|
|
8432
|
+
if (!focusedDate) {
|
|
8433
|
+
focusedDate = firstSelectableInMonth(viewYear, viewMonth);
|
|
7810
8434
|
}
|
|
7811
|
-
|
|
8435
|
+
if (key === "Enter" || key === " ") {
|
|
8436
|
+
e.preventDefault();
|
|
8437
|
+
if (focusedDate && !isDisabled(focusedDate)) {
|
|
8438
|
+
selectDate(new Date(focusedDate.getFullYear(), focusedDate.getMonth(), focusedDate.getDate()));
|
|
8439
|
+
}
|
|
8440
|
+
return;
|
|
8441
|
+
}
|
|
8442
|
+
e.preventDefault();
|
|
8443
|
+
let next = new Date(focusedDate.getFullYear(), focusedDate.getMonth(), focusedDate.getDate());
|
|
8444
|
+
let skipDir = 1;
|
|
8445
|
+
if (key === "ArrowLeft") {
|
|
8446
|
+
next = addDays(next, -1);
|
|
8447
|
+
skipDir = -1;
|
|
8448
|
+
} else if (key === "ArrowRight") {
|
|
8449
|
+
next = addDays(next, 1);
|
|
8450
|
+
skipDir = 1;
|
|
8451
|
+
} else if (key === "ArrowUp") {
|
|
8452
|
+
next = addDays(next, -7);
|
|
8453
|
+
skipDir = -1;
|
|
8454
|
+
} else if (key === "ArrowDown") {
|
|
8455
|
+
next = addDays(next, 7);
|
|
8456
|
+
skipDir = 1;
|
|
8457
|
+
} else if (key === "Home") {
|
|
8458
|
+
next = startOfWeekSunday(next);
|
|
8459
|
+
skipDir = 1;
|
|
8460
|
+
} else if (key === "End") {
|
|
8461
|
+
next = endOfWeekSunday(next);
|
|
8462
|
+
skipDir = -1;
|
|
8463
|
+
} else if (key === "PageUp") {
|
|
8464
|
+
next = addMonthsClamped(next, -1);
|
|
8465
|
+
skipDir = -1;
|
|
8466
|
+
} else if (key === "PageDown") {
|
|
8467
|
+
next = addMonthsClamped(next, 1);
|
|
8468
|
+
skipDir = 1;
|
|
8469
|
+
}
|
|
8470
|
+
next = skipDisabled(next, skipDir, 400);
|
|
8471
|
+
if (next.getMonth() !== viewMonth || next.getFullYear() !== viewYear) {
|
|
8472
|
+
viewYear = next.getFullYear();
|
|
8473
|
+
viewMonth = next.getMonth();
|
|
8474
|
+
const cl = ensureMonthInRange(viewYear, viewMonth);
|
|
8475
|
+
viewYear = cl.y;
|
|
8476
|
+
viewMonth = cl.m;
|
|
8477
|
+
}
|
|
8478
|
+
focusedDate = next;
|
|
8479
|
+
render();
|
|
8480
|
+
requestAnimationFrame(focusFocusedDay);
|
|
7812
8481
|
};
|
|
7813
8482
|
const open = () => {
|
|
8483
|
+
viewMode = "days";
|
|
8484
|
+
if (selectedDate) {
|
|
8485
|
+
viewYear = selectedDate.getFullYear();
|
|
8486
|
+
viewMonth = selectedDate.getMonth();
|
|
8487
|
+
}
|
|
8488
|
+
const cl = ensureMonthInRange(viewYear, viewMonth);
|
|
8489
|
+
viewYear = cl.y;
|
|
8490
|
+
viewMonth = cl.m;
|
|
8491
|
+
if (selectedDate) {
|
|
8492
|
+
focusedDate = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate());
|
|
8493
|
+
} else {
|
|
8494
|
+
focusedDate = firstSelectableInMonth(viewYear, viewMonth);
|
|
8495
|
+
}
|
|
7814
8496
|
render();
|
|
7815
8497
|
popup.classList.add("is-open");
|
|
7816
8498
|
input.setAttribute("aria-expanded", "true");
|
|
8499
|
+
requestAnimationFrame(focusFocusedDay);
|
|
7817
8500
|
};
|
|
7818
8501
|
const close = () => {
|
|
7819
8502
|
popup.classList.remove("is-open");
|
|
7820
8503
|
input.setAttribute("aria-expanded", "false");
|
|
7821
8504
|
viewMode = "days";
|
|
7822
8505
|
};
|
|
7823
|
-
const focusHandler = () =>
|
|
8506
|
+
const focusHandler = () => {
|
|
8507
|
+
if (skipNextFocusOpen) {
|
|
8508
|
+
skipNextFocusOpen = false;
|
|
8509
|
+
return;
|
|
8510
|
+
}
|
|
8511
|
+
open();
|
|
8512
|
+
};
|
|
7824
8513
|
const outsideHandler = (e) => {
|
|
7825
8514
|
if (!wrapper.contains(e.target)) close();
|
|
7826
8515
|
};
|
|
7827
8516
|
const escHandler = (e) => {
|
|
7828
|
-
if (e.key === "Escape"
|
|
8517
|
+
if (e.key === "Escape" && popup.classList.contains("is-open")) {
|
|
8518
|
+
skipNextFocusOpen = true;
|
|
8519
|
+
close();
|
|
8520
|
+
input.focus();
|
|
8521
|
+
}
|
|
7829
8522
|
};
|
|
7830
8523
|
input.addEventListener("focus", focusHandler);
|
|
7831
8524
|
document.addEventListener("click", outsideHandler, true);
|
|
7832
8525
|
document.addEventListener("keydown", escHandler);
|
|
8526
|
+
popup.addEventListener("keydown", handleGridKeydown);
|
|
7833
8527
|
input.setAttribute("aria-haspopup", "dialog");
|
|
7834
8528
|
input.setAttribute("aria-expanded", "false");
|
|
7835
8529
|
input.setAttribute("autocomplete", "off");
|
|
7836
8530
|
cleanup.push(
|
|
7837
8531
|
() => input.removeEventListener("focus", focusHandler),
|
|
7838
8532
|
() => document.removeEventListener("click", outsideHandler, true),
|
|
7839
|
-
() => document.removeEventListener("keydown", escHandler)
|
|
8533
|
+
() => document.removeEventListener("keydown", escHandler),
|
|
8534
|
+
() => popup.removeEventListener("keydown", handleGridKeydown)
|
|
7840
8535
|
);
|
|
7841
8536
|
this.instances.set(input, { cleanup, open, close, popup });
|
|
7842
8537
|
},
|