feat: add honest activity map mode

This commit is contained in:
ik
2026-09-21 19:40:35 +07:00
parent b9aecb75f5
commit 5c1a7ffa01
4 changed files with 54 additions and 3 deletions
+46
View File
@@ -0,0 +1,46 @@
---
import { spotPath, type Activity } from "../lib/api";
const { items } = Astro.props as { items: Activity[] };
const xValues = items.map((item) => item.x);
const yValues = items.map((item) => item.y);
const minX = Math.min(...xValues, 0);
const maxX = Math.max(...xValues, 1);
const minY = Math.min(...yValues, 0);
const maxY = Math.max(...yValues, 1);
const xRange = Math.max(1, maxX - minX);
const yRange = Math.max(1, maxY - minY);
const column = (value: number) => Math.min(20, Math.max(1, Math.round(((value - minX) / xRange) * 18) + 1));
const row = (value: number) => Math.min(20, Math.max(1, Math.round((1 - (value - minY) / yRange) * 18) + 1));
---
<section class="activity-map" aria-label="Схема координат горячих точек">
<header class="activity-map__header">
<div><span class="overline">Режим схемы</span><h3>Горячие точки на координатной сетке</h3></div>
<p>Показаны подтверждённые координаты текущей выборки. Это схема `x:y`, а не географическая карта водоёма.</p>
</header>
<div class="activity-map__plot" role="group" aria-label={`Координатная схема, точек: ${items.length}`}>
<span class="activity-map__axis activity-map__axis--x" aria-hidden="true">x →</span>
<span class="activity-map__axis activity-map__axis--y" aria-hidden="true">y ↑</span>
{items.map((item) => <a class:list={["activity-map__point", `activity-map__point--col-${column(item.x)}`, `activity-map__point--row-${row(item.y)}`]} href={spotPath(item)} aria-label={`${item.waterbody}, ${item.x}:${item.y}, ${item.fish}`}><span>{item.x}:{item.y}</span><strong>{item.fish}</strong></a>)}
</div>
</section>
<style>
.activity-map { display: grid; gap: 18px; }
.activity-map__header { display: flex; justify-content: space-between; gap: 22px; align-items: end; }
.activity-map__header h3 { margin: 6px 0 0; font: 400 27px Georgia, serif; letter-spacing: -.03em; }
.activity-map__header p { max-width: 330px; margin: 0; color: var(--text-muted); font-size: 12px; line-height: 1.5; }
.activity-map__plot { position: relative; display: grid; grid-template-columns: repeat(20, minmax(0, 1fr)); grid-template-rows: repeat(20, minmax(0, 1fr)); min-height: 470px; overflow: hidden; border: 1px solid var(--border); border-radius: 20px; background: linear-gradient(90deg, color-mix(in srgb, var(--accent-muted) 8%, transparent) 1px, transparent 1px), linear-gradient(color-mix(in srgb, var(--accent-muted) 8%, transparent) 1px, transparent 1px), var(--surface-soft); background-size: 10% 10%; }
.activity-map__plot::after { content: ""; position: absolute; inset: 8%; border: 1px dashed color-mix(in srgb, var(--accent-muted) 40%, transparent); border-radius: 12px; pointer-events: none; }
.activity-map__axis { position: absolute; z-index: 1; color: var(--text-subtle); font-size: 10px; font-weight: 750; letter-spacing: .08em; text-transform: uppercase; }
.activity-map__axis--x { right: 5%; bottom: 3%; }
.activity-map__axis--y { left: 2%; top: 4%; writing-mode: vertical-rl; transform: rotate(180deg); }
.activity-map__point { position: relative; z-index: 2; display: grid; align-self: center; justify-self: center; gap: 3px; min-width: 74px; margin: 5px; padding: 8px 10px; border: 1px solid color-mix(in srgb, var(--accent-muted) 45%, var(--border)); border-radius: 12px; background: var(--surface); color: var(--text); text-decoration: none; box-shadow: 0 8px 18px color-mix(in srgb, var(--deep) 11%, transparent); }
.activity-map__point::before { content: ""; position: absolute; left: 50%; bottom: -6px; width: 10px; height: 10px; border-right: 1px solid color-mix(in srgb, var(--accent-muted) 45%, var(--border)); border-bottom: 1px solid color-mix(in srgb, var(--accent-muted) 45%, var(--border)); background: var(--surface); transform: translateX(-50%) rotate(45deg); }
.activity-map__point:hover, .activity-map__point:focus-visible { border-color: var(--focus); transform: scale(1.03); }
.activity-map__point span { color: var(--text-muted); font-size: 10px; font-weight: 750; }
.activity-map__point strong { max-width: 130px; overflow: hidden; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
:global(.activity-map__point--col-1) { grid-column: 1; } :global(.activity-map__point--col-2) { grid-column: 2; } :global(.activity-map__point--col-3) { grid-column: 3; } :global(.activity-map__point--col-4) { grid-column: 4; } :global(.activity-map__point--col-5) { grid-column: 5; } :global(.activity-map__point--col-6) { grid-column: 6; } :global(.activity-map__point--col-7) { grid-column: 7; } :global(.activity-map__point--col-8) { grid-column: 8; } :global(.activity-map__point--col-9) { grid-column: 9; } :global(.activity-map__point--col-10) { grid-column: 10; } :global(.activity-map__point--col-11) { grid-column: 11; } :global(.activity-map__point--col-12) { grid-column: 12; } :global(.activity-map__point--col-13) { grid-column: 13; } :global(.activity-map__point--col-14) { grid-column: 14; } :global(.activity-map__point--col-15) { grid-column: 15; } :global(.activity-map__point--col-16) { grid-column: 16; } :global(.activity-map__point--col-17) { grid-column: 17; } :global(.activity-map__point--col-18) { grid-column: 18; } :global(.activity-map__point--col-19) { grid-column: 19; } :global(.activity-map__point--col-20) { grid-column: 20; }
:global(.activity-map__point--row-1) { grid-row: 1; } :global(.activity-map__point--row-2) { grid-row: 2; } :global(.activity-map__point--row-3) { grid-row: 3; } :global(.activity-map__point--row-4) { grid-row: 4; } :global(.activity-map__point--row-5) { grid-row: 5; } :global(.activity-map__point--row-6) { grid-row: 6; } :global(.activity-map__point--row-7) { grid-row: 7; } :global(.activity-map__point--row-8) { grid-row: 8; } :global(.activity-map__point--row-9) { grid-row: 9; } :global(.activity-map__point--row-10) { grid-row: 10; } :global(.activity-map__point--row-11) { grid-row: 11; } :global(.activity-map__point--row-12) { grid-row: 12; } :global(.activity-map__point--row-13) { grid-row: 13; } :global(.activity-map__point--row-14) { grid-row: 14; } :global(.activity-map__point--row-15) { grid-row: 15; } :global(.activity-map__point--row-16) { grid-row: 16; } :global(.activity-map__point--row-17) { grid-row: 17; } :global(.activity-map__point--row-18) { grid-row: 18; } :global(.activity-map__point--row-19) { grid-row: 19; } :global(.activity-map__point--row-20) { grid-row: 20; }
@media (max-width: 720px) { .activity-map__header { display: grid; gap: 8px; align-items: start; } .activity-map__plot { min-height: 390px; } .activity-map__point { min-width: 62px; padding: 7px 8px; } .activity-map__point strong { max-width: 90px; } }
</style>