From da0cc79def0d83438341e6a147934e4b030c224e Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 19 Jun 2026 09:27:26 +0200 Subject: [PATCH] feat(midsummer): dancing maypole pinned to map centre --- frontend/src/components/map/MapView.tsx | 2 + frontend/src/components/midsummer/Maypole.tsx | 41 +++++++++++++++ frontend/src/styles/midsummer.css | 51 +++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 frontend/src/components/midsummer/Maypole.tsx diff --git a/frontend/src/components/map/MapView.tsx b/frontend/src/components/map/MapView.tsx index 8b9978b7..1e916043 100644 --- a/frontend/src/components/map/MapView.tsx +++ b/frontend/src/components/map/MapView.tsx @@ -4,6 +4,7 @@ import { PlayerDots } from './PlayerDots'; import { TrailsSVG } from './TrailsSVG'; import { HeatmapCanvas } from './HeatmapCanvas'; import { PortalMarkers } from './PortalMarkers'; +import { Maypole } from '../midsummer/Maypole'; import type { TelemetrySnapshot } from '../../types'; interface Props { @@ -143,6 +144,7 @@ export const MapView: React.FC = ({ players, getColor, onSelectPlayer, sh selectedPlayer={selectedPlayer} /> + )} diff --git a/frontend/src/components/midsummer/Maypole.tsx b/frontend/src/components/midsummer/Maypole.tsx new file mode 100644 index 00000000..187983ad --- /dev/null +++ b/frontend/src/components/midsummer/Maypole.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { useMidsummer } from '../../hooks/useMidsummer'; + +interface Props { + imgW: number; + imgH: number; +} + +// Kept small for perf — these orbit the pole via one CSS animation. +const FROG_COUNT = 6; +// Default: dead centre of the Dereth map image. To plant at a landmark, +// import { worldToPx } from '../../utils/coordinates' and compute from +// world coords instead. +const center = (imgW: number, imgH: number) => ({ x: imgW / 2, y: imgH / 2 }); + +/** + * Midsommarstång planted inside the map's pan/zoom group, so it scales and + * pans with the world automatically. Carries its own ring of dancing frogs + * (one CSS rotation) so the spectacle is independent of who is online. + */ +export const Maypole: React.FC = ({ imgW, imgH }) => { + const { enabled } = useMidsummer(); + if (!enabled || imgW === 0) return null; + const { x, y } = center(imgW, imgH); + return ( +