100 lines
2.9 KiB
TypeScript
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;
|
|
};
|