@vanduo-oss/framework 1.3.5 → 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/README.md +5 -4
- 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/effects/morph.css +0 -12
- package/css/vanduo.css +1 -0
- package/dist/build-info.json +3 -3
- package/dist/vanduo.cjs.js +647 -63
- 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 +255 -24
- package/dist/vanduo.css.map +1 -1
- package/dist/vanduo.esm.js +647 -63
- 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 +647 -63
- 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 +0 -3
- package/js/components/timeline.js +226 -0
- package/js/index.js +2 -0
- package/package.json +1 -1
package/dist/vanduo.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: {},
|
|
@@ -6669,10 +6669,6 @@
|
|
|
6669
6669
|
next.classList.remove("vd-morph-next");
|
|
6670
6670
|
next.classList.add("vd-morph-current");
|
|
6671
6671
|
}
|
|
6672
|
-
el.classList.add("morph-done");
|
|
6673
|
-
setTimeout(function() {
|
|
6674
|
-
el.classList.remove("morph-done");
|
|
6675
|
-
}, 350);
|
|
6676
6672
|
if (typeof onComplete === "function") onComplete();
|
|
6677
6673
|
}, duration);
|
|
6678
6674
|
}
|
|
@@ -6683,6 +6679,303 @@
|
|
|
6683
6679
|
window.VanduoMorph = Morph;
|
|
6684
6680
|
})();
|
|
6685
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
|
+
|
|
6686
6979
|
// js/components/flow.js
|
|
6687
6980
|
(function() {
|
|
6688
6981
|
"use strict";
|
|
@@ -7699,6 +7992,115 @@
|
|
|
7699
7992
|
"use strict";
|
|
7700
7993
|
const DAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
7701
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
|
+
}
|
|
7702
8104
|
const Datepicker = {
|
|
7703
8105
|
instances: /* @__PURE__ */ new Map(),
|
|
7704
8106
|
init: function() {
|
|
@@ -7710,28 +8112,70 @@
|
|
|
7710
8112
|
},
|
|
7711
8113
|
initInstance: function(input) {
|
|
7712
8114
|
const cleanup = [];
|
|
7713
|
-
const format = input.getAttribute("data-vd-datepicker-format") || "
|
|
8115
|
+
const format = input.getAttribute("data-vd-datepicker-format") || "YYYY-MM-DD";
|
|
7714
8116
|
const minStr = input.getAttribute("data-vd-datepicker-min");
|
|
7715
8117
|
const maxStr = input.getAttribute("data-vd-datepicker-max");
|
|
7716
|
-
const minDate = minStr ?
|
|
7717
|
-
const maxDate = maxStr ?
|
|
8118
|
+
const minDate = minStr ? parseYmdLocal(minStr) : null;
|
|
8119
|
+
const maxDate = maxStr ? parseYmdLocal(maxStr) : null;
|
|
7718
8120
|
const today = /* @__PURE__ */ new Date();
|
|
7719
8121
|
let viewYear = today.getFullYear();
|
|
7720
8122
|
let viewMonth = today.getMonth();
|
|
7721
8123
|
let selectedDate = null;
|
|
7722
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
|
+
};
|
|
7723
8158
|
if (input.value) {
|
|
7724
|
-
const
|
|
7725
|
-
|
|
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) {
|
|
7726
8166
|
selectedDate = parsed;
|
|
7727
8167
|
viewYear = parsed.getFullYear();
|
|
7728
8168
|
viewMonth = parsed.getMonth();
|
|
7729
8169
|
}
|
|
7730
8170
|
}
|
|
8171
|
+
const clampedInit = ensureMonthInRange(viewYear, viewMonth);
|
|
8172
|
+
viewYear = clampedInit.y;
|
|
8173
|
+
viewMonth = clampedInit.m;
|
|
7731
8174
|
const popup = document.createElement("div");
|
|
7732
8175
|
popup.className = "vd-datepicker-popup";
|
|
7733
8176
|
popup.setAttribute("role", "dialog");
|
|
7734
8177
|
popup.setAttribute("aria-label", "Choose date");
|
|
8178
|
+
popup.tabIndex = -1;
|
|
7735
8179
|
const wrapper = document.createElement("div");
|
|
7736
8180
|
wrapper.className = "vd-suggest-wrapper";
|
|
7737
8181
|
wrapper.style.position = "relative";
|
|
@@ -7739,18 +8183,80 @@
|
|
|
7739
8183
|
input.parentNode.insertBefore(wrapper, input);
|
|
7740
8184
|
wrapper.appendChild(input);
|
|
7741
8185
|
wrapper.appendChild(popup);
|
|
7742
|
-
const
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
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();
|
|
7747
8200
|
};
|
|
7748
|
-
const
|
|
7749
|
-
if (
|
|
7750
|
-
|
|
7751
|
-
|
|
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;
|
|
7752
8259
|
};
|
|
7753
|
-
const isSameDay = (a, b) => a && b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
7754
8260
|
const render = () => {
|
|
7755
8261
|
popup.innerHTML = "";
|
|
7756
8262
|
const header = document.createElement("div");
|
|
@@ -7820,35 +8326,53 @@
|
|
|
7820
8326
|
header.appendChild(nextBtn);
|
|
7821
8327
|
popup.appendChild(header);
|
|
7822
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");
|
|
7823
8333
|
const weekdays = document.createElement("div");
|
|
7824
8334
|
weekdays.className = "vd-datepicker-weekdays";
|
|
7825
|
-
|
|
8335
|
+
weekdays.setAttribute("role", "row");
|
|
8336
|
+
DAYS.forEach(function(d) {
|
|
7826
8337
|
const span = document.createElement("span");
|
|
8338
|
+
span.setAttribute("role", "columnheader");
|
|
8339
|
+
span.setAttribute("aria-label", d);
|
|
7827
8340
|
span.textContent = d;
|
|
7828
8341
|
weekdays.appendChild(span);
|
|
7829
8342
|
});
|
|
7830
|
-
|
|
7831
|
-
const grid = document.createElement("div");
|
|
7832
|
-
grid.className = "vd-datepicker-days";
|
|
8343
|
+
gridWrap.appendChild(weekdays);
|
|
7833
8344
|
const firstDay = new Date(viewYear, viewMonth, 1).getDay();
|
|
7834
8345
|
const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();
|
|
7835
8346
|
const daysInPrev = new Date(viewYear, viewMonth, 0).getDate();
|
|
8347
|
+
const cells = [];
|
|
7836
8348
|
for (let i = firstDay - 1; i >= 0; i--) {
|
|
7837
|
-
const
|
|
7838
|
-
|
|
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 });
|
|
7839
8354
|
}
|
|
7840
8355
|
for (let d = 1; d <= daysInMonth; d++) {
|
|
7841
8356
|
const date = new Date(viewYear, viewMonth, d);
|
|
7842
|
-
|
|
7843
|
-
grid.appendChild(btn);
|
|
8357
|
+
cells.push({ day: d, outside: false, date });
|
|
7844
8358
|
}
|
|
7845
8359
|
const totalCells = firstDay + daysInMonth;
|
|
7846
8360
|
const remaining = totalCells % 7 === 0 ? 0 : 7 - totalCells % 7;
|
|
7847
8361
|
for (let i = 1; i <= remaining; i++) {
|
|
7848
|
-
const
|
|
7849
|
-
|
|
8362
|
+
const date = new Date(viewYear, viewMonth + 1, i);
|
|
8363
|
+
cells.push({ day: i, outside: true, date });
|
|
7850
8364
|
}
|
|
7851
|
-
|
|
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);
|
|
7852
8376
|
} else if (viewMode === "months") {
|
|
7853
8377
|
const grid = document.createElement("div");
|
|
7854
8378
|
grid.className = "vd-datepicker-months";
|
|
@@ -7889,65 +8413,125 @@
|
|
|
7889
8413
|
popup.appendChild(grid);
|
|
7890
8414
|
}
|
|
7891
8415
|
};
|
|
7892
|
-
const
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
if (
|
|
7898
|
-
|
|
7899
|
-
btn.tabIndex = -1;
|
|
7900
|
-
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;
|
|
7901
8423
|
}
|
|
7902
|
-
if (
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
8424
|
+
if (key === "Escape") {
|
|
8425
|
+
e.preventDefault();
|
|
8426
|
+
e.stopPropagation();
|
|
8427
|
+
skipNextFocusOpen = true;
|
|
8428
|
+
close();
|
|
8429
|
+
input.focus();
|
|
8430
|
+
return;
|
|
7907
8431
|
}
|
|
7908
|
-
if (
|
|
7909
|
-
|
|
7910
|
-
selectedDate = date;
|
|
7911
|
-
viewYear = date.getFullYear();
|
|
7912
|
-
viewMonth = date.getMonth();
|
|
7913
|
-
input.value = formatDate(date);
|
|
7914
|
-
close();
|
|
7915
|
-
input.dispatchEvent(new CustomEvent("datepicker:select", {
|
|
7916
|
-
detail: { date, formatted: input.value },
|
|
7917
|
-
bubbles: true
|
|
7918
|
-
}));
|
|
7919
|
-
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
7920
|
-
});
|
|
8432
|
+
if (!focusedDate) {
|
|
8433
|
+
focusedDate = firstSelectableInMonth(viewYear, viewMonth);
|
|
7921
8434
|
}
|
|
7922
|
-
|
|
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);
|
|
7923
8481
|
};
|
|
7924
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
|
+
}
|
|
7925
8496
|
render();
|
|
7926
8497
|
popup.classList.add("is-open");
|
|
7927
8498
|
input.setAttribute("aria-expanded", "true");
|
|
8499
|
+
requestAnimationFrame(focusFocusedDay);
|
|
7928
8500
|
};
|
|
7929
8501
|
const close = () => {
|
|
7930
8502
|
popup.classList.remove("is-open");
|
|
7931
8503
|
input.setAttribute("aria-expanded", "false");
|
|
7932
8504
|
viewMode = "days";
|
|
7933
8505
|
};
|
|
7934
|
-
const focusHandler = () =>
|
|
8506
|
+
const focusHandler = () => {
|
|
8507
|
+
if (skipNextFocusOpen) {
|
|
8508
|
+
skipNextFocusOpen = false;
|
|
8509
|
+
return;
|
|
8510
|
+
}
|
|
8511
|
+
open();
|
|
8512
|
+
};
|
|
7935
8513
|
const outsideHandler = (e) => {
|
|
7936
8514
|
if (!wrapper.contains(e.target)) close();
|
|
7937
8515
|
};
|
|
7938
8516
|
const escHandler = (e) => {
|
|
7939
|
-
if (e.key === "Escape"
|
|
8517
|
+
if (e.key === "Escape" && popup.classList.contains("is-open")) {
|
|
8518
|
+
skipNextFocusOpen = true;
|
|
8519
|
+
close();
|
|
8520
|
+
input.focus();
|
|
8521
|
+
}
|
|
7940
8522
|
};
|
|
7941
8523
|
input.addEventListener("focus", focusHandler);
|
|
7942
8524
|
document.addEventListener("click", outsideHandler, true);
|
|
7943
8525
|
document.addEventListener("keydown", escHandler);
|
|
8526
|
+
popup.addEventListener("keydown", handleGridKeydown);
|
|
7944
8527
|
input.setAttribute("aria-haspopup", "dialog");
|
|
7945
8528
|
input.setAttribute("aria-expanded", "false");
|
|
7946
8529
|
input.setAttribute("autocomplete", "off");
|
|
7947
8530
|
cleanup.push(
|
|
7948
8531
|
() => input.removeEventListener("focus", focusHandler),
|
|
7949
8532
|
() => document.removeEventListener("click", outsideHandler, true),
|
|
7950
|
-
() => document.removeEventListener("keydown", escHandler)
|
|
8533
|
+
() => document.removeEventListener("keydown", escHandler),
|
|
8534
|
+
() => popup.removeEventListener("keydown", handleGridKeydown)
|
|
7951
8535
|
);
|
|
7952
8536
|
this.instances.set(input, { cleanup, open, close, popup });
|
|
7953
8537
|
},
|