forked from MapComplete/MapComplete
Add buttons to quickly swap background layers (also in the locationInput), move copyright into home panel, split privacy policy to seperate welcome message tab
This commit is contained in:
parent
1d0fbe701c
commit
37c0129a6d
22 changed files with 477 additions and 183 deletions
|
@ -1,24 +1,140 @@
|
|||
import Combine from "../Base/Combine";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
|
||||
import State from "../../State";
|
||||
import Loc from "../../Models/Loc";
|
||||
import Svg from "../../Svg";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import Toggle from "../Input/Toggle";
|
||||
import BaseLayer from "../../Models/BaseLayer";
|
||||
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import {GeoOperations} from "../../Logic/GeoOperations";
|
||||
|
||||
class SingleLayerSelectionButton extends Toggle {
|
||||
constructor(state: {
|
||||
locationControl: UIEventSource<Loc>
|
||||
}, prefered: string) {
|
||||
const layer = AvailableBaseLayers.SelectBestLayerAccordingTo(state.locationControl, new UIEventSource(prefered))
|
||||
const layerIsCorrectType = layer.map(bl => bl?.category === prefered)
|
||||
|
||||
public readonly activate: () => void
|
||||
|
||||
/**
|
||||
*
|
||||
* The SingeLayerSelectionButton also acts as an actor to keep the layers in check
|
||||
*
|
||||
* It works the following way:
|
||||
*
|
||||
* - It has a boolean state to indicate wether or not the button is active
|
||||
* - It keeps track of the available layers
|
||||
*/
|
||||
constructor(
|
||||
locationControl: UIEventSource<Loc>,
|
||||
options: {
|
||||
currentBackground: UIEventSource<BaseLayer>,
|
||||
preferredType: string,
|
||||
preferredLayer?: BaseLayer,
|
||||
notAvailable?: () => void
|
||||
}) {
|
||||
|
||||
|
||||
const prefered = options.preferredType
|
||||
const previousLayer = new UIEventSource(options.preferredLayer)
|
||||
|
||||
const unselected = SingleLayerSelectionButton.getIconFor(prefered)
|
||||
.SetClass("rounded-lg p-1 h-12 w-12 overflow-hidden subtle-background border-invisible")
|
||||
|
||||
const selected = SingleLayerSelectionButton.getIconFor(prefered)
|
||||
.SetClass("rounded-lg p-1 h-12 w-12 overflow-hidden subtle-background border-attention-catch")
|
||||
|
||||
|
||||
const available = AvailableBaseLayers
|
||||
.SelectBestLayerAccordingTo(locationControl, new UIEventSource<string | string[]>(options.preferredType))
|
||||
|
||||
let toggle: BaseUIElement = new Toggle(
|
||||
selected,
|
||||
unselected,
|
||||
options.currentBackground.map(bg => bg.category === options.preferredType)
|
||||
)
|
||||
|
||||
|
||||
super(
|
||||
SingleLayerSelectionButton.getIconFor(prefered).SetClass("rounded-full p-3 h-10 w-10"),
|
||||
toggle,
|
||||
undefined,
|
||||
layerIsCorrectType
|
||||
available.map(av => av.category === options.preferredType)
|
||||
);
|
||||
|
||||
/**
|
||||
* Checks that the previous layer is still usable on the current location.
|
||||
* If not, clears the 'previousLayer'
|
||||
*/
|
||||
function checkPreviousLayer() {
|
||||
if (previousLayer.data === undefined) {
|
||||
return
|
||||
}
|
||||
if (previousLayer.data.feature === null || previousLayer.data.feature === undefined) {
|
||||
// Global layer
|
||||
return
|
||||
}
|
||||
const loc = locationControl.data
|
||||
if (!GeoOperations.inside([loc.lon, loc.lat], previousLayer.data.feature)) {
|
||||
// The previous layer is out of bounds
|
||||
previousLayer.setData(undefined)
|
||||
}
|
||||
}
|
||||
|
||||
unselected.onClick(() => {
|
||||
// Note: a check if 'available' has the correct type is not needed:
|
||||
// Unselected will _not_ be visible if availableBaseLayer has a wrong type!
|
||||
checkPreviousLayer()
|
||||
|
||||
previousLayer.setData(previousLayer.data ?? available.data)
|
||||
options.currentBackground.setData(previousLayer.data)
|
||||
})
|
||||
|
||||
|
||||
available.addCallbackAndRunD(availableLayer => {
|
||||
|
||||
if (previousLayer.data === undefined) {
|
||||
// PreviousLayer is unset -> we definitively weren't using this category -> no need to switch
|
||||
return;
|
||||
}
|
||||
if (options.currentBackground.data?.id !== previousLayer.data?.id) {
|
||||
// The previously used layer doesn't match the current layer -> no need to switch
|
||||
return;
|
||||
}
|
||||
|
||||
if (availableLayer.category === options.preferredType) {
|
||||
// Allright, we can set this different layer
|
||||
options.currentBackground.setData(availableLayer)
|
||||
previousLayer.setData(availableLayer)
|
||||
} else {
|
||||
// Uh oh - no correct layer is available... We pass the torch!
|
||||
if (options.notAvailable !== undefined) {
|
||||
options.notAvailable()
|
||||
} else {
|
||||
// Fallback to OSM carto
|
||||
options.currentBackground.setData(AvailableBaseLayers.osmCarto)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
options.currentBackground.addCallbackAndRunD(background => {
|
||||
if (background.category === options.preferredType) {
|
||||
previousLayer.setData(background)
|
||||
}
|
||||
})
|
||||
|
||||
this.activate = () => {
|
||||
checkPreviousLayer()
|
||||
if (available.data.category !== options.preferredType) {
|
||||
// This object can't help either - pass the torch!
|
||||
if (options.notAvailable !== undefined) {
|
||||
options.notAvailable()
|
||||
} else {
|
||||
// Fallback to OSM carto
|
||||
options.currentBackground.setData(AvailableBaseLayers.osmCarto)
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
previousLayer.setData(previousLayer.data ?? available.data)
|
||||
options.currentBackground.setData(previousLayer.data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static getIconFor(type: string) {
|
||||
|
@ -27,27 +143,54 @@ class SingleLayerSelectionButton extends Toggle {
|
|||
return Svg.generic_map_svg()
|
||||
case "photo":
|
||||
return Svg.satellite_svg()
|
||||
case "osmbasedmap":
|
||||
return Svg.osm_logo_svg()
|
||||
default:
|
||||
return Svg.generic_map_svg()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default class BackgroundMapSwitch extends VariableUiElement {
|
||||
export default class BackgroundMapSwitch extends Combine {
|
||||
|
||||
constructor(
|
||||
state: {
|
||||
locationControl: UIEventSource<Loc>
|
||||
locationControl: UIEventSource<Loc>,
|
||||
backgroundLayer: UIEventSource<BaseLayer>
|
||||
},
|
||||
options?: {
|
||||
allowedLayers?: UIEventSource<string[]>
|
||||
}
|
||||
currentBackground: UIEventSource<BaseLayer>,
|
||||
preferredCategory?: string
|
||||
) {
|
||||
options = options ?? {}
|
||||
options.allowedLayers = options.allowedLayers ?? new UIEventSource<string[]>(["photo", "map"])
|
||||
const allowedCategories = ["osmbasedmap", "photo", "map"]
|
||||
|
||||
const previousLayer = state.backgroundLayer.data
|
||||
const buttons = []
|
||||
let activatePrevious: () => void = undefined
|
||||
for (const category of allowedCategories) {
|
||||
let preferredLayer = undefined
|
||||
if (previousLayer.category === category) {
|
||||
preferredLayer = previousLayer
|
||||
}
|
||||
|
||||
super(options.allowedLayers.map(layers => new Combine(layers.map(prefered => new SingleLayerSelectionButton(state, prefered)))));
|
||||
const button = new SingleLayerSelectionButton(
|
||||
state.locationControl,
|
||||
{
|
||||
preferredType: category,
|
||||
preferredLayer: preferredLayer,
|
||||
currentBackground: currentBackground,
|
||||
notAvailable: activatePrevious
|
||||
})
|
||||
activatePrevious = button.activate
|
||||
if (category === preferredCategory) {
|
||||
button.activate()
|
||||
}
|
||||
buttons.push(button)
|
||||
}
|
||||
|
||||
// Selects the initial map
|
||||
|
||||
super(buttons)
|
||||
this.SetClass("flex")
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue