feat(web): сохранять размер миниатюр при скрытых боковых панелях
В скрытом режиме панели больше не растягивают плитки: сетка пересчитывает число колонок так, чтобы размер миниатюры остался прежним, а в строке помещалось больше фото. Каркас публикует ширину, которую занимают панели в текущей раскладке (рейл на широких экранах, панель на сверхшироких), сетка использует её для пересчёта, а навигация курсора стрелками учитывает реальное число колонок.
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Icon from './Icon.svelte';
|
||||
import { density } from '$lib/stores/density.svelte';
|
||||
import { grid } from '$lib/stores/grid.svelte';
|
||||
import { isPanoTag } from '$lib/pano';
|
||||
import type { Photo } from '$lib/types';
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
const GAP_PX = 8;
|
||||
let gridWidth = $state(0);
|
||||
|
||||
$effect(() => {
|
||||
grid.width = gridWidth;
|
||||
});
|
||||
|
||||
// Offer every variant the photo actually has; the backend generates the
|
||||
// medium variant asynchronously, so an incomplete set must still render.
|
||||
function srcsetFor(photo: Photo): string {
|
||||
@@ -44,7 +48,7 @@
|
||||
|
||||
const sizes = $derived(
|
||||
gridWidth > 0
|
||||
? `${Math.max(1, Math.round((gridWidth - (density.cols - 1) * GAP_PX) / density.cols))}px`
|
||||
? `${Math.max(1, Math.round((gridWidth - (grid.cols - 1) * GAP_PX) / grid.cols))}px`
|
||||
: undefined
|
||||
);
|
||||
|
||||
@@ -76,7 +80,7 @@
|
||||
<ul
|
||||
class="grid gap-2"
|
||||
bind:clientWidth={gridWidth}
|
||||
style="grid-template-columns: repeat({density.cols}, minmax(0, 1fr));"
|
||||
style="grid-template-columns: repeat({grid.cols}, minmax(0, 1fr));"
|
||||
>
|
||||
{#each photos as photo, i (photo.id)}
|
||||
<li>
|
||||
|
||||
Reference in New Issue
Block a user