From c679ab041a51cb7e91043c24f559f7f0c3e7628d Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Fri, 26 Jul 2024 14:33:45 +0100 Subject: [PATCH 1/5] Redo the map interactions on the PMP to avoid accidental edits --- src/lib/index.ts | 7 - .../route_check/problems_map/+page.svelte | 151 +++++++++++------- 2 files changed, 97 insertions(+), 61 deletions(-) diff --git a/src/lib/index.ts b/src/lib/index.ts index 605c0f62c0..add9a3ee1c 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -60,10 +60,3 @@ export function dateToString(date: Date): string { let day = date.getDate().toString().padStart(2, "0"); return `${year}-${month}-${day}`; } - -export function confirmNotNull(x: T | null | undefined): T { - if (x == null || x == undefined) { - throw new Error("Oops, notNull given something null"); - } - return x; -} diff --git a/src/routes/route_check/problems_map/+page.svelte b/src/routes/route_check/problems_map/+page.svelte index 4690b897a0..72ab45ef81 100644 --- a/src/routes/route_check/problems_map/+page.svelte +++ b/src/routes/route_check/problems_map/+page.svelte @@ -7,7 +7,6 @@ DefaultButton, SecondaryButton, WarningButton, - Radio, CollapsibleCard, Checkbox, } from "govuk-svelte"; @@ -17,7 +16,7 @@ import { GeoreferenceControls, GeoreferenceLayer } from "$lib/map/georef"; import { MapEvents, Marker, GeoJSON, CircleLayer } from "svelte-maplibre"; import type { MapMouseEvent, Map } from "maplibre-gl"; - import { ClickableCard, confirmNotNull } from "$lib"; + import { ClickableCard } from "$lib"; import { state, type State, @@ -40,29 +39,46 @@ type Kind = "critical" | "conflict"; type ID = { kind: Kind; idx: number }; + type Mode = + | { mode: "select" } + | { mode: "editing"; id: ID } + | { mode: "new-critical" } + | { mode: "new-conflict" }; + + let mode: Mode = { mode: "select" }; let urlKind = $page.url.searchParams.get("kind") || ""; - let newKind: Kind = ["critical", "conflict"].includes(urlKind) - ? (urlKind as Kind) - : "critical"; - let editing: ID | null = null; + if (urlKind == "critical") { + mode = { mode: "new-critical" }; + } else if (urlKind == "conflict") { + mode = { mode: "new-conflict" }; + } + // When changing to a form, preserve the list position and restore later + // TODO Some of this can be in Mode let preserveListScroll: number | null = null; let hoveringSidebar: ID | null = null; let streetviewOn = false; let showContext = true; - $: hoverGj = getHoverData($state, editing, hoveringSidebar); + $: if (map) { + map.getCanvas().style.cursor = + mode.mode == "new-critical" || mode.mode == "new-conflict" + ? "crosshair" + : "auto"; + } + + $: hoverGj = getHoverData($state, mode, hoveringSidebar); function getHoverData( state: State, - editing: ID | null, + mode: Mode, hoveringSidebar: ID | null, ): FeatureCollection { let gj: FeatureCollection = { type: "FeatureCollection" as const, features: [], }; - let id = editing ?? hoveringSidebar; + let id = mode.mode == "editing" ? mode.id : hoveringSidebar; if (id != null) { let list = id.kind == "critical" ? state.criticalIssues : state.policyConflictLog; @@ -73,7 +89,7 @@ async function select(id: ID) { preserveListScroll = sidebar.scrollTop; - editing = id; + mode = { mode: "editing", id }; hoveringSidebar = null; await tick(); sidebar.scrollTop = 0; @@ -81,7 +97,7 @@ async function selectAndZoom(id: ID) { preserveListScroll = sidebar.scrollTop; - editing = id; + mode = { mode: "editing", id }; hoveringSidebar = null; await tick(); sidebar.scrollTop = 0; @@ -118,7 +134,7 @@ } async function stopEditing() { - editing = null; + mode = { mode: "select" }; // Sort by the conflict or critical type. The scroll position may be slightly irrelevant if the user changes these types. $state.policyConflictLog = $state.policyConflictLog.toSorted( @@ -142,13 +158,16 @@ if (streetviewOn) { return; } + if (mode.mode == "select") { + return; + } // Deselect something - if (editing != null) { + if (mode.mode == "editing") { stopEditing(); return; } - if (newKind == "critical") { + if (mode.mode == "new-critical") { $state.criticalIssues = [ ...$state.criticalIssues, { @@ -162,7 +181,7 @@ }, ]; select({ kind: "critical", idx: $state.criticalIssues.length - 1 }); - } else { + } else if (mode.mode == "new-conflict") { $state.policyConflictLog = [ ...$state.policyConflictLog, { @@ -185,15 +204,18 @@ }); function deleteItem() { + if (mode.mode != "editing") { + return; + } // TODO Modal if (!window.confirm("Delete this entry?")) { return; } - if (editing!.kind == "critical") { - $state.criticalIssues.splice(editing!.idx, 1); + if (mode.id.kind == "critical") { + $state.criticalIssues.splice(mode.id.idx, 1); $state.criticalIssues = $state.criticalIssues; } else { - $state.policyConflictLog.splice(editing!.idx, 1); + $state.policyConflictLog.splice(mode.id.idx, 1); $state.policyConflictLog = $state.policyConflictLog; } stopEditing(); @@ -242,18 +264,22 @@ } function onKeyDown(e: KeyboardEvent) { - if (editing == null) { - return; - } - let tag = (e.target as HTMLElement).tagName; - let formFocused = tag == "INPUT" || tag == "TEXTAREA"; - - if (e.key == "Escape" || (e.key == "Enter" && !formFocused)) { - e.stopPropagation(); - stopEditing(); - } else if (e.key == "Delete" && !formFocused) { - e.stopPropagation(); - deleteItem(); + if (mode.mode == "editing") { + let tag = (e.target as HTMLElement).tagName; + let formFocused = tag == "INPUT" || tag == "TEXTAREA"; + + if (e.key == "Escape" || (e.key == "Enter" && !formFocused)) { + e.stopPropagation(); + stopEditing(); + } else if (e.key == "Delete" && !formFocused) { + e.stopPropagation(); + deleteItem(); + } + } else if (mode.mode == "new-critical" || mode.mode == "new-conflict") { + if (e.key == "Escape") { + e.stopPropagation(); + mode = { mode: "select" }; + } } } @@ -265,7 +291,7 @@ style="width: 30%; overflow-y: scroll; padding: 10px; border: 1px solid black;" bind:this={sidebar} > - {#if editing == null} + {#if mode.mode != "editing"} zoom(true)}> Zoom to fit @@ -276,20 +302,6 @@ Show scheme context - - -

- Click the map to add a problem, or select a problem to fill out data -

-

Critical Issues

{#each $state.criticalIssues as critical, idx} Save Delete { - let editingNotNull = confirmNotNull(editing); + on:click={() => createCopy({ - kind: editingNotNull.kind, - idx: editingNotNull.idx, - }); - }} + kind: mode.id.kind, + idx: mode.id.idx, + })} > Copy - {#if editing.kind == "critical"} - + {#if mode.id.kind == "critical"} + {:else} - + {/if} {/if} @@ -353,6 +363,32 @@ + {#if mode.mode != "editing"} +
+ stopEditing()} + style="margin-bottom: 0px" + > + Select + + (mode = { mode: "new-critical" })} + style="margin-bottom: 0px" + > + New critical issue + + (mode = { mode: "new-conflict" })} + style="margin-bottom: 0px" + > + New policy conflict + +
+ {/if} + {#each $state.criticalIssues as critical, idx} @@ -428,4 +464,11 @@ stroke-width: 6px; cursor: pointer; } + + .control-panel { + background: white; + position: absolute; + top: 10px; + left: 350px; + } From c84abd3bc3b3475f9fd2c37f0065d0fd2dff108f Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Fri, 26 Jul 2024 17:24:18 +0100 Subject: [PATCH 2/5] Use images for the panel controls (but need to adjust styling) --- src/lib/IconButton.svelte | 11 ++++++++ src/lib/assets/images/pan.svg | 1 + src/lib/index.ts | 1 + .../route_check/problems_map/+page.svelte | 28 +++++++++++-------- 4 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 src/lib/IconButton.svelte create mode 100644 src/lib/assets/images/pan.svg diff --git a/src/lib/IconButton.svelte b/src/lib/IconButton.svelte new file mode 100644 index 0000000000..0de0549f81 --- /dev/null +++ b/src/lib/IconButton.svelte @@ -0,0 +1,11 @@ + + + + + diff --git a/src/lib/assets/images/pan.svg b/src/lib/assets/images/pan.svg new file mode 100644 index 0000000000..1bb6e3f9eb --- /dev/null +++ b/src/lib/assets/images/pan.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/lib/index.ts b/src/lib/index.ts index add9a3ee1c..91083262a6 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -2,6 +2,7 @@ export { default as ClickableCard } from "./ClickableCard.svelte"; export { default as DateInput } from "./DateInput.svelte"; export { default as FancyRadio } from "./FancyRadio.svelte"; export { default as ExternalLink } from "./ExternalLink.svelte"; +export { default as IconButton } from "./IconButton.svelte"; export { default as Loading } from "./Loading.svelte"; export { default as Modal } from "./Modal.svelte"; export { default as PrevNext } from "./PrevNext.svelte"; diff --git a/src/routes/route_check/problems_map/+page.svelte b/src/routes/route_check/problems_map/+page.svelte index 72ab45ef81..88980d4425 100644 --- a/src/routes/route_check/problems_map/+page.svelte +++ b/src/routes/route_check/problems_map/+page.svelte @@ -16,7 +16,7 @@ import { GeoreferenceControls, GeoreferenceLayer } from "$lib/map/georef"; import { MapEvents, Marker, GeoJSON, CircleLayer } from "svelte-maplibre"; import type { MapMouseEvent, Map } from "maplibre-gl"; - import { ClickableCard } from "$lib"; + import { ClickableCard, IconButton } from "$lib"; import { state, type State, @@ -32,6 +32,7 @@ getPolicyConflictIndex, } from "../lists"; import { page } from "$app/stores"; + import panUrl from "$lib/assets/images/pan.svg?url"; let map: Map; let sidebar: HTMLDivElement; @@ -365,27 +366,32 @@ {#if mode.mode != "editing"}
- stopEditing()} - style="margin-bottom: 0px" > - Select - - + Move map + + (mode = { mode: "new-critical" })} - style="margin-bottom: 0px" > + + + New critical issue - - + (mode = { mode: "new-conflict" })} - style="margin-bottom: 0px" > + New policy conflict - +
{/if} From 676d2035c3f5d63f71cd9ffe77c59542eeeb4f6c Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Mon, 29 Jul 2024 13:34:23 +0100 Subject: [PATCH 3/5] Style adjustments: - Get rid of the geolocate and fullscreen map controls - Make the image buttons look decent --- src/lib/assets/images/pan.svg | 2 +- src/lib/map/MapLibreMap.svelte | 10 +++- .../route_check/problems_map/+page.svelte | 53 ++++++++++++------- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/lib/assets/images/pan.svg b/src/lib/assets/images/pan.svg index 1bb6e3f9eb..dab0aac95f 100644 --- a/src/lib/assets/images/pan.svg +++ b/src/lib/assets/images/pan.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/src/lib/map/MapLibreMap.svelte b/src/lib/map/MapLibreMap.svelte index 81fab54209..8884346a43 100644 --- a/src/lib/map/MapLibreMap.svelte +++ b/src/lib/map/MapLibreMap.svelte @@ -1,6 +1,11 @@