Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redo PMP and JAT map controls for creating objects #73

Merged
merged 5 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/lib/IconButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script lang="ts">
import { SecondaryButton } from "govuk-svelte";

// Removes the margin beneath this button, so it can be used as a floating map control

export let disabled = false;
</script>

<SecondaryButton on:click style="margin-bottom: 0px" {disabled}>
<slot />
</SecondaryButton>
1 change: 1 addition & 0 deletions src/lib/assets/images/movement.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/lib/assets/images/pan.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 1 addition & 7 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -60,10 +61,3 @@ export function dateToString(date: Date): string {
let day = date.getDate().toString().padStart(2, "0");
return `${year}-${month}-${day}`;
}

export function confirmNotNull<T>(x: T | null | undefined): T {
if (x == null || x == undefined) {
throw new Error("Oops, notNull given something null");
}
return x;
}
4 changes: 2 additions & 2 deletions src/lib/map/Geocoder.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
<style>
div {
position: absolute;
top: 20px;
left: 50px;
top: 100px;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also got rid of the old geolocate and fullscreen controls, because they're not useful for us, and adjusted the geocoder position. I'm sure all this could be improved further when we move more controls into map panels (streetview, scheme context layer, basemap, etc)
image

left: 10px;
z-index: 1;
}
</style>
10 changes: 8 additions & 2 deletions src/lib/map/MapLibreMap.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<script lang="ts">
import type { StyleSpecification } from "maplibre-gl";
import { MapLibre, type Map } from "svelte-maplibre";
import {
MapLibre,
type Map,
NavigationControl,
ScaleControl,
} from "svelte-maplibre";
import Geocoder from "./Geocoder.svelte";
import { styleChoice } from "./stores";

Expand Down Expand Up @@ -53,7 +58,6 @@

<MapLibre
style={getStyle($styleChoice)}
standardControls
on:error={(e) => {
// @ts-expect-error Not exported
console.log(e.detail.error);
Expand All @@ -62,6 +66,8 @@
bind:map
bounds={[-5.96, 49.89, 2.31, 55.94]}
>
<NavigationControl />
<ScaleControl />
<Geocoder {map} />
<slot />
</MapLibre>
48 changes: 22 additions & 26 deletions src/routes/route_check/jat_check/EditJunction.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
WarningButton,
DefaultButton,
TextArea,
Radio,
TextInput,
CollapsibleCard,
Checkbox,
Expand All @@ -26,12 +25,17 @@
let otherStage: "existing" | "proposed" =
stage == "existing" ? "proposed" : "existing";

type Mode =
| { mode: "select" }
| { mode: "editing"; id: ID }
| { mode: "new-arm" }
| { mode: "new-movement" };
type Kind = "arm" | "movement";
type ID = { kind: Kind; idx: number };

let newKind: Kind = "arm";
let editing: ID | null = null;
let mode: Mode = { mode: "select" };
// When changing to a form, preserve the list position and restore later
// TODO Some of this could be in Mode
let preserveListScroll: number | null = null;
let hoveringSidebar: ID | null = null;
let streetviewOn = false;
Expand All @@ -42,15 +46,15 @@

async function select(id: ID) {
preserveListScroll = sidebar.scrollTop;
editing = id;
mode = { mode: "editing", id };
hoveringSidebar = null;
// Scroll to the top of the form, which can be long for movements
await tick();
sidebar.scrollTop = 0;
}

async function stopEditing() {
editing = null;
mode = { mode: "select" };
if (preserveListScroll != null) {
await tick();
sidebar.scrollTop = preserveListScroll;
Expand All @@ -63,23 +67,26 @@
}

function deleteItem() {
if (mode.mode != "editing") {
return;
}
// TODO Modal
if (!window.confirm(`Delete this ${editing!.kind}?`)) {
if (!window.confirm(`Delete this ${mode.id.kind}?`)) {
return;
}
if (editing!.kind == "movement") {
$state.jat[junctionIdx][stage].movements.splice(editing!.idx, 1);
if (mode.id.kind == "movement") {
$state.jat[junctionIdx][stage].movements.splice(mode.id.idx, 1);
$state.jat[junctionIdx][stage].movements =
$state.jat[junctionIdx][stage].movements;
} else {
$state.jat[junctionIdx][stage].arms.splice(editing!.idx, 1);
$state.jat[junctionIdx][stage].arms.splice(mode.id.idx, 1);
$state.jat[junctionIdx][stage].arms = $state.jat[junctionIdx][stage].arms;
}
stopEditing();
}

function onKeyDown(e: KeyboardEvent) {
if (editing == null) {
if (mode.mode != "editing") {
return;
}
let tag = (e.target as HTMLElement).tagName;
Expand Down Expand Up @@ -147,7 +154,7 @@
style="width: 30%; overflow-y: scroll; padding: 10px; border: 1px solid black;"
bind:this={sidebar}
>
{#if editing == null}
{#if mode.mode != "editing"}
<slot />

<CollapsibleCard label="Tools">
Expand All @@ -160,16 +167,6 @@
<Checkbox bind:checked={showContext}>Show scheme context</Checkbox>
</CollapsibleCard>

<Radio
legend="Add to map"
choices={[
["arm", "Arm"],
["movement", "Movement"],
]}
inlineSmall
bind:value={newKind}
/>

<h3>Arms</h3>
{#each $state.jat[junctionIdx][stage].arms as arm, idx}
<ClickableCard
Expand Down Expand Up @@ -219,14 +216,14 @@
{:else}
<DefaultButton on:click={stopEditing}>Save</DefaultButton>
<WarningButton on:click={deleteItem}>Delete</WarningButton>
{#if editing.kind == "movement"}
<Form {junctionIdx} {stage} idx={editing.idx} />
{#if mode.id.kind == "movement"}
<Form {junctionIdx} {stage} idx={mode.id.idx} />
<DefaultButton on:click={stopEditing}>Save</DefaultButton>
<WarningButton on:click={deleteItem}>Delete</WarningButton>
{:else}
<TextInput
label="Arm Name"
bind:value={$state.jat[junctionIdx][stage].arms[editing.idx].name}
bind:value={$state.jat[junctionIdx][stage].arms[mode.id.idx].name}
/>
{/if}
{/if}
Expand All @@ -236,8 +233,7 @@
bind:this={mapControls}
{junctionIdx}
{stage}
{newKind}
bind:editing
{mode}
{hoveringSidebar}
{streetviewOn}
{showContext}
Expand Down
78 changes: 70 additions & 8 deletions src/routes/route_check/jat_check/MapControls.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,21 @@
} from "svelte-maplibre";
import type { MapMouseEvent, Map } from "maplibre-gl";
import { state, type Arm, type Movement, type State } from "../data";

import { IconButton } from "$lib";
import panUrl from "$lib/assets/images/pan.svg?url";
import movementUrl from "$lib/assets/images/movement.svg?url";

type Mode =
| { mode: "select" }
| { mode: "editing"; id: ID }
| { mode: "new-arm" }
| { mode: "new-movement" };
type Kind = "arm" | "movement";
type ID = { kind: Kind; idx: number };

export let junctionIdx: number;
export let stage: "existing" | "proposed";
export let newKind: Kind;
export let editing: ID | null;
export let mode: Mode;
export let hoveringSidebar: ID | null;
export let streetviewOn: boolean;
export let showContext: boolean;
Expand All @@ -40,7 +47,7 @@

let map: Map;

$: hoverGj = getHoverData($state, editing, hoveringSidebar);
$: hoverGj = getHoverData($state, mode, hoveringSidebar);

let scoreColors = {
0: colors.red.background,
Expand All @@ -51,14 +58,14 @@

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) {
if (id.kind == "arm") {
gj.features.push(
Expand All @@ -85,13 +92,16 @@
return;
}

if (mode.mode == "select") {
return;
}
// Deselect something
if (editing != null) {
if (mode.mode == "editing") {
await stopEditing();
return;
}

if (newKind == "arm") {
if (mode.mode == "new-arm") {
$state.jat[junctionIdx][stage].arms = [
...$state.jat[junctionIdx][stage].arms,
{
Expand Down Expand Up @@ -230,6 +240,47 @@
<MapLibreMap bind:map>
<MapEvents on:click={onMapClick} />

{#if mode.mode != "editing"}
<div class="control-panel">
<IconButton on:click={stopEditing}>
<img src={panUrl} alt="Move map" style="vertical-align: middle;" />
{#if mode.mode == "select"}
<u>Move map</u>
{:else}
Move map
{/if}
</IconButton>
<IconButton on:click={() => (mode = { mode: "new-arm" })}>
<svg
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
style="vertical-align: middle;"
>
<circle cx="12" cy="12" r="12" fill="white" />
<text x="6" y="16" fill="#4472c4">A</text>
</svg>
{#if mode.mode == "new-arm"}
<u>New arm</u>
{:else}
New arm
{/if}
</IconButton>
<IconButton on:click={() => (mode = { mode: "new-movement" })}>
<img
src={movementUrl}
alt="New movement"
style="vertical-align: middle;"
/>
{#if mode.mode == "new-movement"}
<u>New movement</u>
{:else}
New movement
{/if}
</IconButton>
</div>
{/if}

<ContextualMap gj={$state.summary.networkMap} show={showContext} />

{#each $state.jat[junctionIdx][stage].arms as arm, idx}
Expand Down Expand Up @@ -363,4 +414,15 @@
border: 3px solid black;
cursor: pointer;
}

.control-panel {
background: white;
position: absolute;
top: 10px;

left: 50%;
transform: translate(-50%, 0);
/* TODO Specified manually */
width: 450px;
}
</style>
Loading
Loading