Add tooltip for box selection feature in map component
All checks were successful
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 19s
CI / check (push) Successful in 22s
CI / build (push) Successful in 40s

- Introduced a tooltip that guides users to draw a box for camera searches, displayed conditionally based on local storage state.
- Implemented tooltip positioning logic and dismiss functionality.
- Updated the Svelte component to enhance user interaction and experience.
This commit is contained in:
2025-12-24 21:09:29 -06:00
parent 04e3359369
commit 57187e7ed4

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { onMount, onDestroy, tick } from 'svelte';
import Map from 'ol/Map.js';
import type MapBrowserEvent from 'ol/MapBrowserEvent.js';
import { Style, Stroke, Fill } from 'ol/style.js';
@@ -88,6 +88,9 @@
let locationSearchDebounceTimer: ReturnType<typeof setTimeout> | null = null;
let selectionBoxCenterPixel: [number, number] | null = null;
let footerCollapsed = false;
let showBoxTooltip = false;
let boxSelectButton: HTMLButtonElement;
let boxTooltipPosition: { left: number; top: number } | null = null;
let cameraSource: VectorSource;
let clusterSource: Cluster;
@@ -203,6 +206,15 @@
restoreStateFromUrl();
const hasSeenTooltip = localStorage.getItem('surveilled-box-tooltip-seen');
if (!hasSeenTooltip) {
setTimeout(async () => {
showBoxTooltip = true;
await tick();
updateTooltipPosition();
}, 500);
}
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
@@ -424,12 +436,32 @@
}
}
function updateTooltipPosition() {
if (!boxSelectButton) return;
const toolbar = boxSelectButton.closest('.toolbar');
if (!toolbar) return;
const toolbarContainer = toolbar.parentElement;
if (!toolbarContainer) return;
const buttonRect = boxSelectButton.getBoundingClientRect();
const containerRect = toolbarContainer.getBoundingClientRect();
boxTooltipPosition = {
left: buttonRect.left - containerRect.left + buttonRect.width / 2,
top: buttonRect.bottom - containerRect.top + 8,
};
}
function dismissBoxTooltip() {
showBoxTooltip = false;
localStorage.setItem('surveilled-box-tooltip-seen', 'true');
}
function toggleBoxSelectMode() {
if (isBoxSelectMode) {
disableBoxSelectMode();
} else {
enableBoxSelectMode();
}
dismissBoxTooltip();
}
function enableBoxSelectMode() {
@@ -515,9 +547,6 @@
width: 2.5,
lineDash: [5, 3],
}),
fill: new Fill({
color: 'rgba(239, 68, 68, 0.18)',
}),
})
);
source.clear();
@@ -947,6 +976,7 @@
class="toolbar bg-bg-secondary/80 backdrop-blur-sm border border-border-color rounded-lg px-2 py-1.5 flex items-center gap-1 shadow-lg"
>
<button
bind:this={boxSelectButton}
class="toolbar-btn {isBoxSelectMode ? 'active' : ''}"
title="Select Area (B)"
on:click={toggleBoxSelectMode}
@@ -983,6 +1013,30 @@
<option value="satellite">Satellite</option>
</select>
</div>
{#if showBoxTooltip && boxTooltipPosition}
<div
class="absolute z-[1300] pointer-events-none"
style="left: {boxTooltipPosition.left}px; top: {boxTooltipPosition.top}px; transform: translateX(-50%);"
>
<div
class="bg-bg-secondary border border-border-color rounded-lg px-3 py-2 shadow-lg text-sm text-text-primary relative"
>
<div
class="absolute -top-2 left-1/2 -translate-x-1/2 w-0 h-0 border-l-4 border-r-4 border-b-4 border-l-transparent border-r-transparent border-b-border-color"
></div>
<div
class="absolute -top-[7px] left-1/2 -translate-x-1/2 w-0 h-0 border-l-[7px] border-r-[7px] border-b-[7px] border-l-transparent border-r-transparent border-b-bg-secondary"
></div>
<div class="relative z-10">Click here to draw a box and search for cameras</div>
<button
class="absolute top-1 right-1 text-text-secondary hover:text-text-primary pointer-events-auto"
on:click={dismissBoxTooltip}
>
×
</button>
</div>
</div>
{/if}
</div>
<button
class="sm:hidden fixed bottom-4 left-4 z-[1100] bg-bg-secondary border border-border-color rounded-full px-3 py-2 text-xs font-semibold text-text-primary shadow-lg"