Repository navigation
feat: Dedicated reset view, undo and delete map buttons #664
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,14 @@ | ||
| import Map from "ol/Map"; | ||
| import { Control, ScaleLine } from "ol/control"; | ||
| import { Control, ScaleLine, Zoom } from "ol/control"; | ||
| import "ol/ol.css"; | ||
| import "ol-ext/dist/ol-ext.css"; | ||
| import northArrowIcon from "./icons/north-arrow-n.svg"; | ||
| import trashCanIcon from "./icons/trash-can.svg"; | ||
| import addIcon from "./icons/add.svg"; | ||
| import removeIcon from "./icons/remove.svg"; | ||
| import filterCenterFocusIcon from "./icons/filter-center-focus.svg"; | ||
| import deleteIcon from "./icons/delete.svg"; | ||
| import infoIcon from "./icons/info.svg"; | ||
| import closeIcon from "./icons/close.svg"; | ||
| import printIcon from "./icons/printer.svg"; | ||
| import PrintDialog from "ol-ext/control/PrintDialog"; | ||
| import { Options } from "ol-ext/control/PrintDialog"; | ||
|
|
@@ -33,30 +38,83 @@ export function northArrowControl() { | |
| return new Control({ element: element }); | ||
| } | ||
|
|
||
| export function resetControl(listener: any, icon: string) { | ||
| function makeIcon(src: string) { | ||
| const image = document.createElement("img"); | ||
| image.src = src; | ||
| image.alt = ""; | ||
| return image; | ||
| } | ||
|
|
||
| function makeButton(title: string, listener: () => void, icon?: string) { | ||
| const button = document.createElement("button"); | ||
| button.title = "Reset map view"; | ||
| button.type = "button"; | ||
| button.title = title; | ||
|
|
||
| if (icon === "unicode") { | ||
| button.innerHTML = "↺"; | ||
| } else { | ||
| const image = document.createElement("img"); | ||
| image.className = "reset-icon"; | ||
| image.src = trashCanIcon; | ||
| button.appendChild(image); | ||
| } | ||
| if (icon) button.appendChild(makeIcon(icon)); | ||
|
|
||
| // this is an internal event listener, so doesn't need to be removed later | ||
| // ref https://lit.dev/docs/components/lifecycle/#disconnectedcallback | ||
| button.addEventListener("click", listener, false); | ||
|
|
||
| return button; | ||
| } | ||
|
|
||
| function makeButtonControl(className: string, button: HTMLButtonElement) { | ||
| const element = document.createElement("div"); | ||
| element.className = "reset-control ol-unselectable ol-control"; | ||
| element.className = `${className} ol-unselectable ol-control`; | ||
| element.appendChild(button); | ||
|
|
||
| return new Control({ element: element }); | ||
| } | ||
|
|
||
| function makeResetButton(listener: () => void) { | ||
| const button = makeButton("Reset map view", listener, filterCenterFocusIcon); | ||
| button.classList.add("reset-button"); | ||
| return button; | ||
| } | ||
|
|
||
| /** | ||
| * Icons for the default OL attribution control's expand & collapse button | ||
| */ | ||
| export function attributionLabels() { | ||
| return { | ||
| label: makeIcon(infoIcon), | ||
| collapseLabel: makeIcon(closeIcon), | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Default OL zoom control with a 'reset view' button between zoom in & out | ||
| */ | ||
| export class ZoomWithResetControl extends Zoom { | ||
| constructor(listener: () => void, showReset = true) { | ||
| super({ | ||
| zoomInLabel: makeIcon(addIcon), | ||
| zoomOutLabel: makeIcon(removeIcon), | ||
| }); | ||
| if (showReset) { | ||
| this.element.insertBefore( | ||
| makeResetButton(listener), | ||
| this.element.lastElementChild, | ||
| ); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Standalone 'reset view' control, used when the zoom control is disabled | ||
| */ | ||
| export function resetControl(listener: () => void) { | ||
| return makeButtonControl("reset-control", makeResetButton(listener)); | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice and flexible, thanks for accounting for various "modes" here 🙌 |
||
|
|
||
| export function deleteControl(listener: () => void) { | ||
| return makeButtonControl( | ||
| "delete-control", | ||
| makeButton("Delete all points", listener, deleteIcon), | ||
| ); | ||
| } | ||
|
|
||
| PrintDialog.prototype.scales = { | ||
| // @ts-ignore | ||
| 100: "1/100", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| Icons are sourced from [Font-GIS](https://viglino.github.io/font-gis/?fg=arrow-o) and [Carbon Design System](https://carbondesignsystem.com/guidelines/icons/library) | ||
| Icons are sourced from [Font-GIS](https://viglino.github.io/font-gis/?fg=arrow-o), [Carbon Design System](https://carbondesignsystem.com/guidelines/icons/library) and [Material Icons](https://mui.com/material-ui/material-icons/) (Apache 2.0) |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,10 +19,13 @@ import { Vector as VectorSource, XYZ } from "ol/source"; | |
| import { Circle, Fill, Icon, Stroke, Style, Text } from "ol/style"; | ||
| import View from "ol/View"; | ||
| import { | ||
| attributionLabels, | ||
| deleteControl, | ||
| northArrowControl, | ||
| PrintControl, | ||
| resetControl, | ||
| scaleControl, | ||
| ZoomWithResetControl, | ||
| } from "./controls"; | ||
| import { | ||
| configureDraw, | ||
|
|
@@ -239,12 +242,21 @@ export class MyMap extends LitElement { | |
| @property({ type: Boolean }) | ||
| hideResetControl = false; | ||
|
|
||
| /** | ||
| * @deprecated - the reset control now always resets the view only, use `hideDeleteControl` to control deleting drawings | ||
| */ | ||
| @property({ type: Boolean }) | ||
| resetViewOnly = false; | ||
|
|
||
| /** | ||
| * @deprecated - the reset control always uses the "filter center focus" icon now, and deleting drawings is handled by the separate delete control | ||
| */ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We've been doing |
||
| @property({ type: String }) | ||
| resetControlImage: ResetControlImageEnum = "unicode"; | ||
|
|
||
| @property({ type: Boolean }) | ||
| hideDeleteControl = false; | ||
|
|
||
| @property({ type: Boolean }) | ||
| staticMode = false; | ||
|
|
||
|
|
@@ -377,8 +389,9 @@ export class MyMap extends LitElement { | |
| attribution: true, | ||
| attributionOptions: { | ||
| collapsed: this.collapseAttributions, | ||
| ...attributionLabels(), | ||
| }, | ||
| zoom: !this.staticMode, | ||
| zoom: false, // added below with a custom reset button | ||
| rotate: false, // alternatively uses custom prop `showNorthArrow` | ||
| }), | ||
| interactions: defaultInteractions({ | ||
|
|
@@ -404,31 +417,52 @@ export class MyMap extends LitElement { | |
| const draw = configureDraw(this.drawType, this.drawPointer, this.drawColor); | ||
| const modify = configureModify(this.drawPointer, this.drawColor); | ||
|
|
||
| // Add a custom 'reset' control to the map | ||
| // Ensure the draw interaction is active, keeping snap as the last interaction added | ||
| const enableDraw = () => { | ||
| if (map.getInteractions().getArray().includes(draw)) return; | ||
| map.addInteraction(draw); | ||
| map.removeInteraction(snap); | ||
| map.addInteraction(snap); | ||
| }; | ||
|
|
||
| // Track whether a shape is part-way through being drawn | ||
| let isSketching = false; | ||
| draw.on("drawstart", () => (isSketching = true)); | ||
| draw.on(["drawend", "drawabort"], () => (isSketching = false)); | ||
|
|
||
| const handleDelete = () => { | ||
| if (isSketching) draw.abortDrawing(); | ||
| if (drawingSource.getFeatures().length === 0) return; | ||
|
|
||
| drawingSource.clear(); | ||
| this.dispatch("geojsonChange", {}); | ||
| enableDraw(); | ||
| }; | ||
|
|
||
| // Reset the view port of the map based on available data or center/zoom by default | ||
| const handleReset = () => { | ||
| // Reset the view port of the map based on available data or center/zoom by default | ||
| if (this.showFeaturesAtPoint) { | ||
| fitToData(map, outlineSource, this.featureBuffer); | ||
| } else if (geojsonSource.getFeatures().length > 0) { | ||
| fitToData(map, geojsonSource, this.geojsonBuffer); | ||
| } else if (this.resetViewOnly && drawingSource.getFeatures().length > 0) { | ||
| } else if (this.drawMode && drawingSource.getFeatures().length > 0) { | ||
| fitToData(map, drawingSource, this.drawGeojsonDataBuffer); | ||
| } else { | ||
| map.getView().setCenter(centerCoordinate); | ||
| map.getView().setZoom(this.zoom); | ||
| } | ||
|
|
||
| // If in drawMode, also clear features from the drawingSource by default | ||
| if (this.drawMode && !this.resetViewOnly) { | ||
| drawingSource.clear(); | ||
| this.dispatch("geojsonChange", {}); | ||
| map.addInteraction(draw); | ||
| map.addInteraction(snap); | ||
| } | ||
| }; | ||
|
|
||
| if (!this.hideResetControl) { | ||
| map.addControl(resetControl(handleReset, this.resetControlImage)); | ||
| if (!this.staticMode) { | ||
| map.addControl( | ||
| new ZoomWithResetControl(handleReset, !this.hideResetControl), | ||
| ); | ||
| } else if (!this.hideResetControl) { | ||
| map.addControl(resetControl(handleReset)); | ||
| } | ||
|
|
||
| if (this.drawMode && !this.hideDeleteControl) { | ||
| map.addControl(deleteControl(handleDelete)); | ||
| } | ||
|
|
||
| // add custom scale line and north arrow controls to the map | ||
|
|
@@ -905,9 +939,11 @@ export class MyMap extends LitElement { | |
| <div | ||
| id="${this.id}" | ||
| class="map" | ||
| role="${this.staticMode && !this.collapseAttributions | ||
| ? "presentation" | ||
| : "application"}" | ||
| role="${ | ||
| this.staticMode && !this.collapseAttributions | ||
| ? "presentation" | ||
| : "application" | ||
| }" | ||
| tabindex="${this.staticMode && !this.collapseAttributions ? -1 : 0}" | ||
| data-testid="${this.dataTestId}" | ||
| />` | ||
|
|
@@ -918,9 +954,11 @@ export class MyMap extends LitElement { | |
| <div | ||
| id="${this.id}" | ||
| class="map" | ||
| role="${this.staticMode && !this.collapseAttributions | ||
| ? "presentation" | ||
| : "application"}" | ||
| role="${ | ||
| this.staticMode && !this.collapseAttributions | ||
| ? "presentation" | ||
| : "application" | ||
| }" | ||
| tabindex="${this.staticMode && !this.collapseAttributions ? -1 : 0}" | ||
| data-testid="${this.dataTestId}" | ||
| />`; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice to retire lingering
unicodehere 😅 💅