트론트 홈페이지와 포트폴리오 초기 구성
This commit is contained in:
113
map-dialog.js
Normal file
113
map-dialog.js
Normal file
@@ -0,0 +1,113 @@
|
||||
/* Keep the map and its navigation inside a dismissible view. */
|
||||
(() => {
|
||||
'use strict';
|
||||
const trigger = document.querySelector('.map-link');
|
||||
const dialog = document.querySelector('#map-dialog');
|
||||
if (!trigger || !dialog) return;
|
||||
|
||||
const canvas = dialog.querySelector('.map-dialog-canvas');
|
||||
const body = dialog.querySelector('.map-dialog-body');
|
||||
const status = dialog.querySelector('.map-dialog-status');
|
||||
const retry = dialog.querySelector('.map-dialog-retry');
|
||||
const closeButton = dialog.querySelector('.map-dialog-close');
|
||||
const historyKey = '__trontMapDialog';
|
||||
const token = `map-${Date.now()}`;
|
||||
let frame, timer, savedScroll, savedStyle, waitingForBack = false;
|
||||
let backdropPressed = false;
|
||||
|
||||
function showError() {
|
||||
clearTimeout(timer);
|
||||
frame?.remove();
|
||||
frame = null;
|
||||
body.setAttribute('aria-busy', 'false');
|
||||
status.hidden = false;
|
||||
status.querySelector('p').textContent = '지도를 불러오지 못했습니다. 연결 상태를 확인하고 다시 시도해 주세요.';
|
||||
retry.hidden = false;
|
||||
}
|
||||
|
||||
function loadMap() {
|
||||
clearTimeout(timer);
|
||||
frame?.remove();
|
||||
body.setAttribute('aria-busy', 'true');
|
||||
status.hidden = false;
|
||||
status.querySelector('p').textContent = '지도를 불러오는 중입니다.';
|
||||
retry.hidden = true;
|
||||
frame = document.createElement('iframe');
|
||||
frame.title = '구글 지도 — 트론트, IT융합산업빌딩 위치';
|
||||
// The campus shares one street address; use the IT building's location.
|
||||
frame.src = 'https://maps.google.com/maps?hl=ko&q=35.8920895,128.6141078&z=17&output=embed';
|
||||
frame.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox');
|
||||
frame.referrerPolicy = 'no-referrer-when-downgrade';
|
||||
const loadingFrame = frame;
|
||||
frame.addEventListener('load', () => {
|
||||
if (frame !== loadingFrame || !dialog.open) return;
|
||||
clearTimeout(timer);
|
||||
body.setAttribute('aria-busy', 'false');
|
||||
status.hidden = true;
|
||||
});
|
||||
frame.addEventListener('error', showError);
|
||||
canvas.replaceChildren(frame);
|
||||
timer = setTimeout(showError, 20000);
|
||||
}
|
||||
|
||||
function openMap(addHistory = true) {
|
||||
if (dialog.open || waitingForBack) return;
|
||||
savedScroll = { x: scrollX, y: scrollY };
|
||||
// Save the page's history entry before fixing the body resets scrollY to 0.
|
||||
if (addHistory) {
|
||||
try { history.pushState({ ...history.state, [historyKey]: token }, '', location.href); }
|
||||
catch { /* The close controls also work in local file previews. */ }
|
||||
}
|
||||
savedStyle = ['position', 'top', 'left', 'width', 'paddingRight'].map(key => [key, document.body.style[key]]);
|
||||
const scrollbar = innerWidth - document.documentElement.clientWidth;
|
||||
const padding = parseFloat(getComputedStyle(document.body).paddingRight) || 0;
|
||||
Object.assign(document.body.style, {
|
||||
position: 'fixed', top: `-${savedScroll.y}px`, left: `-${savedScroll.x}px`,
|
||||
width: '100%', paddingRight: `${padding + scrollbar}px`
|
||||
});
|
||||
document.documentElement.classList.add('map-dialog-open');
|
||||
dialog.showModal();
|
||||
closeButton.focus({ preventScroll: true });
|
||||
loadMap();
|
||||
}
|
||||
|
||||
function closeMap() {
|
||||
if (!dialog.open) return;
|
||||
clearTimeout(timer);
|
||||
frame?.remove();
|
||||
frame = null;
|
||||
dialog.close();
|
||||
savedStyle.forEach(([key, value]) => { document.body.style[key] = value; });
|
||||
document.documentElement.classList.remove('map-dialog-open');
|
||||
window.scrollTo({ left: savedScroll.x, top: savedScroll.y, behavior: 'instant' });
|
||||
trigger.focus({ preventScroll: true });
|
||||
}
|
||||
|
||||
function dismiss() {
|
||||
closeMap();
|
||||
if (history.state?.[historyKey] === token) {
|
||||
waitingForBack = true;
|
||||
history.back();
|
||||
}
|
||||
}
|
||||
|
||||
function isBackdrop(event) {
|
||||
const rect = dialog.getBoundingClientRect();
|
||||
return event.target === dialog && (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom);
|
||||
}
|
||||
|
||||
trigger.addEventListener('click', () => openMap());
|
||||
closeButton.addEventListener('click', dismiss);
|
||||
retry.addEventListener('click', loadMap);
|
||||
dialog.addEventListener('cancel', event => { event.preventDefault(); dismiss(); });
|
||||
dialog.addEventListener('pointerdown', event => { backdropPressed = isBackdrop(event); });
|
||||
dialog.addEventListener('click', event => {
|
||||
if (backdropPressed && isBackdrop(event)) dismiss();
|
||||
backdropPressed = false;
|
||||
});
|
||||
window.addEventListener('popstate', event => {
|
||||
waitingForBack = false;
|
||||
if (event.state?.[historyKey] === token) openMap(false);
|
||||
else closeMap();
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user