diff --git a/package.json b/package.json index d314a2b..5f755b4 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ }, "scripts": { "start": "vite", - "build": "vite build", + "build": "node ../scripts/generate-update-manifest.mjs && vite build", "lint": "yarn check:eslint && yarn check:prettier", "check:eslint": "eslint src/*", "check:prettier": "prettier --check .", diff --git a/src/app/components/updates-dialog/UpdatesDialog.css.ts b/src/app/components/updates-dialog/UpdatesDialog.css.ts index cc86776..af1f358 100644 --- a/src/app/components/updates-dialog/UpdatesDialog.css.ts +++ b/src/app/components/updates-dialog/UpdatesDialog.css.ts @@ -3,7 +3,13 @@ import { color, config, toRem } from 'folds'; const MOBILE_BREAKPOINT = '480px'; -/** Dialog height cap — 85% of the viewport, minus safe areas. */ +export const PortalLayer = style({ + position: 'fixed', + inset: 0, + zIndex: config.zIndex.Max, +}); + +/** Dialog + overlay padding must never exceed the viewport height. */ const DIALOG_MAX_HEIGHT = 'calc(85vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px))'; diff --git a/src/app/components/updates-dialog/UpdatesDialog.tsx b/src/app/components/updates-dialog/UpdatesDialog.tsx index 202fda9..873363c 100644 --- a/src/app/components/updates-dialog/UpdatesDialog.tsx +++ b/src/app/components/updates-dialog/UpdatesDialog.tsx @@ -1,8 +1,10 @@ import React, { useState } from 'react'; +import { createPortal } from 'react-dom'; import FocusTrap from 'focus-trap-react'; import { Box, Button, + config, Dialog, IconButton, Overlay, @@ -46,18 +48,19 @@ export function UpdatesDialog({ setShowOlderList(false); }; - return ( - }> - - - + return createPortal( +
+ }> + + + @@ -177,5 +180,7 @@ export function UpdatesDialog({ +
, + document.body ); } diff --git a/src/app/data/updateNotes.ts b/src/app/data/updateNotes.ts index 9add859..497ab41 100644 --- a/src/app/data/updateNotes.ts +++ b/src/app/data/updateNotes.ts @@ -1,6 +1,15 @@ import { trimTrailingSlash } from '../utils/common'; -const UPDATE_BASE_PATH = `${trimTrailingSlash(import.meta.env.BASE_URL)}/update`; +function getUpdateBaseUrl(): string { + const basePath = trimTrailingSlash(import.meta.env.BASE_URL || './'); + const relative = + basePath === '.' || basePath === '' ? 'update/' : `${basePath}/update/`; + return new URL(relative, window.location.href).href.replace(/\/$/, ''); +} + +function getUpdateFileUrl(file: string): string { + return new URL(file, `${getUpdateBaseUrl()}/`).href; +} export type UpdateManifestEntry = { file: string; @@ -61,12 +70,12 @@ export function resolveUpdateAssetUrl(src: string | undefined): string | undefin if (src.startsWith('/')) return src; const normalized = src.replace(/^\.\//, ''); - return `${UPDATE_BASE_PATH}/${normalized}`; + return new URL(normalized, `${getUpdateBaseUrl()}/`).href; } export async function loadUpdateManifest(): Promise { try { - const response = await fetch(`${UPDATE_BASE_PATH}/manifest.json`, { cache: 'no-cache' }); + const response = await fetch(getUpdateFileUrl('manifest.json'), { cache: 'no-cache' }); if (!response.ok) return null; const data = (await response.json()) as UpdateManifest; @@ -86,7 +95,7 @@ export async function loadUpdateDocument(file: string): Promise - {children} + ); }