From 50b9983be43eada6a44709b87220d0258bbc82bd Mon Sep 17 00:00:00 2001 From: Iu Date: Tue, 20 Oct 2026 21:38:02 +0300 Subject: [PATCH] =?UTF-8?q?feat(web):=20=D1=81=D0=BE=D1=85=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D1=8F=D1=82=D1=8C=20=D1=80=D0=B0=D0=B7=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=20=D0=BC=D0=B8=D0=BD=D0=B8=D0=B0=D1=82=D1=8E=D1=80=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=20=D1=81=D0=BA=D1=80=D1=8B=D1=82=D1=8B=D1=85=20?= =?UTF-8?q?=D0=B1=D0=BE=D0=BA=D0=BE=D0=B2=D1=8B=D1=85=20=D0=BF=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=D0=BB=D1=8F=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В скрытом режиме панели больше не растягивают плитки: сетка пересчитывает число колонок так, чтобы размер миниатюры остался прежним, а в строке помещалось больше фото. Каркас публикует ширину, которую занимают панели в текущей раскладке (рейл на широких экранах, панель на сверхшироких), сетка использует её для пересчёта, а навигация курсора стрелками учитывает реальное число колонок. --- frontend/src/lib/components/AppShell.svelte | 32 ++++++++++++++++++- frontend/src/lib/components/PhotoGrid.svelte | 10 ++++-- frontend/src/lib/stores/grid.svelte.ts | 20 ++++++++++++ frontend/src/lib/stores/panels.svelte.ts | 2 ++ .../src/routes/(app)/albums/[id]/+page.svelte | 4 +-- openspec/specs/ui-workspace/spec.md | 8 ++++- 6 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 frontend/src/lib/stores/grid.svelte.ts diff --git a/frontend/src/lib/components/AppShell.svelte b/frontend/src/lib/components/AppShell.svelte index 5210464..402bf97 100644 --- a/frontend/src/lib/components/AppShell.svelte +++ b/frontend/src/lib/components/AppShell.svelte @@ -27,17 +27,32 @@ const RAIL = { min: 176, max: 480, key: 'rail-width', def: 240 }; const PANEL = { min: 220, max: 560, key: 'panel-width', def: 288 }; + // Ширина разделителя (w-1) между панелью и содержимым. + const HANDLE_PX = 4; let railWidth = $state(RAIL.def); let panelWidth = $state(PANEL.def); let isFullscreen = $state(false); + let isWide = $state(false); + let isExtraWide = $state(false); + + // Ширина, которую занимают панели в текущей раскладке: рейл и панель + // встают рядом с содержимым на широких экранах, на узких — поверх него. + const reserved = $derived( + (isWide ? railWidth + HANDLE_PX : 0) + + (isExtraWide && panel && panelVisible ? panelWidth + HANDLE_PX : 0) + ); $effect(() => { // Режим скрытия действует только там, где он доступен (просмотр альбома). if (!panelsToggle) panels.reset(); }); + $effect(() => { + panels.reserved = reserved; + }); + onMount(() => { const r = parseInt(localStorage.getItem(RAIL.key) ?? '', 10); if (r >= RAIL.min && r <= RAIL.max) railWidth = r; @@ -47,7 +62,22 @@ const onFs = () => (isFullscreen = !!document.fullscreenElement); document.addEventListener('fullscreenchange', onFs); onFs(); - return () => document.removeEventListener('fullscreenchange', onFs); + + const mqLg = window.matchMedia('(min-width: 1024px)'); + const mqXl = window.matchMedia('(min-width: 1280px)'); + const sync = () => { + isWide = mqLg.matches; + isExtraWide = mqXl.matches; + }; + sync(); + mqLg.addEventListener('change', sync); + mqXl.addEventListener('change', sync); + + return () => { + document.removeEventListener('fullscreenchange', onFs); + mqLg.removeEventListener('change', sync); + mqXl.removeEventListener('change', sync); + }; }); async function toggleFullscreen() { diff --git a/frontend/src/lib/components/PhotoGrid.svelte b/frontend/src/lib/components/PhotoGrid.svelte index 9b49089..62d743d 100644 --- a/frontend/src/lib/components/PhotoGrid.svelte +++ b/frontend/src/lib/components/PhotoGrid.svelte @@ -1,6 +1,6 @@