Files
core2026/app/game-frontend/src/utils/auxiliaryNavigation.ts
T

48 lines
2.1 KiB
TypeScript

import { gameFrontendRuntimeConfig } from '../config/runtimeConfig';
// 이름은 앱이 직접 연 탭에만 부여한다. URL/sessionStorage 표식은 링크 복사나
// 브라우저의 새 탭 열기에도 전달될 수 있으므로 닫기 권한 판정에 사용하지 않는다.
const auxiliaryWindowPrefix = 'sammo-auxiliary:';
export const isAuxiliaryWindow = (): boolean => window.name.startsWith(auxiliaryWindowPrefix);
export const installAuxiliaryNavigation = (): void => {
document.addEventListener(
'click',
(event: MouseEvent) => {
if (
event.defaultPrevented ||
event.button !== 0 ||
event.ctrlKey ||
event.metaKey ||
event.shiftKey ||
event.altKey
)
return;
const link = event.target instanceof Element ? event.target.closest('a') : null;
if (!link || link.target !== '_blank' || link.hasAttribute('download')) return;
const url = new URL(link.href, window.location.href);
const basePath = gameFrontendRuntimeConfig.appBasePath;
if (url.origin !== window.location.origin || !url.pathname.startsWith(basePath)) return;
event.preventDefault();
const child = window.open('about:blank', '_blank');
if (!child) {
// 팝업 차단 시 현재 탭에서 열어 돌아가기 동작을 제공한다.
window.location.assign(url.href);
return;
}
child.name = `${auxiliaryWindowPrefix}${crypto.randomUUID()}`;
child.opener = null;
// 새 문서 소유의 링크로 이동해야 원본 링크의 referrer 정책도 적용된다.
const destination = child.document.createElement('a');
destination.href = url.href;
destination.rel = link.rel;
destination.referrerPolicy = link.referrerPolicy;
child.document.body.append(destination);
destination.click();
},
true
);
};