Files
frontend/apps/sn/src/features/windowPopup/lib/openWindowPopup.ts
T
2026-06-01 15:49:50 +09:00

100 lines
2.9 KiB
TypeScript

import {
isWindowPopupKey,
windowPopupDefinitions,
type WindowPopupKey,
} from "../model/popupDefinitions";
import type { OpenWindowPopupParams } from "../model/types";
const buildQueryString = (params: OpenWindowPopupParams = {}) => {
const searchParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value === null || value === undefined || value === "") return;
searchParams.set(key, String(value));
});
const queryString = searchParams.toString();
return queryString ? `?${queryString}` : "";
};
const clamp = (value: number, min: number, max: number) => {
if (max < min) return min;
return Math.min(Math.max(value, min), max);
};
const buildPopupFeatures = (
width: number,
height: number,
placement: "center" | "browser-left" = "center",
) => {
const screenLeft = window.screenX ?? window.screenLeft ?? 0;
const screenTop = window.screenY ?? window.screenTop ?? 0;
const outerWidth = window.outerWidth || window.screen.width;
const outerHeight = window.outerHeight || window.screen.height;
const screen = window.screen as Screen & {
availLeft?: number;
availTop?: number;
};
const availableLeft = screen.availLeft ?? 0;
const availableTop = screen.availTop ?? 0;
const availableWidth = window.screen.availWidth || outerWidth;
const availableHeight = window.screen.availHeight || outerHeight;
const safeWidth = Math.min(width, Math.max(360, availableWidth - 48));
const safeHeight = Math.min(height, Math.max(420, availableHeight - 72));
const minLeft = availableLeft;
const minTop = availableTop + 24;
const maxLeft = availableLeft + availableWidth - safeWidth - 16;
const maxTop = availableTop + availableHeight - safeHeight - 24;
const left =
placement === "browser-left"
? clamp(screenLeft, minLeft, maxLeft)
: clamp(
screenLeft + Math.round((outerWidth - safeWidth) / 2),
availableLeft + 16,
maxLeft,
);
const top = clamp(
screenTop + Math.round((outerHeight - safeHeight) / 2),
minTop,
maxTop,
);
return [
`width=${safeWidth}`,
`height=${safeHeight}`,
`left=${left}`,
`top=${top}`,
"resizable=yes",
"scrollbars=yes",
"status=no",
"location=no",
"menubar=no",
"toolbar=no",
].join(",");
};
export const openWindowPopup = (
popupKey: WindowPopupKey,
params?: OpenWindowPopupParams,
) => {
if (typeof window === "undefined") return null;
if (!isWindowPopupKey(popupKey)) return null;
const definition = windowPopupDefinitions[popupKey];
const popupUrl = `/popup/${popupKey}${buildQueryString(params)}`;
const popupFeatures = buildPopupFeatures(
definition.width,
definition.height,
"placement" in definition ? definition.placement : undefined,
);
const popupWindow = window.open(
popupUrl,
`sn-popup-${popupKey}`,
popupFeatures,
);
popupWindow?.focus();
return popupWindow;
};