import {UIEventSource} from "../../Logic/UIEventSource"; import {OsmConnection} from "../../Logic/Osm/OsmConnection"; import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"; import BaseUIElement from "../BaseUIElement"; import LocationInput from "../Input/LocationInput"; import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; import {BBox} from "../../Logic/BBox"; import {TagUtils} from "../../Logic/Tags/TagUtils"; import {SubtleButton} from "../Base/SubtleButton"; import Combine from "../Base/Combine"; import Translations from "../i18n/Translations"; import Svg from "../../Svg"; import Toggle from "../Input/Toggle"; import SimpleAddUI, {PresetInfo} from "../BigComponents/SimpleAddUI"; import BaseLayer from "../../Models/BaseLayer"; import Img from "../Base/Img"; import Title from "../Base/Title"; import {GlobalFilter} from "../../Logic/State/MapState"; import {VariableUiElement} from "../Base/VariableUIElement"; import {Tag} from "../../Logic/Tags/Tag"; export default class ConfirmLocationOfPoint extends Combine { constructor( state: { globalFilters: UIEventSource; featureSwitchIsTesting: UIEventSource; osmConnection: OsmConnection, featurePipeline: FeaturePipeline, backgroundLayer?: UIEventSource }, filterViewIsOpened: UIEventSource, preset: PresetInfo, confirmText: BaseUIElement, loc: { lon: number, lat: number }, confirm: (tags: any[], location: { lat: number, lon: number }, snapOntoWayId: string) => void, cancel: () => void, closePopup: () => void ) { let preciseInput: LocationInput = undefined if (preset.preciseInput !== undefined) { // Create location input // We uncouple the event source const zloc = {...loc, zoom: 19} const locationSrc = new UIEventSource(zloc); let backgroundLayer = new UIEventSource(state?.backgroundLayer?.data ?? AvailableBaseLayers.osmCarto); if (preset.preciseInput.preferredBackground) { const defaultBackground = AvailableBaseLayers.SelectBestLayerAccordingTo(locationSrc, new UIEventSource(preset.preciseInput.preferredBackground)); // Note that we _break the link_ here, as the minimap will take care of the switching! backgroundLayer.setData(defaultBackground.data) } let snapToFeatures: UIEventSource<{ feature: any }[]> = undefined let mapBounds: UIEventSource = undefined if (preset.preciseInput.snapToLayers && preset.preciseInput.snapToLayers.length > 0) { snapToFeatures = new UIEventSource<{ feature: any }[]>([]) mapBounds = new UIEventSource(undefined) } const tags = TagUtils.KVtoProperties(preset.tags ?? []); preciseInput = new LocationInput({ mapBackground: backgroundLayer, centerLocation: locationSrc, snapTo: snapToFeatures, snappedPointTags: tags, maxSnapDistance: preset.preciseInput.maxSnapDistance, bounds: mapBounds }) preciseInput.installBounds(preset.boundsFactor ?? 0.25, true) preciseInput.SetClass("rounded-xl overflow-hidden border border-gray").SetStyle("height: 18rem; max-height: 50vh") if (preset.preciseInput.snapToLayers && preset.preciseInput.snapToLayers.length > 0) { // We have to snap to certain layers. // Lets fetch them let loadedBbox: BBox = undefined mapBounds?.addCallbackAndRunD(bbox => { if (loadedBbox !== undefined && bbox.isContainedIn(loadedBbox)) { // All is already there // return; } bbox = bbox.pad(Math.max(preset.boundsFactor ?? 0.25, 2), Math.max(preset.boundsFactor ?? 0.25, 2)); loadedBbox = bbox; const allFeatures: { feature: any }[] = [] preset.preciseInput.snapToLayers.forEach(layerId => { console.log("Snapping to", layerId) state.featurePipeline.GetFeaturesWithin(layerId, bbox)?.forEach(feats => allFeatures.push(...feats.map(f => ({feature: f})))) }) console.log("Snapping to", allFeatures) snapToFeatures.setData(allFeatures) }) } } let confirmButton: BaseUIElement = new SubtleButton(preset.icon(), new Combine([ confirmText, Translations.t.general.add.warnVisibleForEveryone.Clone().SetClass("alert") ]).SetClass("flex flex-col") ).SetClass("font-bold break-words") .onClick(() => { console.log("The confirmLocationPanel - precise input yielded ", preciseInput?.GetValue()?.data) const globalFilterTagsToAdd: Tag[][] = state.globalFilters.data.filter(gf => gf.onNewPoint !== undefined) .map(gf => gf.onNewPoint.tags) const globalTags : Tag[] = [].concat(...globalFilterTagsToAdd) console.log("Global tags to add are: ", globalTags) confirm([...preset.tags, ...globalTags], preciseInput?.GetValue()?.data ?? loc, preciseInput?.snappedOnto?.data?.properties?.id); }); if (preciseInput !== undefined) { confirmButton = new Combine([preciseInput, confirmButton]) } const openLayerControl = new SubtleButton( Svg.layers_ui(), new Combine([ Translations.t.general.add.layerNotEnabled .Subs({layer: preset.layerToAddTo.layerDef.name}) .SetClass("alert"), Translations.t.general.add.openLayerControl ]) ) .onClick(() => filterViewIsOpened.setData(true)) let openLayerOrConfirm = new Toggle( confirmButton, openLayerControl, preset.layerToAddTo.isDisplayed ) const disableFilter = new SubtleButton( new Combine([ Svg.filter_ui().SetClass("absolute w-full"), Svg.cross_bottom_right_svg().SetClass("absolute red-svg") ]).SetClass("relative"), new Combine( [ Translations.t.general.add.disableFiltersExplanation.Clone(), Translations.t.general.add.disableFilters.Clone().SetClass("text-xl") ] ).SetClass("flex flex-col") ).onClick(() => { const appliedFilters = preset.layerToAddTo.appliedFilters; appliedFilters.data.forEach((_, k) => appliedFilters.data.set(k, undefined)) appliedFilters.ping() cancel() closePopup() }) // We assume the number of global filters won't change during the run of the program for (let i = 0; i < state.globalFilters.data.length; i++) { const hasBeenCheckedOf = new UIEventSource(false); const filterConfirmPanel = new VariableUiElement( state.globalFilters.map(gfs => { const gf = gfs[i] const confirm = gf.onNewPoint?.confirmAddNew?.Subs({preset: preset.title}) return new Combine([ gf.onNewPoint?.safetyCheck, new SubtleButton(Svg.confirm_svg(), confirm).onClick(() => hasBeenCheckedOf.setData(true)) ]) } )) openLayerOrConfirm = new Toggle( openLayerOrConfirm, filterConfirmPanel, state.globalFilters.map(f => hasBeenCheckedOf.data || f[i]?.onNewPoint === undefined, [hasBeenCheckedOf]) ) } const hasActiveFilter = preset.layerToAddTo.appliedFilters .map(appliedFilters => { const activeFilters = Array.from(appliedFilters.values()).filter(f => f?.currentFilter !== undefined); return activeFilters.length === 0; }) // If at least one filter is active which _might_ hide a newly added item, this blocks the preset and requests the filter to be disabled const disableFiltersOrConfirm = new Toggle( openLayerOrConfirm, disableFilter, hasActiveFilter) const tagInfo = SimpleAddUI.CreateTagInfoFor(preset, state.osmConnection); const cancelButton = new SubtleButton(Svg.close_ui(), Translations.t.general.cancel ).onClick(cancel) let examples: BaseUIElement = undefined; if (preset.exampleImages !== undefined && preset.exampleImages.length > 0) { examples = new Combine([ new Title(preset.exampleImages.length == 1 ? Translations.t.general.example : Translations.t.general.examples), new Combine(preset.exampleImages.map(img => new Img(img).SetClass("h-64 m-1 w-auto rounded-lg"))).SetClass("flex flex-wrap items-stretch") ]) } super([ new Toggle( Translations.t.general.testing.SetClass("alert"), undefined, state.featureSwitchIsTesting ), disableFiltersOrConfirm, cancelButton, preset.description, examples, tagInfo ]) this.SetClass("flex flex-col") } }