Accessibility: port MoveWizard to Svelte as it handles tab cycling correctly
This commit is contained in:
parent
f77a06f317
commit
307549b593
3 changed files with 280 additions and 281 deletions
144
src/UI/Popup/MoveWizard.svelte
Normal file
144
src/UI/Popup/MoveWizard.svelte
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
<script lang="ts">
|
||||||
|
|
||||||
|
import { UIEventSource } from "../../Logic/UIEventSource";
|
||||||
|
import type { MoveReason } from "./MoveWizard";
|
||||||
|
import { MoveWizardState } from "./MoveWizard";
|
||||||
|
|
||||||
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||||
|
import ToSvelte from "../Base/ToSvelte.svelte";
|
||||||
|
import Tr from "../Base/Tr.svelte";
|
||||||
|
import Translations from "../i18n/Translations";
|
||||||
|
import Move from "../../assets/svg/Move.svelte";
|
||||||
|
import Move_not_allowed from "../../assets/svg/Move_not_allowed.svelte";
|
||||||
|
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||||
|
import { XCircleIcon } from "@babeard/svelte-heroicons/solid";
|
||||||
|
import type { MapProperties } from "../../Models/MapProperties";
|
||||||
|
import type { Feature, Point } from "geojson";
|
||||||
|
import { GeoOperations } from "../../Logic/GeoOperations";
|
||||||
|
import LocationInput from "../InputElement/Helpers/LocationInput.svelte";
|
||||||
|
import OpenBackgroundSelectorButton from "../BigComponents/OpenBackgroundSelectorButton.svelte";
|
||||||
|
import Geosearch from "../BigComponents/Geosearch.svelte";
|
||||||
|
import Move_confirm from "../../assets/svg/Move_confirm.svelte";
|
||||||
|
import If from "../Base/If.svelte";
|
||||||
|
import Constants from "../../Models/Constants";
|
||||||
|
|
||||||
|
|
||||||
|
export let state: SpecialVisualizationState;
|
||||||
|
|
||||||
|
export let layer: LayerConfig;
|
||||||
|
export let featureToMove: Feature<Point>;
|
||||||
|
|
||||||
|
let id: string = featureToMove.properties.id;
|
||||||
|
let currentStep: "start" | "reason" | "pick_location" | "moved" = "start";
|
||||||
|
const t = Translations.t.move;
|
||||||
|
const reason = new UIEventSource<MoveReason>(undefined);
|
||||||
|
let [lon, lat] = GeoOperations.centerpointCoordinates(featureToMove);
|
||||||
|
|
||||||
|
let newLocation = new UIEventSource<{ lon: number; lat: number }>(undefined);
|
||||||
|
|
||||||
|
function initMapProperties() {
|
||||||
|
return <any>{
|
||||||
|
allowMoving: new UIEventSource(true),
|
||||||
|
allowRotating: new UIEventSource(false),
|
||||||
|
allowZooming: new UIEventSource(true),
|
||||||
|
bounds: new UIEventSource(undefined),
|
||||||
|
location: new UIEventSource({ lon, lat }),
|
||||||
|
minzoom: new UIEventSource($reason.minZoom),
|
||||||
|
rasterLayer: state.mapProperties.rasterLayer,
|
||||||
|
zoom: new UIEventSource($reason?.startZoom ?? 16)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let moveWizardState = new MoveWizardState(id, layer.allowMove, state);
|
||||||
|
let notAllowed = moveWizardState.moveDisallowedReason;
|
||||||
|
let currentMapProperties: MapProperties = undefined;
|
||||||
|
</script>
|
||||||
|
{#if moveWizardState.reasons.length > 0}
|
||||||
|
|
||||||
|
{#if $notAllowed}
|
||||||
|
<div class="flex m-2 p-2 rounded-lg bg-gray-200">
|
||||||
|
<Move_not_allowed class="h-8 w-8 m-2" />
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<Tr t={t.cannotBeMoved} />
|
||||||
|
<Tr t={$notAllowed} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if currentStep === "start"}
|
||||||
|
{#if moveWizardState.reasons.length === 1}
|
||||||
|
<button class="flex" on:click={() => {reason.setData(moveWizardState.reasons[0]); currentStep = "pick_location"}}>
|
||||||
|
<ToSvelte construct={moveWizardState.reasons[0].icon.SetStyle("height: 1.5rem; width: 1.5rem;")}></ToSvelte>
|
||||||
|
<Tr t={Translations.T(moveWizardState.reasons[0].invitingText)} />
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<button class="flex" on:click={() => {currentStep = "reason"}}>
|
||||||
|
<Move class="w-6 h-6" />
|
||||||
|
<Tr t={t.inviteToMove.generic} />
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
{:else if currentStep === "reason"}
|
||||||
|
<div class="flex flex-col interactive border-interactive p-2">
|
||||||
|
|
||||||
|
<Tr cls="text-lg font-bold" t={t.whyMove} />
|
||||||
|
{#each moveWizardState.reasons as reasonSpec}
|
||||||
|
<button on:click={() => {reason.setData(reasonSpec); currentStep = "pick_location"}}>
|
||||||
|
<ToSvelte construct={reasonSpec.icon.SetClass("w-16 h-16 pr-2")} />
|
||||||
|
<Tr t={reasonSpec.text} />
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{:else if currentStep === "pick_location"}
|
||||||
|
<div class="flex flex-col border-interactive interactive p-2">
|
||||||
|
<Tr cls="text-lg font-bold" t={t.moveTitle} />
|
||||||
|
|
||||||
|
|
||||||
|
<div class="relative w-full h-64">
|
||||||
|
<LocationInput mapProperties={currentMapProperties = initMapProperties()} value={newLocation}
|
||||||
|
initialCoordinate={{lon, lat}} />
|
||||||
|
<div class="absolute bottom-0 left-0">
|
||||||
|
<OpenBackgroundSelectorButton {state} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if $reason.includeSearch}
|
||||||
|
<Geosearch bounds={ currentMapProperties.bounds} clearAfterView={false} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<If condition={currentMapProperties.zoom.mapD(zoom => zoom >= Constants.minZoomLevelToAddNewPoint)}>
|
||||||
|
|
||||||
|
<button class="flex flex primary w-full"
|
||||||
|
on:click={() => {moveWizardState.moveFeature(newLocation.data, reason.data, featureToMove)}}>
|
||||||
|
<Move class="w-6 h-6 mr-2" />
|
||||||
|
<Tr t={t.confirmMove} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div slot="else" class="alert">
|
||||||
|
<Tr t={t.zoomInFurther} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</If>
|
||||||
|
|
||||||
|
<button class="w-full" on:click={() => {currentStep= "start"}}>
|
||||||
|
<XCircleIcon class="w-6 h-6 mr-2" />
|
||||||
|
<Tr t={t.cancel} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{:else if currentStep === "moved"}
|
||||||
|
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<Tr cls="thanks" t={t.pointIsMoved} />
|
||||||
|
<button on:click={() => {currentStep = "reason"}}>
|
||||||
|
<Move />
|
||||||
|
<Tr t={t.inviteToMoveAgain} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/if}
|
||||||
|
{/if}
|
|
@ -1,281 +0,0 @@
|
||||||
import { SubtleButton } from "../Base/SubtleButton"
|
|
||||||
import Combine from "../Base/Combine"
|
|
||||||
import Svg from "../../Svg"
|
|
||||||
import Toggle from "../Input/Toggle"
|
|
||||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
|
||||||
import Translations from "../i18n/Translations"
|
|
||||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
|
||||||
import { Translation } from "../i18n/Translation"
|
|
||||||
import BaseUIElement from "../BaseUIElement"
|
|
||||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
|
||||||
import ChangeLocationAction from "../../Logic/Osm/Actions/ChangeLocationAction"
|
|
||||||
import MoveConfig from "../../Models/ThemeConfig/MoveConfig"
|
|
||||||
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
|
|
||||||
import { And } from "../../Logic/Tags/And"
|
|
||||||
import { Tag } from "../../Logic/Tags/Tag"
|
|
||||||
import { LoginToggle } from "./LoginButton"
|
|
||||||
import { SpecialVisualizationState } from "../SpecialVisualization"
|
|
||||||
import { Feature, Point } from "geojson"
|
|
||||||
import { OsmTags } from "../../Models/OsmFeature"
|
|
||||||
import SvelteUIElement from "../Base/SvelteUIElement"
|
|
||||||
import { MapProperties } from "../../Models/MapProperties"
|
|
||||||
import LocationInput from "../InputElement/Helpers/LocationInput.svelte"
|
|
||||||
import Geosearch from "../BigComponents/Geosearch.svelte"
|
|
||||||
import Constants from "../../Models/Constants"
|
|
||||||
import OpenBackgroundSelectorButton from "../BigComponents/OpenBackgroundSelectorButton.svelte"
|
|
||||||
|
|
||||||
interface MoveReason {
|
|
||||||
text: Translation | string
|
|
||||||
invitingText: Translation | string
|
|
||||||
icon: BaseUIElement
|
|
||||||
changesetCommentValue: string
|
|
||||||
lockBounds: true | boolean
|
|
||||||
includeSearch: false | boolean
|
|
||||||
background: undefined | "map" | "photo" | string | string[]
|
|
||||||
startZoom: number
|
|
||||||
minZoom: number
|
|
||||||
eraseAddressFields: false | boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class MoveWizard extends Toggle {
|
|
||||||
/**
|
|
||||||
* The UI-element which helps moving a point
|
|
||||||
*/
|
|
||||||
constructor(
|
|
||||||
featureToMove: Feature<Point>,
|
|
||||||
tags: UIEventSource<OsmTags>,
|
|
||||||
state: SpecialVisualizationState,
|
|
||||||
options: MoveConfig
|
|
||||||
) {
|
|
||||||
const t = Translations.t.move
|
|
||||||
|
|
||||||
const reasons: MoveReason[] = []
|
|
||||||
if (options.enableRelocation) {
|
|
||||||
reasons.push({
|
|
||||||
text: t.reasons.reasonRelocation,
|
|
||||||
invitingText: t.inviteToMove.reasonRelocation,
|
|
||||||
icon: Svg.relocation_svg(),
|
|
||||||
changesetCommentValue: "relocated",
|
|
||||||
lockBounds: false,
|
|
||||||
background: undefined,
|
|
||||||
includeSearch: true,
|
|
||||||
startZoom: 12,
|
|
||||||
minZoom: 6,
|
|
||||||
eraseAddressFields: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (options.enableImproveAccuracy) {
|
|
||||||
reasons.push({
|
|
||||||
text: t.reasons.reasonInaccurate,
|
|
||||||
invitingText: t.inviteToMove.reasonInaccurate,
|
|
||||||
icon: Svg.crosshair_svg(),
|
|
||||||
changesetCommentValue: "improve_accuracy",
|
|
||||||
lockBounds: true,
|
|
||||||
includeSearch: false,
|
|
||||||
background: "photo",
|
|
||||||
startZoom: 18,
|
|
||||||
minZoom: 16,
|
|
||||||
eraseAddressFields: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentStep = new UIEventSource<"start" | "reason" | "pick_location" | "moved">(
|
|
||||||
"start"
|
|
||||||
)
|
|
||||||
const moveReason = new UIEventSource<MoveReason>(undefined)
|
|
||||||
let moveButton: BaseUIElement
|
|
||||||
if (reasons.length === 1) {
|
|
||||||
const reason = reasons[0]
|
|
||||||
moveReason.setData(reason)
|
|
||||||
moveButton = new SubtleButton(
|
|
||||||
reason.icon.SetStyle("height: 1.5rem; width: 1.5rem;"),
|
|
||||||
Translations.T(reason.invitingText)
|
|
||||||
).onClick(() => {
|
|
||||||
currentStep.setData("pick_location")
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
moveButton = new SubtleButton(
|
|
||||||
Svg.move_svg().SetStyle("width: 1.5rem; height: 1.5rem"),
|
|
||||||
t.inviteToMove.generic
|
|
||||||
).onClick(() => {
|
|
||||||
currentStep.setData("reason")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const moveAgainButton = new SubtleButton(Svg.move_svg(), t.inviteToMoveAgain).onClick(
|
|
||||||
() => {
|
|
||||||
currentStep.setData("reason")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const selectReason = new Combine(
|
|
||||||
reasons.map((r) =>
|
|
||||||
new SubtleButton(r.icon, r.text).onClick(() => {
|
|
||||||
moveReason.setData(r)
|
|
||||||
currentStep.setData("pick_location")
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
const cancelButton = new SubtleButton(Svg.close_svg(), t.cancel).onClick(() =>
|
|
||||||
currentStep.setData("start")
|
|
||||||
)
|
|
||||||
|
|
||||||
const [lon, lat] = GeoOperations.centerpointCoordinates(featureToMove)
|
|
||||||
const locationInput = moveReason.map((reason) => {
|
|
||||||
if (reason === undefined) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapProperties: Partial<MapProperties> = {
|
|
||||||
minzoom: new UIEventSource(reason.minZoom),
|
|
||||||
zoom: new UIEventSource(reason?.startZoom ?? 16),
|
|
||||||
location: new UIEventSource({ lon, lat }),
|
|
||||||
bounds: new UIEventSource(undefined),
|
|
||||||
rasterLayer: state.mapProperties.rasterLayer,
|
|
||||||
}
|
|
||||||
const value = new UIEventSource<{ lon: number; lat: number }>(undefined)
|
|
||||||
const locationInput = new Combine([
|
|
||||||
new SvelteUIElement(LocationInput, {
|
|
||||||
mapProperties,
|
|
||||||
value,
|
|
||||||
initialCoordinate: { lon, lat },
|
|
||||||
}).SetClass("w-full h-full"),
|
|
||||||
new SvelteUIElement(OpenBackgroundSelectorButton, { state }).SetClass(
|
|
||||||
"absolute bottom-0 left-0"
|
|
||||||
),
|
|
||||||
]).SetClass("relative w-full h-full")
|
|
||||||
|
|
||||||
let searchPanel: BaseUIElement = undefined
|
|
||||||
if (reason.includeSearch) {
|
|
||||||
searchPanel = new SvelteUIElement(Geosearch, {
|
|
||||||
bounds: mapProperties.bounds,
|
|
||||||
clearAfterView: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
locationInput.SetStyle("height: 17.5rem")
|
|
||||||
|
|
||||||
const confirmMove = new SubtleButton(Svg.move_confirm_svg(), t.confirmMove)
|
|
||||||
confirmMove.onClick(async () => {
|
|
||||||
const loc = value.data
|
|
||||||
await state.changes.applyAction(
|
|
||||||
new ChangeLocationAction(featureToMove.properties.id, [loc.lon, loc.lat], {
|
|
||||||
reason: reason.changesetCommentValue,
|
|
||||||
theme: state.layout.id,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
featureToMove.properties._lat = loc.lat
|
|
||||||
featureToMove.properties._lon = loc.lon
|
|
||||||
featureToMove.geometry.coordinates = [loc.lon, loc.lat]
|
|
||||||
if (reason.eraseAddressFields) {
|
|
||||||
await state.changes.applyAction(
|
|
||||||
new ChangeTagAction(
|
|
||||||
featureToMove.properties.id,
|
|
||||||
new And([
|
|
||||||
new Tag("addr:housenumber", ""),
|
|
||||||
new Tag("addr:street", ""),
|
|
||||||
new Tag("addr:city", ""),
|
|
||||||
new Tag("addr:postcode", ""),
|
|
||||||
]),
|
|
||||||
featureToMove.properties,
|
|
||||||
{
|
|
||||||
changeType: "relocated",
|
|
||||||
theme: state.layout.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
state.featureProperties.getStore(id).ping()
|
|
||||||
currentStep.setData("moved")
|
|
||||||
state.mapProperties.location.setData(loc)
|
|
||||||
})
|
|
||||||
const zoomInFurhter = t.zoomInFurther.SetClass("alert block m-6")
|
|
||||||
return new Combine([
|
|
||||||
searchPanel,
|
|
||||||
locationInput,
|
|
||||||
new Toggle(
|
|
||||||
confirmMove,
|
|
||||||
zoomInFurhter,
|
|
||||||
mapProperties.zoom.map((zoom) => zoom >= Constants.minZoomLevelToAddNewPoint)
|
|
||||||
),
|
|
||||||
]).SetClass("flex flex-col")
|
|
||||||
})
|
|
||||||
|
|
||||||
const dialogClasses = "p-2 md:p-4 m-2 border border-gray-400 rounded-xl flex flex-col"
|
|
||||||
|
|
||||||
const moveFlow = new LoginToggle(
|
|
||||||
new VariableUiElement(
|
|
||||||
currentStep.map((currentStep) => {
|
|
||||||
switch (currentStep) {
|
|
||||||
case "start":
|
|
||||||
return moveButton
|
|
||||||
case "reason":
|
|
||||||
return new Combine([
|
|
||||||
t.whyMove.SetClass("text-lg font-bold"),
|
|
||||||
selectReason,
|
|
||||||
cancelButton,
|
|
||||||
]).SetClass(dialogClasses)
|
|
||||||
case "pick_location":
|
|
||||||
return new Combine([
|
|
||||||
t.moveTitle.SetClass("text-lg font-bold"),
|
|
||||||
new VariableUiElement(locationInput),
|
|
||||||
cancelButton,
|
|
||||||
]).SetClass(dialogClasses)
|
|
||||||
case "moved":
|
|
||||||
return new Combine([
|
|
||||||
t.pointIsMoved.SetClass("thanks"),
|
|
||||||
moveAgainButton,
|
|
||||||
]).SetClass("flex flex-col")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
),
|
|
||||||
undefined,
|
|
||||||
state
|
|
||||||
)
|
|
||||||
let id = featureToMove.properties.id
|
|
||||||
const backend = state.osmConnection._oauth_config.url
|
|
||||||
if (id.startsWith(backend)) {
|
|
||||||
id = id.substring(backend.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
const moveDisallowedReason = new UIEventSource<BaseUIElement>(undefined)
|
|
||||||
if (id.startsWith("way")) {
|
|
||||||
moveDisallowedReason.setData(t.isWay)
|
|
||||||
} else if (id.startsWith("relation")) {
|
|
||||||
moveDisallowedReason.setData(t.isRelation)
|
|
||||||
} else if (id.indexOf("-") < 0) {
|
|
||||||
state.osmObjectDownloader.DownloadReferencingWays(id).then((referencing) => {
|
|
||||||
if (referencing.length > 0) {
|
|
||||||
console.log("Got a referencing way, move not allowed")
|
|
||||||
moveDisallowedReason.setData(t.partOfAWay)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
state.osmObjectDownloader.DownloadReferencingRelations(id).then((partOf) => {
|
|
||||||
if (partOf.length > 0) {
|
|
||||||
moveDisallowedReason.setData(t.partOfRelation)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
super(
|
|
||||||
moveFlow,
|
|
||||||
new Combine([
|
|
||||||
Svg.move_not_allowed_svg().SetStyle("height: 2rem").SetClass("m-2"),
|
|
||||||
new Combine([
|
|
||||||
t.cannotBeMoved,
|
|
||||||
new VariableUiElement(moveDisallowedReason).SetClass("subtle"),
|
|
||||||
]).SetClass("flex flex-col"),
|
|
||||||
]).SetClass("flex m-2 p-2 rounded-lg bg-gray-200"),
|
|
||||||
moveDisallowedReason.map((r) => r === undefined)
|
|
||||||
)
|
|
||||||
|
|
||||||
const self = this
|
|
||||||
currentStep.addCallback((state) => {
|
|
||||||
if (state === "start") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.ScrollIntoView()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
136
src/UI/Popup/MoveWizardState.ts
Normal file
136
src/UI/Popup/MoveWizardState.ts
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
import Svg from "../../Svg"
|
||||||
|
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||||
|
import Translations from "../i18n/Translations"
|
||||||
|
import { Translation } from "../i18n/Translation"
|
||||||
|
import BaseUIElement from "../BaseUIElement"
|
||||||
|
import ChangeLocationAction from "../../Logic/Osm/Actions/ChangeLocationAction"
|
||||||
|
import MoveConfig from "../../Models/ThemeConfig/MoveConfig"
|
||||||
|
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
|
||||||
|
import { And } from "../../Logic/Tags/And"
|
||||||
|
import { Tag } from "../../Logic/Tags/Tag"
|
||||||
|
import { SpecialVisualizationState } from "../SpecialVisualization"
|
||||||
|
import { Feature, Point } from "geojson"
|
||||||
|
|
||||||
|
export interface MoveReason {
|
||||||
|
text: Translation | string
|
||||||
|
invitingText: Translation | string
|
||||||
|
icon: BaseUIElement
|
||||||
|
changesetCommentValue: string
|
||||||
|
lockBounds: true | boolean
|
||||||
|
includeSearch: false | boolean
|
||||||
|
background: undefined | "map" | "photo" | string | string[]
|
||||||
|
startZoom: number
|
||||||
|
minZoom: number
|
||||||
|
eraseAddressFields: false | boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MoveWizardState {
|
||||||
|
public readonly reasons: ReadonlyArray<MoveReason>
|
||||||
|
|
||||||
|
public readonly moveDisallowedReason = new UIEventSource<Translation>(undefined)
|
||||||
|
private readonly _state: SpecialVisualizationState
|
||||||
|
|
||||||
|
constructor(id: string, options: MoveConfig, state: SpecialVisualizationState) {
|
||||||
|
this._state = state
|
||||||
|
const t = Translations.t.move
|
||||||
|
|
||||||
|
this.reasons = MoveWizardState.initReasons(options)
|
||||||
|
|
||||||
|
if (this.reasons.length > 0) {
|
||||||
|
this.checkIsAllowed(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static initReasons(options: MoveConfig): MoveReason[] {
|
||||||
|
const t = Translations.t.move
|
||||||
|
|
||||||
|
const reasons: MoveReason[] = []
|
||||||
|
if (options.enableRelocation) {
|
||||||
|
reasons.push({
|
||||||
|
text: t.reasons.reasonRelocation,
|
||||||
|
invitingText: t.inviteToMove.reasonRelocation,
|
||||||
|
icon: Svg.relocation_svg(),
|
||||||
|
changesetCommentValue: "relocated",
|
||||||
|
lockBounds: false,
|
||||||
|
background: undefined,
|
||||||
|
includeSearch: true,
|
||||||
|
startZoom: 12,
|
||||||
|
minZoom: 6,
|
||||||
|
eraseAddressFields: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (options.enableImproveAccuracy) {
|
||||||
|
reasons.push({
|
||||||
|
text: t.reasons.reasonInaccurate,
|
||||||
|
invitingText: t.inviteToMove.reasonInaccurate,
|
||||||
|
icon: Svg.crosshair_svg(),
|
||||||
|
changesetCommentValue: "improve_accuracy",
|
||||||
|
lockBounds: true,
|
||||||
|
includeSearch: false,
|
||||||
|
background: "photo",
|
||||||
|
startZoom: 18,
|
||||||
|
minZoom: 16,
|
||||||
|
eraseAddressFields: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return reasons
|
||||||
|
}
|
||||||
|
|
||||||
|
public async moveFeature(
|
||||||
|
loc: { lon: number; lat: number },
|
||||||
|
reason: MoveReason,
|
||||||
|
featureToMove: Feature<Point>
|
||||||
|
) {
|
||||||
|
const state = this._state
|
||||||
|
await state.changes.applyAction(
|
||||||
|
new ChangeLocationAction(featureToMove.properties.id, [loc.lon, loc.lat], {
|
||||||
|
reason: reason.changesetCommentValue,
|
||||||
|
theme: state.layout.id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
featureToMove.properties._lat = loc.lat
|
||||||
|
featureToMove.properties._lon = loc.lon
|
||||||
|
featureToMove.geometry.coordinates = [loc.lon, loc.lat]
|
||||||
|
if (reason.eraseAddressFields) {
|
||||||
|
await state.changes.applyAction(
|
||||||
|
new ChangeTagAction(
|
||||||
|
featureToMove.properties.id,
|
||||||
|
new And([
|
||||||
|
new Tag("addr:housenumber", ""),
|
||||||
|
new Tag("addr:street", ""),
|
||||||
|
new Tag("addr:city", ""),
|
||||||
|
new Tag("addr:postcode", ""),
|
||||||
|
]),
|
||||||
|
featureToMove.properties,
|
||||||
|
{
|
||||||
|
changeType: "relocated",
|
||||||
|
theme: state.layout.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
state.featureProperties.getStore(featureToMove.properties.id)?.ping()
|
||||||
|
state.mapProperties.location.setData(loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkIsAllowed(id: string) {
|
||||||
|
const t = Translations.t.move
|
||||||
|
if (id.startsWith("way")) {
|
||||||
|
this.moveDisallowedReason.setData(t.isWay)
|
||||||
|
} else if (id.startsWith("relation")) {
|
||||||
|
this.moveDisallowedReason.setData(t.isRelation)
|
||||||
|
} else if (id.indexOf("-") < 0) {
|
||||||
|
this._state.osmObjectDownloader.DownloadReferencingWays(id).then((referencing) => {
|
||||||
|
if (referencing.length > 0) {
|
||||||
|
this.moveDisallowedReason.setData(t.partOfAWay)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this._state.osmObjectDownloader.DownloadReferencingRelations(id).then((partOf) => {
|
||||||
|
if (partOf.length > 0) {
|
||||||
|
this.moveDisallowedReason.setData(t.partOfRelation)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue