376 lines
9.1 KiB
JavaScript
376 lines
9.1 KiB
JavaScript
/**
|
|
* 학습 가이드 PDF 팝업
|
|
* - btn-guide 클릭 시 레이어 오픈
|
|
* - 탭 전환 시 pdf.js viewer iframe src 교체
|
|
*/
|
|
const LearningGuideModal = (function () {
|
|
const DEFAULT_TAB_KEY = "player";
|
|
const VIEWER_PATH = "/js/lib/pdfjs-viewer/web/viewer.html";
|
|
|
|
let layer = null;
|
|
let viewer = null;
|
|
let closeBtn = null;
|
|
let triggerBtn = null;
|
|
let tabSwiper = null;
|
|
let tabs = [];
|
|
let activeTabKey = DEFAULT_TAB_KEY;
|
|
let isOpen = false;
|
|
|
|
function resolvePdfUrl(pdfPath) {
|
|
if (!pdfPath) return "";
|
|
|
|
try {
|
|
return new URL(pdfPath, window.location.origin).href;
|
|
} catch (error) {
|
|
console.error("[LearningGuideModal] PDF URL resolve failed:", error);
|
|
return pdfPath;
|
|
}
|
|
}
|
|
|
|
function buildViewerUrl(pdfPath) {
|
|
if (!pdfPath) return "";
|
|
|
|
const viewerUrl = new URL(VIEWER_PATH, window.location.origin);
|
|
const fileUrl = resolvePdfUrl(pdfPath);
|
|
viewerUrl.searchParams.set("file", fileUrl);
|
|
viewerUrl.hash = "zoom=60&textlayer=off";
|
|
return viewerUrl.href;
|
|
}
|
|
|
|
function getTabButtons() {
|
|
if (!layer) return [];
|
|
return Array.from(layer.querySelectorAll(".learning-guide-tab"));
|
|
}
|
|
|
|
function getTabButton(key) {
|
|
return getTabButtons().find(function (tab) {
|
|
return tab.dataset.guideKey === key;
|
|
});
|
|
}
|
|
|
|
function detectTabKeyFromPath() {
|
|
const path = window.location.pathname.toLowerCase();
|
|
const pathMap = [
|
|
["index", "main"],
|
|
["main", "main"],
|
|
["myclass", "myclass"],
|
|
["onboarding", "onboarding"],
|
|
["learning", "legal"],
|
|
["legal_edu", "legal"],
|
|
["legal", "legal"],
|
|
["leadership", "leadership"],
|
|
["insight", "insight"],
|
|
["biztrend", "biztrend"],
|
|
["mypage", "mypage"],
|
|
["player", "player"],
|
|
];
|
|
|
|
for (let i = 0; i < pathMap.length; i += 1) {
|
|
const needle = pathMap[i][0];
|
|
const key = pathMap[i][1];
|
|
if (path.indexOf(needle) !== -1 && getTabButton(key)) return key;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function getPageDefaultTabKey() {
|
|
if (!layer) return DEFAULT_TAB_KEY;
|
|
|
|
const fromPath = detectTabKeyFromPath();
|
|
if (fromPath) return fromPath;
|
|
|
|
const btnKey =
|
|
triggerBtn && (triggerBtn.dataset.defaultGuideKey || "").trim();
|
|
if (btnKey && getTabButton(btnKey)) return btnKey;
|
|
|
|
const key = (layer.dataset.defaultGuideKey || "").trim();
|
|
if (key && getTabButton(key)) return key;
|
|
|
|
return DEFAULT_TAB_KEY;
|
|
}
|
|
|
|
function getFirstAvailableTabKey() {
|
|
const availableTab = getTabButtons().find(function (tab) {
|
|
return tab.dataset.pdf && !tab.disabled;
|
|
});
|
|
return availableTab ? availableTab.dataset.guideKey : getPageDefaultTabKey();
|
|
}
|
|
|
|
function initDefaultTab() {
|
|
activeTabKey = getPageDefaultTabKey();
|
|
updateTabStates(activeTabKey);
|
|
}
|
|
|
|
function syncOpenState(tabKey) {
|
|
const nextKey = tabKey || getPageDefaultTabKey();
|
|
loadPdfByKey(nextKey, { forceReload: true });
|
|
}
|
|
|
|
function setDefaultTab(key) {
|
|
if (!layer || !key) return;
|
|
|
|
const nextKey = String(key).trim();
|
|
if (!getTabButton(nextKey)) return;
|
|
|
|
layer.dataset.defaultGuideKey = nextKey;
|
|
activeTabKey = nextKey;
|
|
updateTabStates(nextKey);
|
|
|
|
if (isOpen) {
|
|
loadPdfByKey(nextKey, { forceReload: true });
|
|
return;
|
|
}
|
|
|
|
updateTabLayout(0);
|
|
}
|
|
|
|
function updateTabStates(key) {
|
|
getTabButtons().forEach(function (tab) {
|
|
const isActive = tab.dataset.guideKey === key;
|
|
tab.classList.toggle("is-active", isActive);
|
|
tab.setAttribute("aria-selected", isActive ? "true" : "false");
|
|
});
|
|
activeTabKey = key;
|
|
}
|
|
|
|
function getTabIndex(key) {
|
|
const tab = getTabButton(key);
|
|
if (!tab || !tabSwiper) return -1;
|
|
|
|
const slide = tab.closest(".swiper-slide");
|
|
if (!slide) return -1;
|
|
|
|
return Array.from(tabSwiper.slides).indexOf(slide);
|
|
}
|
|
|
|
function isTabOverflow() {
|
|
if (!tabSwiper) return false;
|
|
|
|
const swiperEl = tabSwiper.el;
|
|
|
|
// 자연 너비 기준 측정을 위해 scroll 모드로 일시 전환
|
|
swiperEl.classList.remove("is-tab-even");
|
|
swiperEl.classList.add("is-tab-scroll");
|
|
tabSwiper.update();
|
|
|
|
return tabSwiper.wrapperEl.scrollWidth > swiperEl.clientWidth + 1;
|
|
}
|
|
|
|
function updateTabLayout(speed) {
|
|
if (!tabSwiper) return;
|
|
|
|
const swiperEl = tabSwiper.el;
|
|
const overflow = isTabOverflow();
|
|
|
|
swiperEl.classList.toggle("is-tab-scroll", overflow);
|
|
swiperEl.classList.toggle("is-tab-even", !overflow);
|
|
|
|
tabSwiper.params.centeredSlides = overflow;
|
|
tabSwiper.params.centeredSlidesBounds = overflow;
|
|
tabSwiper.update();
|
|
|
|
const index = getTabIndex(activeTabKey);
|
|
if (index >= 0) {
|
|
tabSwiper.slideTo(index, speed !== undefined ? speed : 300);
|
|
}
|
|
}
|
|
|
|
function isViewerBlank() {
|
|
if (!viewer) return true;
|
|
|
|
const attrSrc = viewer.getAttribute("src");
|
|
return !attrSrc || !attrSrc.trim();
|
|
}
|
|
|
|
function loadPdfByKey(key, options) {
|
|
const tab = getTabButton(key);
|
|
if (!tab || !tab.dataset.pdf) return;
|
|
|
|
const forceReload = options && options.forceReload;
|
|
updateTabStates(key);
|
|
|
|
if (viewer) {
|
|
const nextSrc = buildViewerUrl(tab.dataset.pdf);
|
|
|
|
if (forceReload) {
|
|
viewer.removeAttribute("src");
|
|
requestAnimationFrame(function () {
|
|
viewer.src = nextSrc;
|
|
});
|
|
} else {
|
|
viewer.src = nextSrc;
|
|
}
|
|
}
|
|
|
|
updateTabLayout(300);
|
|
}
|
|
|
|
function initTabSwiper() {
|
|
if (typeof Swiper === "undefined" || !layer) return;
|
|
|
|
const swiperEl = layer.querySelector(".learning-guide-tab-swiper");
|
|
if (!swiperEl) return;
|
|
|
|
tabs = getTabButtons();
|
|
|
|
tabSwiper = new Swiper(swiperEl, {
|
|
slidesPerView: "auto",
|
|
spaceBetween: 1,
|
|
centeredSlides: false,
|
|
centeredSlidesBounds: false,
|
|
slideToClickedSlide: true,
|
|
watchOverflow: true,
|
|
speed: 300,
|
|
resistanceRatio: 0.65,
|
|
});
|
|
}
|
|
|
|
function destroyTabSwiper() {
|
|
if (tabSwiper) {
|
|
tabSwiper.destroy(true, true);
|
|
tabSwiper = null;
|
|
}
|
|
}
|
|
|
|
function open(tabKey) {
|
|
if (!layer || isOpen) return;
|
|
|
|
layer.classList.remove("hidden");
|
|
layer.classList.add("is-open");
|
|
layer.setAttribute("aria-hidden", "false");
|
|
isOpen = true;
|
|
|
|
if (typeof scrollManager !== "undefined") {
|
|
scrollManager.lock();
|
|
} else if (typeof bodyLock === "function") {
|
|
bodyLock();
|
|
}
|
|
|
|
requestAnimationFrame(function () {
|
|
requestAnimationFrame(function () {
|
|
syncOpenState(tabKey);
|
|
});
|
|
});
|
|
|
|
if (closeBtn) {
|
|
closeBtn.focus();
|
|
}
|
|
}
|
|
|
|
function close() {
|
|
if (!layer || !isOpen) return;
|
|
|
|
layer.classList.remove("is-open");
|
|
layer.classList.add("hidden");
|
|
layer.setAttribute("aria-hidden", "true");
|
|
isOpen = false;
|
|
|
|
if (typeof scrollManager !== "undefined") {
|
|
scrollManager.unlock();
|
|
} else if (typeof bodyUnlock === "function") {
|
|
bodyUnlock();
|
|
}
|
|
|
|
if (triggerBtn) {
|
|
triggerBtn.focus();
|
|
}
|
|
}
|
|
|
|
function handleTabClick(event) {
|
|
const tab = event.currentTarget;
|
|
if (!tab || tab.disabled || tab.classList.contains("is-disabled")) return;
|
|
|
|
const key = tab.dataset.guideKey;
|
|
if (!key || !tab.dataset.pdf) return;
|
|
|
|
if (key === activeTabKey) return;
|
|
loadPdfByKey(key);
|
|
}
|
|
|
|
function handleLayerClick(event) {
|
|
if (event.target === layer) {
|
|
close();
|
|
}
|
|
}
|
|
|
|
function handleKeydown(event) {
|
|
if (!isOpen || event.key !== "Escape") return;
|
|
close();
|
|
}
|
|
|
|
let resizeTimer = null;
|
|
function handleResize() {
|
|
if (!tabSwiper) return;
|
|
|
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
resizeTimer = setTimeout(function () {
|
|
tabSwiper.update();
|
|
updateTabLayout(0);
|
|
}, 150);
|
|
}
|
|
|
|
function bindEvents() {
|
|
if (!triggerBtn) {
|
|
triggerBtn = document.querySelector(".btn-guide");
|
|
}
|
|
if (triggerBtn) {
|
|
triggerBtn.addEventListener("click", function () {
|
|
open();
|
|
});
|
|
}
|
|
|
|
if (closeBtn) {
|
|
closeBtn.addEventListener("click", function (event) {
|
|
event.stopPropagation();
|
|
close();
|
|
});
|
|
}
|
|
|
|
if (layer) {
|
|
layer.addEventListener("click", handleLayerClick);
|
|
const content = layer.querySelector(".learning-guide-content");
|
|
if (content) {
|
|
content.addEventListener("click", function (event) {
|
|
event.stopPropagation();
|
|
});
|
|
}
|
|
}
|
|
|
|
getTabButtons().forEach(function (tab) {
|
|
tab.addEventListener("click", handleTabClick);
|
|
});
|
|
|
|
document.addEventListener("keydown", handleKeydown);
|
|
window.addEventListener("resize", handleResize);
|
|
}
|
|
|
|
function init() {
|
|
layer = document.getElementById("learningGuideLayer");
|
|
viewer = document.getElementById("learningGuideViewer");
|
|
closeBtn = document.getElementById("btnCloseLearningGuide");
|
|
triggerBtn = document.querySelector(".btn-guide");
|
|
|
|
if (!layer || !viewer) return;
|
|
|
|
initTabSwiper();
|
|
initDefaultTab();
|
|
bindEvents();
|
|
}
|
|
|
|
return {
|
|
init: init,
|
|
open: open,
|
|
close: close,
|
|
switchTab: loadPdfByKey,
|
|
setDefaultTab: setDefaultTab,
|
|
getDefaultTab: getPageDefaultTabKey,
|
|
buildViewerUrl: buildViewerUrl,
|
|
destroy: destroyTabSwiper,
|
|
};
|
|
})();
|
|
|
|
if (typeof window !== "undefined") {
|
|
window.LearningGuideModal = LearningGuideModal;
|
|
}
|