Skip to content

Commit 67c4fc4

Browse files
feat(ui): sync form params with url query params (permalink)
1 parent dd53fe4 commit 67c4fc4

File tree

7 files changed

+157
-43
lines changed

7 files changed

+157
-43
lines changed

client-src/create/form.js

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Margin, ORIENTATION, PAPER_FORMAT } from "@giscience/ol-print-layout-control";
22
import { get as getProjection, toLonLat, transformExtent } from "ol/proj";
33
import { SKETCH_MAP_MARGINS } from "./sketchMapMargins";
4-
import { fillSelectOptions } from "../shared";
54

65
function bindFormToPrintLayoutControl(printLayoutControl, messageController) {
76
const paperFormats = { ...PAPER_FORMAT };
@@ -10,12 +9,6 @@ function bindFormToPrintLayoutControl(printLayoutControl, messageController) {
109
// TODO: This is temporary. Delete once A0 works
1110
delete paperFormats.A0;
1211

13-
// property: format
14-
fillSelectOptions("format", paperFormats);
15-
16-
// set initial form value from ol-control
17-
document.getElementById("format").value = printLayoutControl.getFormat();
18-
1912
// bind listeners to update ol-control from form
2013
document.getElementById("format")
2114
.addEventListener("change", (event) => {
@@ -27,9 +20,6 @@ function bindFormToPrintLayoutControl(printLayoutControl, messageController) {
2720
);
2821
});
2922

30-
// property: orientation
31-
fillSelectOptions("orientation", ORIENTATION);
32-
3323
// set initial form value from ol-control
3424
document.getElementById("orientation").value = printLayoutControl.getOrientation();
3525

@@ -78,6 +68,10 @@ function bindFormToPrintLayoutControl(printLayoutControl, messageController) {
7868

7969
// disable form submit and display info if zoom is lower than 9
8070
function handleZoomChange(zoom) {
71+
document.getElementById("zoom").value = zoom;
72+
document.getElementById("page-setup-form").dispatchEvent(new CustomEvent("zoom-updated", {
73+
detail: { value: zoom },
74+
}));
8175
if (zoom < 9) {
8276
messageController.addWarning("zoom-info");
8377
} else {
@@ -132,19 +126,24 @@ function bindFormToPrintLayoutControl(printLayoutControl, messageController) {
132126
printLayoutControl.on("change:bbox", (event) => {
133127
// update the URL when the selection is changed (e.g. to bookmark the current selection)
134128
const newCenter = printLayoutControl.getMap().getView().getCenter();
135-
window.history.replaceState({}, "", `?center=${newCenter}`);
129+
document.getElementById("center").value = newCenter;
130+
document.getElementById("page-setup-form").dispatchEvent(new CustomEvent("center-updated", {
131+
detail: { value: newCenter },
132+
}));
136133
// show warning and disable form if bbox crosses the antimeridian
137134
handleAntimeridian(event.target.getBboxAsLonLat());
138135
});
139136
}
140137

141138
function bindFormToLayerSwitcherControl(layerSwitcherControl) {
142-
// set initial form value from ol-control
139+
// set initial form value from ol-control
143140
document.getElementById("layer").value = layerSwitcherControl.get("activeLayer").name;
144-
145141
function handleLayerSwitch(event) {
146142
const activeLayerName = event.target.get("activeLayer").name;
147143
document.getElementById("layer").value = activeLayerName;
144+
document.getElementById("page-setup-form").dispatchEvent(new CustomEvent("layer-updated", {
145+
detail: { value: activeLayerName },
146+
}));
148147
}
149148
layerSwitcherControl.on("change:activeLayer", handleLayerSwitch);
150149
}

client-src/create/index.js

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import "ol-geocoder/dist/ol-geocoder.css";
44
import "./geocoder.css";
55
import "./create.css";
66

7+
import Alpine from "alpinejs";
78
import {
89
addGeocoderControl,
910
addLayerswitcherControl,
@@ -13,17 +14,62 @@ import {
1314
import { bindFormToLayerSwitcherControl, bindFormToPrintLayoutControl } from "./form.js";
1415
import { MessageController } from "./messageController";
1516

17+
window.Alpine = Alpine;
18+
19+
const updateQueryParam = (paramName, newValue) => {
20+
const url = new URL(window.location);
21+
url.searchParams.set(paramName, newValue);
22+
window.history.replaceState({}, "", url);
23+
};
24+
25+
document.addEventListener("alpine:init", () => {
26+
Alpine.directive("query-string", (el, {
27+
modifiers, // eslint-disable-line no-unused-vars
28+
expression,
29+
}, {
30+
evaluate,
31+
}) => {
32+
const urlParams = new URLSearchParams(window.location.search);
33+
34+
const paramName = expression;
35+
const initialValue = urlParams.get(paramName) ?? evaluate(expression);
36+
37+
Alpine.bind(el, {
38+
"x-data": function () { // eslint-disable-line func-names
39+
return {
40+
init() {
41+
this[paramName] = initialValue;
42+
this.$watch(expression, (value) => {
43+
updateQueryParam(paramName, value);
44+
});
45+
},
46+
};
47+
},
48+
});
49+
});
50+
});
51+
52+
Alpine.start();
53+
1654
// Retrieve potentially given map center and baselayer from URL (e.g. from a bookmarked selection)
1755
const searchParams = new URLSearchParams(window.location.search);
1856
const centerArg = searchParams.get("center");
57+
const zoomArg = searchParams.get("zoom");
1958
const baselayerArg = searchParams.get("layer");
59+
const orientationArg = searchParams.get("orientation");
60+
const formatArg = searchParams.get("format");
2061

2162
let center = [966253.1800856147, 6344703.99262965];
2263
if (centerArg != null) {
2364
const centerCoords = centerArg.split(",");
2465
center = [parseFloat(centerCoords[0]), parseFloat(centerCoords[1])];
2566
}
2667

68+
let zoom = 15;
69+
if (zoomArg != null) {
70+
zoom = zoomArg;
71+
}
72+
2773
// type BaselayerType = "OSM" | "ESRI:World_Imagery"
2874
let activeBaselayer;
2975
if (baselayerArg != null) {
@@ -36,9 +82,18 @@ if (baselayerArg != null) {
3682
}
3783
}
3884

39-
const map = createMap("map", center, 15, activeBaselayer);
85+
let orientation = "LANDSCAPE";
86+
if (orientationArg != null) {
87+
orientation = orientationArg.toUpperCase();
88+
}
89+
90+
let format = "A4";
91+
if (formatArg != null) {
92+
format = formatArg.toUpperCase();
93+
}
4094

41-
const printLayoutControl = addPrintLayoutControl(map);
95+
const map = createMap("map", center, zoom, activeBaselayer);
96+
const printLayoutControl = addPrintLayoutControl(map, format, orientation);
4297
const messageController = new MessageController();
4398

4499
bindFormToPrintLayoutControl(printLayoutControl, messageController);

client-src/create/map.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,18 @@ function createMap(target = "map", lonLat = [966253.1800856147, 6344703.99262965
108108
/**
109109
* Add the print-layout-control to an OpenLayers Map
110110
* @param map - An instance of an OpenLayers Map
111+
* @param format - An instance of FORMAT
112+
* @param orientation - An instance of ORIENTATION
111113
* @returns {PrintLayout}
112114
*/
113-
function addPrintLayoutControl(map) {
115+
function addPrintLayoutControl(map, format, orientation) {
116+
const paperFormat = PAPER_FORMAT[format];
117+
const paperOrientation = ORIENTATION[orientation];
118+
const paperMargins = SKETCH_MAP_MARGINS[paperFormat][paperOrientation];
114119
const printLayoutControl = new PrintLayout({
115-
format: PAPER_FORMAT.A4,
116-
orientation: ORIENTATION.LANDSCAPE,
117-
margin: SKETCH_MAP_MARGINS.A4.landscape,
120+
format: paperFormat,
121+
orientation: paperOrientation,
122+
margin: paperMargins,
118123
});
119124
map.addControl(printLayoutControl);
120125

client-src/shared/domHelpers.js

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,6 @@ function getUUIDFromURL() {
88
return location.pathname.match(UUID_V4_PATTERN)[0];
99
}
1010

11-
/**
12-
* Fills the options of an HTMLSelectElement with the values of a simple key-value Object
13-
* @param {string} selectElementId - the id of an HTMLSelectElement
14-
* @param { {[key:string] : string|number}} optionsMap - a key-value Object, values will be used as
15-
* options text and value
16-
*/
17-
function fillSelectOptions(selectElementId, optionsMap) {
18-
const selectElement = document.getElementById(selectElementId);
19-
Object.values(optionsMap)
20-
.forEach((paperformatValue) => {
21-
const option = document.createElement("option");
22-
option.text = paperformatValue;
23-
option.value = paperformatValue;
24-
selectElement.appendChild(option);
25-
});
26-
}
27-
2811
/**
2912
* set the aria-busy HTMLAttribute on the given HTMLElement
3013
* @param elementId
@@ -103,7 +86,6 @@ function openAllDetailsElements() {
10386

10487
export {
10588
getUUIDFromURL,
106-
fillSelectOptions,
10789
setTaskStatus,
10890
setDisabled,
10991
setDownloadLink,

package-lock.json

Lines changed: 46 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"dependencies": {
5050
"@giscience/ol-print-layout-control": "^1.0.2",
5151
"@picocss/pico": "^2.1.1",
52+
"alpinejs": "^3.15.0",
5253
"filebokz": "^0.1.3",
5354
"ol": "^8.1.0",
5455
"ol-geocoder": "^4.3.1"

sketch_map_tool/templates/create.jinja

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,49 @@
3737
the antimeridian.') }}
3838
</article>
3939
</div>
40-
41-
<form id="page-setup-form" action="/{{ lang }}/create/results" method="post">
40+
<!--
41+
State is synced between OpenLayers (including PrintLayoutControl)
42+
and Alping.js via below HTML form and custom events.
43+
Alping.js synchronizes form values to URL query parameter via `x-query-string`.
44+
-->
45+
<form
46+
id="page-setup-form"
47+
action="/{{ lang }}/create/results"
48+
method="post"
49+
x-data="{ format: '', orientation: '', layer: '', center: '', zoom: ''}"
50+
x-on:layer-updated="layer= $event.detail.value"
51+
x-on:center-updated="center= $event.detail.value"
52+
x-on:zoom-updated="zoom= $event.detail.value"
53+
>
4254
<label for="format">{{ _('Paper Format') }}</label>
43-
<select id="format" name="format"></select>
55+
<select id="format" name="format" x-model="format" x-query-string="format">
56+
<!-- TODO: re-enable A0 -->
57+
<!--<option value="A0">A0</option>-->
58+
<option value="A1">A1</option>
59+
<option value="A2">A2</option>
60+
<option value="A3">A3</option>
61+
<option value="A4">A4</option>
62+
<option value="LETTER">Letter</option>
63+
<option value="TABLOID">Tabloid</option>
64+
</select>
4465

4566
<label for="orientation">{{ _('Paper Orientation') }}</label>
46-
<select id="orientation" name="orientation"></select>
67+
<select id="orientation" name="orientation" x-model="orientation" x-query-string="orientation">
68+
<option value="landscape">Landscape</option>
69+
<option value="portrait">Portrait</option>
70+
</select>
4771

4872
<input type="hidden" id="bbox" name="bbox" value="1,1,5,5">
4973
<input type="hidden" id="bboxWGS84" name="bboxWGS84" value="1,1,5,5">
5074
<input type="hidden" id="size" name="size" value='{"width": 6055, "height": 8658}'>
5175
<input type="hidden" id="scale" name="scale" value='11545.36'>
52-
<input type="hidden" id="layer" name="layer" value="satellite">
76+
<input type="hidden" id="layer" name="layer" x-model="layer" x-query-string="layer">
77+
<input hidden disabled id="center" x-model="center" x-query-string="center">
78+
<input hidden disabled id="zoom" x-model="zoom" x-query-string="zoom">
5379
<button id="next-button" type="button">{{ _('NEXT') }} &rarr;</button>
5480
</form>
5581
</div>
5682

5783
</div>
5884
</main>
59-
{% endblock body %}
85+
{% endblock body %}

0 commit comments

Comments
 (0)