import type { SearchContextContribution } from "@govoplan/core-webui"; export type SearchOverlayAnchor = { top: number; left: number; width: number; height: number; }; export type SearchOverlayLayout = { top: number; left: number; width: number; maxHeight: number; inputOffset: number; inputWidth: number; inputHeight: number; resultsHeight: number; }; const DESKTOP_PANEL_WIDTH = 820; const DESKTOP_MARGIN = 16; const MOBILE_MARGIN = 12; const MOBILE_BREAKPOINT = 900; const RESULTS_GAP = 8; export function calculateSearchOverlayLayout( anchor: SearchOverlayAnchor, viewportWidth: number, viewportHeight: number ): SearchOverlayLayout { const mobile = viewportWidth < MOBILE_BREAKPOINT; const margin = mobile ? MOBILE_MARGIN : DESKTOP_MARGIN; const width = Math.max(1, Math.min(DESKTOP_PANEL_WIDTH, viewportWidth - margin * 2)); const left = Math.max(margin, (viewportWidth - width) / 2); const inputWidth = width; const inputOffset = 0; const top = Math.max(0, anchor.top); const inputHeight = Math.max(1, anchor.height); const availableResultsHeight = viewportHeight - top - inputHeight - RESULTS_GAP - margin; const resultsHeight = Math.max(96, Math.min(620, availableResultsHeight)); return { top, left, width, maxHeight: inputHeight + RESULTS_GAP + resultsHeight, inputOffset, inputWidth, inputHeight, resultsHeight }; } export function selectSearchContext( contexts: readonly SearchContextContribution[], pathname: string ): SearchContextContribution | null { const matches = contexts.flatMap((context) => context.pathPrefixes .filter((prefix) => pathMatchesPrefix(pathname, prefix)) .map((prefix) => ({ context, prefixLength: normalizePath(prefix).length })) ); matches.sort((left, right) => right.prefixLength - left.prefixLength || (left.context.order ?? 100) - (right.context.order ?? 100) || left.context.id.localeCompare(right.context.id) ); return matches[0]?.context ?? null; } function pathMatchesPrefix(pathname: string, prefix: string): boolean { const normalizedPath = normalizePath(pathname); const normalizedPrefix = normalizePath(prefix); return normalizedPrefix === "/" || normalizedPath === normalizedPrefix || normalizedPath.startsWith(`${normalizedPrefix}/`); } function normalizePath(value: string): string { const normalized = `/${String(value || "").trim().replace(/^\/+|\/+$/g, "")}`; return normalized === "/" ? normalized : normalized.replace(/\/+$/g, ""); }