Refactoring: fix 'delete' and 'move'-buttons as special elements
This commit is contained in:
parent
466dd16568
commit
8a1f0599d9
19 changed files with 317 additions and 296 deletions
|
@ -38,9 +38,9 @@ export class MenuState {
|
||||||
[],
|
[],
|
||||||
(str) => MenuState._menuviewTabs.indexOf(<any>str)
|
(str) => MenuState._menuviewTabs.indexOf(<any>str)
|
||||||
)
|
)
|
||||||
this.themeIsOpened.addCallbackAndRun((isOpen) => {
|
this.menuIsOpened.addCallbackAndRun((isOpen) => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
this.highlightedLayerInFilters.setData(undefined)
|
this.highlightedUserSetting.setData(undefined)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.themeViewTab.addCallbackAndRun((tab) => {
|
this.themeViewTab.addCallbackAndRun((tab) => {
|
||||||
|
|
|
@ -234,9 +234,9 @@ class ExpandTagRendering extends Conversion<
|
||||||
|
|
||||||
if (typeof layer.source !== "string") {
|
if (typeof layer.source !== "string") {
|
||||||
if (found.condition === undefined) {
|
if (found.condition === undefined) {
|
||||||
found.condition = layer.source.osmTags
|
found.condition = layer.source["osmTags"]
|
||||||
} else {
|
} else {
|
||||||
found.condition = { and: [found.condition, layer.source.osmTags] }
|
found.condition = { and: [found.condition, layer.source["osmTags"]] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,7 +436,7 @@ class DetectInline extends DesugaringStep<QuestionableTagRenderingConfigJson> {
|
||||||
if (typeof json.render === "string") {
|
if (typeof json.render === "string") {
|
||||||
spec = { "*": json.render }
|
spec = { "*": json.render }
|
||||||
} else {
|
} else {
|
||||||
spec = json.render
|
spec = <Record<string, string>>json.render
|
||||||
}
|
}
|
||||||
const errors: string[] = []
|
const errors: string[] = []
|
||||||
for (const key in spec) {
|
for (const key in spec) {
|
||||||
|
@ -480,7 +480,10 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
|
||||||
json: LayerConfigJson,
|
json: LayerConfigJson,
|
||||||
context: string
|
context: string
|
||||||
): { result: LayerConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } {
|
): { result: LayerConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } {
|
||||||
if (json.tagRenderings === undefined) {
|
if (
|
||||||
|
json.tagRenderings === undefined ||
|
||||||
|
json.tagRenderings.some((tr) => tr["id"] === "leftover-questions")
|
||||||
|
) {
|
||||||
return { result: json }
|
return { result: json }
|
||||||
}
|
}
|
||||||
json = JSON.parse(JSON.stringify(json))
|
json = JSON.parse(JSON.stringify(json))
|
||||||
|
@ -500,7 +503,6 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
|
||||||
const errors: string[] = []
|
const errors: string[] = []
|
||||||
const warnings: string[] = []
|
const warnings: string[] = []
|
||||||
if (noLabels.length > 1) {
|
if (noLabels.length > 1) {
|
||||||
console.log(json.tagRenderings)
|
|
||||||
errors.push(
|
errors.push(
|
||||||
"At " +
|
"At " +
|
||||||
context +
|
context +
|
||||||
|
@ -572,6 +574,45 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
|
||||||
|
constructor() {
|
||||||
|
super(
|
||||||
|
"Add some editing elements, such as the delete button or the move button if they are configured. These used to be handled by the feature info box, but this has been replaced by special visualisation elements",
|
||||||
|
[],
|
||||||
|
"AddEditingElements"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
convert(
|
||||||
|
json: LayerConfigJson,
|
||||||
|
context: string
|
||||||
|
): { result: LayerConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } {
|
||||||
|
json = JSON.parse(JSON.stringify(json))
|
||||||
|
|
||||||
|
if (json.allowSplit && !ValidationUtils.hasSpecialVisualisation(json, "split_button")) {
|
||||||
|
json.tagRenderings.push({
|
||||||
|
id: "split-button",
|
||||||
|
render: { "*": "{split_button()}" },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.allowMove && !ValidationUtils.hasSpecialVisualisation(json, "move_button")) {
|
||||||
|
json.tagRenderings.push({
|
||||||
|
id: "move-button",
|
||||||
|
render: { "*": "{move_button()}" },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (json.deletion && !ValidationUtils.hasSpecialVisualisation(json, "delete_button")) {
|
||||||
|
json.tagRenderings.push({
|
||||||
|
id: "delete-button",
|
||||||
|
render: { "*": "{delete_button()}" },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return { result: json }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class ExpandRewrite<T> extends Conversion<T | RewritableConfigJson<T>, T[]> {
|
export class ExpandRewrite<T> extends Conversion<T | RewritableConfigJson<T>, T[]> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("Applies a rewrite", [], "ExpandRewrite")
|
super("Applies a rewrite", [], "ExpandRewrite")
|
||||||
|
@ -1064,6 +1105,36 @@ class PreparePointRendering extends Fuse<PointRenderingConfigJson | LineRenderin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
|
||||||
|
private readonly _state: DesugaringContext
|
||||||
|
|
||||||
|
constructor(state: DesugaringContext) {
|
||||||
|
super(
|
||||||
|
"Adds a default 'minimap'-element to the tagrenderings if none of the elements define such a minimap",
|
||||||
|
["tagRenderings"],
|
||||||
|
"AddMiniMap"
|
||||||
|
)
|
||||||
|
this._state = state
|
||||||
|
}
|
||||||
|
|
||||||
|
convert(layerConfig: LayerConfigJson, context: string): { result: LayerConfigJson } {
|
||||||
|
if (!layerConfig.tagRenderings) {
|
||||||
|
return { result: layerConfig }
|
||||||
|
}
|
||||||
|
const state = this._state
|
||||||
|
const hasMinimap = ValidationUtils.hasSpecialVisualisation(layerConfig, "minimap")
|
||||||
|
if (!hasMinimap) {
|
||||||
|
layerConfig = { ...layerConfig }
|
||||||
|
layerConfig.tagRenderings = [...layerConfig.tagRenderings]
|
||||||
|
layerConfig.tagRenderings.push(state.tagRenderings.get("minimap"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
result: layerConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class PrepareLayer extends Fuse<LayerConfigJson> {
|
export class PrepareLayer extends Fuse<LayerConfigJson> {
|
||||||
constructor(state: DesugaringContext) {
|
constructor(state: DesugaringContext) {
|
||||||
super(
|
super(
|
||||||
|
@ -1072,6 +1143,9 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
|
||||||
new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
|
new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
|
||||||
new On("tagRenderings", (layer) => new Concat(new ExpandTagRendering(state, layer))),
|
new On("tagRenderings", (layer) => new Concat(new ExpandTagRendering(state, layer))),
|
||||||
new On("tagRenderings", new Each(new DetectInline())),
|
new On("tagRenderings", new Each(new DetectInline())),
|
||||||
|
new AddQuestionBox(),
|
||||||
|
new AddMiniMap(state),
|
||||||
|
new AddEditingElements(),
|
||||||
new On("mapRendering", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
|
new On("mapRendering", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
|
||||||
new On<(PointRenderingConfigJson | LineRenderingConfigJson)[], LayerConfigJson>(
|
new On<(PointRenderingConfigJson | LineRenderingConfigJson)[], LayerConfigJson>(
|
||||||
"mapRendering",
|
"mapRendering",
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
SetDefault,
|
SetDefault,
|
||||||
} from "./Conversion"
|
} from "./Conversion"
|
||||||
import { LayoutConfigJson } from "../Json/LayoutConfigJson"
|
import { LayoutConfigJson } from "../Json/LayoutConfigJson"
|
||||||
import { AddQuestionBox, PrepareLayer } from "./PrepareLayer"
|
import { PrepareLayer } from "./PrepareLayer"
|
||||||
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
||||||
import { Utils } from "../../../Utils"
|
import { Utils } from "../../../Utils"
|
||||||
import Constants from "../../Constants"
|
import Constants from "../../Constants"
|
||||||
|
@ -295,56 +295,6 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
|
|
||||||
private readonly _state: DesugaringContext
|
|
||||||
|
|
||||||
constructor(state: DesugaringContext) {
|
|
||||||
super(
|
|
||||||
"Adds a default 'minimap'-element to the tagrenderings if none of the elements define such a minimap",
|
|
||||||
["tagRenderings"],
|
|
||||||
"AddMiniMap"
|
|
||||||
)
|
|
||||||
this._state = state
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if this tag rendering has a minimap in some language.
|
|
||||||
* Note: this minimap can be hidden by conditions
|
|
||||||
*
|
|
||||||
* AddMiniMap.hasMinimap({render: "{minimap()}"}) // => true
|
|
||||||
* AddMiniMap.hasMinimap({render: {en: "{minimap()}"}}) // => true
|
|
||||||
* AddMiniMap.hasMinimap({render: {en: "{minimap()}", nl: "{minimap()}"}}) // => true
|
|
||||||
* AddMiniMap.hasMinimap({render: {en: "{minimap()}", nl: "No map for the dutch!"}}) // => true
|
|
||||||
* AddMiniMap.hasMinimap({render: "{minimap()}"}) // => true
|
|
||||||
* AddMiniMap.hasMinimap({render: "{minimap(18,featurelist)}"}) // => true
|
|
||||||
* AddMiniMap.hasMinimap({mappings: [{if: "xyz=abc",then: "{minimap(18,featurelist)}"}]}) // => true
|
|
||||||
* AddMiniMap.hasMinimap({render: "Some random value {key}"}) // => false
|
|
||||||
* AddMiniMap.hasMinimap({render: "Some random value {minimap}"}) // => false
|
|
||||||
*/
|
|
||||||
static hasMinimap(renderingConfig: TagRenderingConfigJson): boolean {
|
|
||||||
return ValidationUtils.getSpecialVisualisations(renderingConfig).some(
|
|
||||||
(vis) => vis.funcName === "minimap"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
convert(layerConfig: LayerConfigJson, context: string): { result: LayerConfigJson } {
|
|
||||||
const state = this._state
|
|
||||||
const hasMinimap =
|
|
||||||
layerConfig.tagRenderings?.some((tr) =>
|
|
||||||
AddMiniMap.hasMinimap(<TagRenderingConfigJson>tr)
|
|
||||||
) ?? true
|
|
||||||
if (!hasMinimap) {
|
|
||||||
layerConfig = { ...layerConfig }
|
|
||||||
layerConfig.tagRenderings = [...layerConfig.tagRenderings]
|
|
||||||
layerConfig.tagRenderings.push(state.tagRenderings.get("minimap"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
result: layerConfig,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AddContextToTranslationsInLayout extends DesugaringStep<LayoutConfigJson> {
|
class AddContextToTranslationsInLayout extends DesugaringStep<LayoutConfigJson> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(
|
super(
|
||||||
|
@ -660,9 +610,7 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> {
|
||||||
? new Pass("AddDefaultLayers is disabled due to the set flag")
|
? new Pass("AddDefaultLayers is disabled due to the set flag")
|
||||||
: new AddDefaultLayers(state),
|
: new AddDefaultLayers(state),
|
||||||
new AddDependencyLayersToTheme(state),
|
new AddDependencyLayersToTheme(state),
|
||||||
new AddImportLayers(),
|
new AddImportLayers()
|
||||||
new On("layers", new Each(new AddQuestionBox())),
|
|
||||||
new On("layers", new Each(new AddMiniMap(state)))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,22 @@ import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
|
||||||
import { Utils } from "../../../Utils"
|
import { Utils } from "../../../Utils"
|
||||||
import SpecialVisualizations from "../../../UI/SpecialVisualizations"
|
import SpecialVisualizations from "../../../UI/SpecialVisualizations"
|
||||||
import { RenderingSpecification, SpecialVisualization } from "../../../UI/SpecialVisualization"
|
import { RenderingSpecification, SpecialVisualization } from "../../../UI/SpecialVisualization"
|
||||||
|
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
||||||
|
|
||||||
export default class ValidationUtils {
|
export default class ValidationUtils {
|
||||||
|
public static hasSpecialVisualisation(
|
||||||
|
layer: LayerConfigJson,
|
||||||
|
specialVisualisation: string
|
||||||
|
): boolean {
|
||||||
|
return (
|
||||||
|
layer.tagRenderings?.some((tagRendering) =>
|
||||||
|
ValidationUtils.getSpecialVisualisations(<TagRenderingConfigJson>tagRendering).some(
|
||||||
|
(vis) => vis.funcName === specialVisualisation
|
||||||
|
)
|
||||||
|
) ?? false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives all the (function names of) used special visualisations
|
* Gives all the (function names of) used special visualisations
|
||||||
* @param renderingConfig
|
* @param renderingConfig
|
||||||
|
@ -15,6 +29,7 @@ export default class ValidationUtils {
|
||||||
(spec) => spec["func"]
|
(spec) => spec["func"]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getSpecialVisualsationsWithArgs(
|
public static getSpecialVisualsationsWithArgs(
|
||||||
renderingConfig: TagRenderingConfigJson
|
renderingConfig: TagRenderingConfigJson
|
||||||
): RenderingSpecification[] {
|
): RenderingSpecification[] {
|
||||||
|
|
|
@ -90,7 +90,7 @@ export default class LayerConfig extends WithContextLoader {
|
||||||
|
|
||||||
if (json.source === "special" || json.source === "special:library") {
|
if (json.source === "special" || json.source === "special:library") {
|
||||||
this.source = null
|
this.source = null
|
||||||
} else if (json.source.osmTags === undefined) {
|
} else if (json.source["osmTags"] === undefined) {
|
||||||
throw (
|
throw (
|
||||||
"Layer " +
|
"Layer " +
|
||||||
this.id +
|
this.id +
|
||||||
|
@ -122,8 +122,8 @@ export default class LayerConfig extends WithContextLoader {
|
||||||
}
|
}
|
||||||
this.syncSelection = json.syncSelection ?? "no"
|
this.syncSelection = json.syncSelection ?? "no"
|
||||||
if (typeof json.source !== "string") {
|
if (typeof json.source !== "string") {
|
||||||
this.maxAgeOfCache = json.source.maxCacheAge ?? 24 * 60 * 60 * 30
|
this.maxAgeOfCache = json.source["maxCacheAge"] ?? 24 * 60 * 60 * 30
|
||||||
const osmTags = TagUtils.Tag(json.source.osmTags, context + "source.osmTags")
|
const osmTags = TagUtils.Tag(json.source["osmTags"], context + "source.osmTags")
|
||||||
if (osmTags.isNegative()) {
|
if (osmTags.isNegative()) {
|
||||||
throw (
|
throw (
|
||||||
context +
|
context +
|
||||||
|
|
|
@ -248,7 +248,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
new UIEventSource<Record<string, string>>(last_click.properties)
|
new UIEventSource<Record<string, string>>(last_click.properties)
|
||||||
)
|
)
|
||||||
new ShowDataLayer(this.map, {
|
new ShowDataLayer(this.map, {
|
||||||
features: last_click,
|
features: new FilteringFeatureSource(last_click_layer, last_click),
|
||||||
doShowLayer: new ImmutableStore(true),
|
doShowLayer: new ImmutableStore(true),
|
||||||
layer: last_click_layer.layerDef,
|
layer: last_click_layer.layerDef,
|
||||||
selectedElement: this.selectedElement,
|
selectedElement: this.selectedElement,
|
||||||
|
|
|
@ -10,56 +10,58 @@
|
||||||
import Hotkeys from "../Base/Hotkeys";
|
import Hotkeys from "../Base/Hotkeys";
|
||||||
import { Geocoding } from "../../Logic/Osm/Geocoding";
|
import { Geocoding } from "../../Logic/Osm/Geocoding";
|
||||||
import { BBox } from "../../Logic/BBox";
|
import { BBox } from "../../Logic/BBox";
|
||||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
import { GeoIndexedStoreForLayer } from "../../Logic/FeatureSource/Actors/GeoIndexedStore";
|
||||||
|
|
||||||
export let state: SpecialVisualizationState
|
export let perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer> | undefined = undefined;
|
||||||
export let bounds: UIEventSource<BBox>
|
export let bounds: UIEventSource<BBox>;
|
||||||
export let selectedElement: UIEventSource<Feature>;
|
export let selectedElement: UIEventSource<Feature> | undefined = undefined;
|
||||||
export let selectedLayer: UIEventSource<LayerConfig>;
|
export let selectedLayer: UIEventSource<LayerConfig> | undefined = undefined;
|
||||||
|
|
||||||
let searchContents: string = undefined;
|
let searchContents: string = undefined;
|
||||||
|
|
||||||
let isRunning: boolean = false;
|
let isRunning: boolean = false;
|
||||||
|
|
||||||
let inputElement: HTMLInputElement;
|
let inputElement: HTMLInputElement;
|
||||||
|
|
||||||
let feedback: string = undefined
|
let feedback: string = undefined;
|
||||||
|
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{ ctrl: "F" },
|
{ ctrl: "F" },
|
||||||
Translations.t.hotkeyDocumentation.selectSearch,
|
Translations.t.hotkeyDocumentation.selectSearch,
|
||||||
() => {
|
() => {
|
||||||
inputElement?.focus()
|
inputElement?.focus();
|
||||||
inputElement?.select()
|
inputElement?.select();
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
|
|
||||||
async function performSearch() {
|
async function performSearch() {
|
||||||
try {
|
try {
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
searchContents = searchContents?.trim() ?? ""
|
searchContents = searchContents?.trim() ?? "";
|
||||||
if (searchContents === "") {
|
if (searchContents === "") {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const result = await Geocoding.Search(searchContents, bounds.data)
|
const result = await Geocoding.Search(searchContents, bounds.data);
|
||||||
if (result.length == 0) {
|
if (result.length == 0) {
|
||||||
feedback = Translations.t.search.nothing.txt
|
feedback = Translations.t.search.nothing.txt;
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const poi = result[0]
|
const poi = result[0];
|
||||||
const [lat0, lat1, lon0, lon1] = poi.boundingbox
|
const [lat0, lat1, lon0, lon1] = poi.boundingbox;
|
||||||
bounds.set(new BBox([[lon0, lat0], [lon1, lat1]]).pad(0.01))
|
bounds.set(new BBox([[lon0, lat0], [lon1, lat1]]).pad(0.01));
|
||||||
const id = poi.osm_type + "/" + poi.osm_id
|
if (perLayer !== undefined) {
|
||||||
const perLayer = state.perLayer
|
const id = poi.osm_type + "/" + poi.osm_id;
|
||||||
const layers = Array.from(perLayer.values())
|
const layers = Array.from(perLayer?.values() ?? []);
|
||||||
for (const layer of layers) {
|
for (const layer of layers) {
|
||||||
const found = layer.features.data.find(f => f.properties.id === id)
|
const found = layer.features.data.find(f => f.properties.id === id);
|
||||||
selectedElement.setData(found)
|
selectedElement?.setData(found);
|
||||||
selectedLayer.setData(layer.layer.layerDef)
|
selectedLayer?.setData(layer.layer.layerDef);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
feedback = Translations.t.search.error.txt
|
feedback = Translations.t.search.error.txt;
|
||||||
} finally {
|
} finally {
|
||||||
isRunning = false;
|
isRunning = false;
|
||||||
}
|
}
|
||||||
|
@ -72,7 +74,7 @@
|
||||||
|
|
||||||
{#if isRunning}
|
{#if isRunning}
|
||||||
<Loading>{Translations.t.general.search.searching}</Loading>
|
<Loading>{Translations.t.general.search.searching}</Loading>
|
||||||
{:else if feedback !== undefined}
|
{:else if feedback !== undefined}
|
||||||
<div class="alert" on:click={() => feedback = undefined}>
|
<div class="alert" on:click={() => feedback = undefined}>
|
||||||
{feedback}
|
{feedback}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -177,9 +177,7 @@ export class ImageUploadFlow extends Toggle {
|
||||||
)
|
)
|
||||||
.onClick(() => {
|
.onClick(() => {
|
||||||
console.log("Opening the license settings... ")
|
console.log("Opening the license settings... ")
|
||||||
ScrollableFullScreen.collapse()
|
state.guistate.openUsersettings("picture-license")
|
||||||
DefaultGuiState.state.userInfoIsOpened.setData(true)
|
|
||||||
DefaultGuiState.state.userInfoFocusedQuestion.setData("picture-license")
|
|
||||||
})
|
})
|
||||||
.SetClass("underline"),
|
.SetClass("underline"),
|
||||||
]).SetStyle("font-size:small;"),
|
]).SetStyle("font-size:small;"),
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
import { Map as MlMap } from "maplibre-gl";
|
import { Map as MlMap } from "maplibre-gl";
|
||||||
import { MapLibreAdaptor } from "../../Map/MapLibreAdaptor";
|
import { MapLibreAdaptor } from "../../Map/MapLibreAdaptor";
|
||||||
import MaplibreMap from "../../Map/MaplibreMap.svelte";
|
import MaplibreMap from "../../Map/MaplibreMap.svelte";
|
||||||
import Svg from "../../../Svg";
|
|
||||||
import ToSvelte from "../../Base/ToSvelte.svelte";
|
|
||||||
import DragInvitation from "../../Base/DragInvitation.svelte";
|
import DragInvitation from "../../Base/DragInvitation.svelte";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,13 +12,13 @@
|
||||||
export let value: UIEventSource<{lon: number, lat: number}>;
|
export let value: UIEventSource<{lon: number, lat: number}>;
|
||||||
export let mapProperties: Partial<MapProperties> & { readonly location: UIEventSource<{ lon: number; lat: number }> } = undefined;
|
export let mapProperties: Partial<MapProperties> & { readonly location: UIEventSource<{ lon: number; lat: number }> } = undefined;
|
||||||
/**
|
/**
|
||||||
* Called when setup is done, cna be used to add layrs to the map
|
* Called when setup is done, can be used to add more layers to the map
|
||||||
*/
|
*/
|
||||||
export let onCreated : (value: Store<{lon: number, lat: number}> , map: Store<MlMap>, mapProperties: MapProperties ) => void
|
export let onCreated : (value: Store<{lon: number, lat: number}> , map: Store<MlMap>, mapProperties: MapProperties ) => void
|
||||||
|
|
||||||
export let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined);
|
export let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined);
|
||||||
let mla = new MapLibreAdaptor(map, mapProperties);
|
let mla = new MapLibreAdaptor(map, mapProperties);
|
||||||
|
mapProperties.location.syncWith(value)
|
||||||
if(onCreated){
|
if(onCreated){
|
||||||
onCreated(value, map, mla)
|
onCreated(value, map, mla)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import PointRenderingConfig from "../../Models/ThemeConfig/PointRenderingConfig"
|
||||||
import { OsmTags } from "../../Models/OsmFeature"
|
import { OsmTags } from "../../Models/OsmFeature"
|
||||||
import { FeatureSource } from "../../Logic/FeatureSource/FeatureSource"
|
import { FeatureSource } from "../../Logic/FeatureSource/FeatureSource"
|
||||||
import { BBox } from "../../Logic/BBox"
|
import { BBox } from "../../Logic/BBox"
|
||||||
import { Feature } from "geojson"
|
import { Feature, Point } from "geojson"
|
||||||
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
|
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
|
||||||
import LineRenderingConfig from "../../Models/ThemeConfig/LineRenderingConfig"
|
import LineRenderingConfig from "../../Models/ThemeConfig/LineRenderingConfig"
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
|
@ -26,6 +26,7 @@ class PointRenderingLayer {
|
||||||
private readonly _onClick: (feature: Feature) => void
|
private readonly _onClick: (feature: Feature) => void
|
||||||
private readonly _allMarkers: Map<string, Marker> = new Map<string, Marker>()
|
private readonly _allMarkers: Map<string, Marker> = new Map<string, Marker>()
|
||||||
private _dirty = false
|
private _dirty = false
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
map: MlMap,
|
map: MlMap,
|
||||||
features: FeatureSource,
|
features: FeatureSource,
|
||||||
|
@ -139,6 +140,18 @@ class PointRenderingLayer {
|
||||||
store
|
store
|
||||||
.map((tags) => this._config.rotationAlignment.GetRenderValue(tags).Subs(tags).txt)
|
.map((tags) => this._config.rotationAlignment.GetRenderValue(tags).Subs(tags).txt)
|
||||||
.addCallbackAndRun((pitchAligment) => marker.setRotationAlignment(pitchAligment))
|
.addCallbackAndRun((pitchAligment) => marker.setRotationAlignment(pitchAligment))
|
||||||
|
if (feature.geometry.type === "Point") {
|
||||||
|
// When the tags get 'pinged', check that the location didn't change
|
||||||
|
store.addCallbackAndRunD(() => {
|
||||||
|
// Check if the location is still the same
|
||||||
|
const oldLoc = marker.getLngLat()
|
||||||
|
const newloc = (<Point>feature.geometry).coordinates
|
||||||
|
if (newloc[0] === oldLoc.lng && newloc[1] === oldLoc.lat) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
marker.setLngLat({ lon: newloc[0], lat: newloc[1] })
|
||||||
|
})
|
||||||
|
}
|
||||||
return marker
|
return marker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,6 +172,7 @@ class LineRenderingLayer {
|
||||||
|
|
||||||
private static readonly lineConfigKeysColor = ["color", "fillColor"] as const
|
private static readonly lineConfigKeysColor = ["color", "fillColor"] as const
|
||||||
private static readonly lineConfigKeysNumber = ["width", "offset"] as const
|
private static readonly lineConfigKeysNumber = ["width", "offset"] as const
|
||||||
|
private static missingIdTriggered = false
|
||||||
private readonly _map: MlMap
|
private readonly _map: MlMap
|
||||||
private readonly _config: LineRenderingConfig
|
private readonly _config: LineRenderingConfig
|
||||||
private readonly _visibility?: Store<boolean>
|
private readonly _visibility?: Store<boolean>
|
||||||
|
@ -167,7 +181,6 @@ class LineRenderingLayer {
|
||||||
private readonly _layername: string
|
private readonly _layername: string
|
||||||
private readonly _listenerInstalledOn: Set<string> = new Set<string>()
|
private readonly _listenerInstalledOn: Set<string> = new Set<string>()
|
||||||
|
|
||||||
private static missingIdTriggered = false
|
|
||||||
constructor(
|
constructor(
|
||||||
map: MlMap,
|
map: MlMap,
|
||||||
features: FeatureSource,
|
features: FeatureSource,
|
||||||
|
@ -248,6 +261,11 @@ class LineRenderingLayer {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
map.on("click", linelayer, (e) => {
|
||||||
|
console.log("Click", e)
|
||||||
|
e.originalEvent["consumed"] = true
|
||||||
|
this._onClick(e.features[0])
|
||||||
|
})
|
||||||
const polylayer = this._layername + "_polygon"
|
const polylayer = this._layername + "_polygon"
|
||||||
map.addLayer({
|
map.addLayer({
|
||||||
source: this._layername,
|
source: this._layername,
|
||||||
|
@ -260,6 +278,10 @@ class LineRenderingLayer {
|
||||||
"fill-opacity": 0.1,
|
"fill-opacity": 0.1,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
map.on("click", polylayer, (e) => {
|
||||||
|
e.originalEvent["consumed"] = true
|
||||||
|
this._onClick(e.features[0])
|
||||||
|
})
|
||||||
|
|
||||||
this._visibility?.addCallbackAndRunD((visible) => {
|
this._visibility?.addCallbackAndRunD((visible) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -20,10 +20,10 @@ import { RadioButton } from "../Input/RadioButton"
|
||||||
import { FixedInputElement } from "../Input/FixedInputElement"
|
import { FixedInputElement } from "../Input/FixedInputElement"
|
||||||
import Title from "../Base/Title"
|
import Title from "../Base/Title"
|
||||||
import { SubstitutedTranslation } from "../SubstitutedTranslation"
|
import { SubstitutedTranslation } from "../SubstitutedTranslation"
|
||||||
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
|
|
||||||
import TagRenderingQuestion from "./TagRenderingQuestion"
|
import TagRenderingQuestion from "./TagRenderingQuestion"
|
||||||
import { OsmId } from "../../Models/OsmFeature"
|
import { OsmId, OsmTags } from "../../Models/OsmFeature"
|
||||||
import { LoginToggle } from "./LoginButton"
|
import { LoginToggle } from "./LoginButton"
|
||||||
|
import { SpecialVisualizationState } from "../SpecialVisualization"
|
||||||
|
|
||||||
export default class DeleteWizard extends Toggle {
|
export default class DeleteWizard extends Toggle {
|
||||||
/**
|
/**
|
||||||
|
@ -41,13 +41,18 @@ export default class DeleteWizard extends Toggle {
|
||||||
* Ideal for the case of "THIS PATH IS ON MY GROUND AND SHOULD BE DELETED IMMEDIATELY OR I WILL GET MY LAWYER" but to mark it as private instead.
|
* Ideal for the case of "THIS PATH IS ON MY GROUND AND SHOULD BE DELETED IMMEDIATELY OR I WILL GET MY LAWYER" but to mark it as private instead.
|
||||||
* (Note that _delete_reason is used as trigger to do actual deletion - setting such a tag WILL delete from the database with that as changeset comment)
|
* (Note that _delete_reason is used as trigger to do actual deletion - setting such a tag WILL delete from the database with that as changeset comment)
|
||||||
*
|
*
|
||||||
* @param id: The id of the element to remove
|
|
||||||
* @param state: the state of the application
|
|
||||||
* @param options softDeletionTags: the tags to apply if the user doesn't have permission to delete, e.g. 'disused:amenity=public_bookcase', 'amenity='. After applying, the element should not be picked up on the map anymore. If undefined, the wizard will only show up if the point can be (hard) deleted
|
|
||||||
*/
|
*/
|
||||||
constructor(id: OsmId, state: FeaturePipelineState, options: DeleteConfig) {
|
constructor(
|
||||||
const deleteAbility = new DeleteabilityChecker(id, state, options.neededChangesets)
|
id: OsmId,
|
||||||
const tagsSource = state.allElements.getEventSourceById(id)
|
tagsSource: UIEventSource<OsmTags>,
|
||||||
|
state: SpecialVisualizationState,
|
||||||
|
options: DeleteConfig
|
||||||
|
) {
|
||||||
|
const deleteAbility = new DeleteabilityChecker(
|
||||||
|
id,
|
||||||
|
state.osmConnection,
|
||||||
|
options.neededChangesets
|
||||||
|
)
|
||||||
|
|
||||||
const isDeleted = new UIEventSource(false)
|
const isDeleted = new UIEventSource(false)
|
||||||
const allowSoftDeletion = !!options.softDeletionTags
|
const allowSoftDeletion = !!options.softDeletionTags
|
||||||
|
@ -62,7 +67,7 @@ export default class DeleteWizard extends Toggle {
|
||||||
if (selected["retagTo"] !== undefined) {
|
if (selected["retagTo"] !== undefined) {
|
||||||
// no _delete_reason is given, which implies that this is _not_ a deletion but merely a retagging via a nonDeleteMapping
|
// no _delete_reason is given, which implies that this is _not_ a deletion but merely a retagging via a nonDeleteMapping
|
||||||
actionToTake = new ChangeTagAction(id, selected["retagTo"], tagsSource.data, {
|
actionToTake = new ChangeTagAction(id, selected["retagTo"], tagsSource.data, {
|
||||||
theme: state?.layoutToUse?.id ?? "unkown",
|
theme: state?.layout?.id ?? "unkown",
|
||||||
changeType: "special-delete",
|
changeType: "special-delete",
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -70,7 +75,7 @@ export default class DeleteWizard extends Toggle {
|
||||||
id,
|
id,
|
||||||
options.softDeletionTags,
|
options.softDeletionTags,
|
||||||
{
|
{
|
||||||
theme: state?.layoutToUse?.id ?? "unkown",
|
theme: state?.layout?.id ?? "unkown",
|
||||||
specialMotivation: selected["deleteReason"],
|
specialMotivation: selected["deleteReason"],
|
||||||
},
|
},
|
||||||
deleteAbility.canBeDeleted.data.canBeDeleted
|
deleteAbility.canBeDeleted.data.canBeDeleted
|
||||||
|
@ -250,7 +255,7 @@ export default class DeleteWizard extends Toggle {
|
||||||
private static constructMultipleChoice(
|
private static constructMultipleChoice(
|
||||||
config: DeleteConfig,
|
config: DeleteConfig,
|
||||||
tagsSource: UIEventSource<Record<string, string>>,
|
tagsSource: UIEventSource<Record<string, string>>,
|
||||||
state: FeaturePipelineState
|
state: SpecialVisualizationState
|
||||||
): InputElement<{ deleteReason: string } | { retagTo: TagsFilter }> {
|
): InputElement<{ deleteReason: string } | { retagTo: TagsFilter }> {
|
||||||
const elements: InputElement<{ deleteReason: string } | { retagTo: TagsFilter }>[] = []
|
const elements: InputElement<{ deleteReason: string } | { retagTo: TagsFilter }>[] = []
|
||||||
|
|
||||||
|
@ -282,19 +287,13 @@ export default class DeleteWizard extends Toggle {
|
||||||
|
|
||||||
class DeleteabilityChecker {
|
class DeleteabilityChecker {
|
||||||
public readonly canBeDeleted: UIEventSource<{ canBeDeleted?: boolean; reason: Translation }>
|
public readonly canBeDeleted: UIEventSource<{ canBeDeleted?: boolean; reason: Translation }>
|
||||||
private readonly _id: string
|
private readonly _id: OsmId
|
||||||
private readonly _allowDeletionAtChangesetCount: number
|
private readonly _allowDeletionAtChangesetCount: number
|
||||||
private readonly _state: {
|
private readonly _osmConnection: OsmConnection
|
||||||
osmConnection: OsmConnection
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(id: OsmId, osmConnection: OsmConnection, allowDeletionAtChangesetCount?: number) {
|
||||||
id: string,
|
|
||||||
state: { osmConnection: OsmConnection },
|
|
||||||
allowDeletionAtChangesetCount?: number
|
|
||||||
) {
|
|
||||||
this._id = id
|
this._id = id
|
||||||
this._state = state
|
this._osmConnection = osmConnection
|
||||||
this._allowDeletionAtChangesetCount = allowDeletionAtChangesetCount ?? Number.MAX_VALUE
|
this._allowDeletionAtChangesetCount = allowDeletionAtChangesetCount ?? Number.MAX_VALUE
|
||||||
|
|
||||||
this.canBeDeleted = new UIEventSource<{ canBeDeleted?: boolean; reason: Translation }>({
|
this.canBeDeleted = new UIEventSource<{ canBeDeleted?: boolean; reason: Translation }>({
|
||||||
|
@ -324,7 +323,7 @@ class DeleteabilityChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does the currently logged in user have enough experience to delete this point?
|
// Does the currently logged in user have enough experience to delete this point?
|
||||||
const deletingPointsOfOtherAllowed = this._state.osmConnection.userDetails.map((ud) => {
|
const deletingPointsOfOtherAllowed = this._osmConnection.userDetails.map((ud) => {
|
||||||
if (ud === undefined) {
|
if (ud === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -347,10 +346,10 @@ class DeleteabilityChecker {
|
||||||
// Not yet downloaded
|
// Not yet downloaded
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const userId = self._state.osmConnection.userDetails.data.uid
|
const userId = self._osmConnection.userDetails.data.uid
|
||||||
return !previous.some((editor) => editor !== userId)
|
return !previous.some((editor) => editor !== userId)
|
||||||
},
|
},
|
||||||
[self._state.osmConnection.userDetails]
|
[self._osmConnection.userDetails]
|
||||||
)
|
)
|
||||||
|
|
||||||
// User allowed OR only edited by self?
|
// User allowed OR only edited by self?
|
||||||
|
|
|
@ -17,7 +17,6 @@ import MoveWizard from "./MoveWizard"
|
||||||
import Toggle from "../Input/Toggle"
|
import Toggle from "../Input/Toggle"
|
||||||
import Lazy from "../Base/Lazy"
|
import Lazy from "../Base/Lazy"
|
||||||
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
|
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
|
||||||
import { Tag } from "../../Logic/Tags/Tag"
|
|
||||||
import Svg from "../../Svg"
|
import Svg from "../../Svg"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
|
|
||||||
|
@ -32,9 +31,6 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
||||||
setHash?: true | boolean
|
setHash?: true | boolean
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (state === undefined) {
|
|
||||||
throw "State is undefined!"
|
|
||||||
}
|
|
||||||
const showAllQuestions = state.featureSwitchShowAllQuestions.map(
|
const showAllQuestions = state.featureSwitchShowAllQuestions.map(
|
||||||
(fsShow) => fsShow || state.showAllQuestionsAtOnce.data,
|
(fsShow) => fsShow || state.showAllQuestionsAtOnce.data,
|
||||||
[state.showAllQuestionsAtOnce]
|
[state.showAllQuestionsAtOnce]
|
||||||
|
@ -98,27 +94,11 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
||||||
private static GenerateMainContent(
|
private static GenerateMainContent(
|
||||||
tags: UIEventSource<any>,
|
tags: UIEventSource<any>,
|
||||||
layerConfig: LayerConfig,
|
layerConfig: LayerConfig,
|
||||||
state: FeaturePipelineState,
|
state: FeaturePipelineState
|
||||||
showAllQuestions?: Store<boolean>
|
|
||||||
): BaseUIElement {
|
): BaseUIElement {
|
||||||
let questionBoxes: Map<string, QuestionBox> = new Map<string, QuestionBox>()
|
let questionBoxes: Map<string, QuestionBox> = new Map<string, QuestionBox>()
|
||||||
const t = Translations.t.general
|
const t = Translations.t.general
|
||||||
const allGroupNames = Utils.Dedup(layerConfig.tagRenderings.map((tr) => tr.group))
|
const allGroupNames = Utils.Dedup(layerConfig.tagRenderings.map((tr) => tr.group))
|
||||||
if (state?.featureSwitchUserbadge?.data ?? true) {
|
|
||||||
const questionSpecs = layerConfig.tagRenderings.filter((tr) => tr.id === "questions")
|
|
||||||
for (const groupName of allGroupNames) {
|
|
||||||
const questions = layerConfig.tagRenderings.filter((tr) => tr.group === groupName)
|
|
||||||
const questionSpec = questionSpecs.filter((tr) => tr.group === groupName)[0]
|
|
||||||
const questionBox = new QuestionBox(state, {
|
|
||||||
tagsSource: tags,
|
|
||||||
tagRenderings: questions,
|
|
||||||
units: layerConfig.units,
|
|
||||||
showAllQuestionsAtOnce:
|
|
||||||
questionSpec?.freeform?.helperArgs["showAllQuestions"] ?? showAllQuestions,
|
|
||||||
})
|
|
||||||
questionBoxes.set(groupName, questionBox)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const withQuestion = layerConfig.tagRenderings.filter(
|
const withQuestion = layerConfig.tagRenderings.filter(
|
||||||
(tr) => tr.question !== undefined
|
(tr) => tr.question !== undefined
|
||||||
|
@ -243,40 +223,6 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
||||||
editElements.push(questionBox)
|
editElements.push(questionBox)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (layerConfig.allowMove) {
|
|
||||||
editElements.push(
|
|
||||||
new VariableUiElement(
|
|
||||||
tags
|
|
||||||
.map((tags) => tags.id)
|
|
||||||
.map((id) => {
|
|
||||||
const feature = state.allElements.ContainingFeatures.get(id)
|
|
||||||
if (feature === undefined) {
|
|
||||||
return "This feature is not register in the state.allElements and cannot be moved"
|
|
||||||
}
|
|
||||||
return new MoveWizard(feature, state, layerConfig.allowMove)
|
|
||||||
})
|
|
||||||
).SetClass("text-base")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layerConfig.deletion) {
|
|
||||||
editElements.push(
|
|
||||||
new VariableUiElement(
|
|
||||||
tags
|
|
||||||
.map((tags) => tags.id)
|
|
||||||
.map((id) => new DeleteWizard(id, state, layerConfig.deletion))
|
|
||||||
).SetClass("text-base")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layerConfig.allowSplit) {
|
|
||||||
editElements.push(
|
|
||||||
new VariableUiElement(
|
|
||||||
tags.map((tags) => tags.id).map((id) => new SplitRoadWizard(id, state))
|
|
||||||
).SetClass("text-base")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
editElements.push(
|
editElements.push(
|
||||||
new VariableUiElement(
|
new VariableUiElement(
|
||||||
state.osmConnection.userDetails
|
state.osmConnection.userDetails
|
||||||
|
@ -302,30 +248,6 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
editElements.push(
|
|
||||||
Toggle.If(state.featureSwitchIsDebugging, () => {
|
|
||||||
const config_all_tags: TagRenderingConfig = new TagRenderingConfig(
|
|
||||||
{ render: "{all_tags()}" },
|
|
||||||
""
|
|
||||||
)
|
|
||||||
const config_download: TagRenderingConfig = new TagRenderingConfig(
|
|
||||||
{ render: "{export_as_geojson()}" },
|
|
||||||
""
|
|
||||||
)
|
|
||||||
const config_id: TagRenderingConfig = new TagRenderingConfig(
|
|
||||||
{ render: "{open_in_iD()}" },
|
|
||||||
""
|
|
||||||
)
|
|
||||||
|
|
||||||
return new Combine([
|
|
||||||
new TagRenderingAnswer(tags, config_all_tags, state),
|
|
||||||
new TagRenderingAnswer(tags, config_download, state),
|
|
||||||
new TagRenderingAnswer(tags, config_id, state),
|
|
||||||
"This is layer " + layerConfig.id,
|
|
||||||
])
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return new Combine(editElements).SetClass("flex flex-col")
|
return new Combine(editElements).SetClass("flex flex-col")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,28 @@
|
||||||
import { SubtleButton } from "../Base/SubtleButton"
|
import { SubtleButton } from "../Base/SubtleButton"
|
||||||
import Combine from "../Base/Combine"
|
import Combine from "../Base/Combine"
|
||||||
import Svg from "../../Svg"
|
import Svg from "../../Svg"
|
||||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
|
||||||
import Toggle from "../Input/Toggle"
|
import Toggle from "../Input/Toggle"
|
||||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||||
import { Translation } from "../i18n/Translation"
|
import { Translation } from "../i18n/Translation"
|
||||||
import BaseUIElement from "../BaseUIElement"
|
import BaseUIElement from "../BaseUIElement"
|
||||||
import LocationInput from "../Input/LocationInput"
|
|
||||||
import Loc from "../../Models/Loc"
|
|
||||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||||
import { OsmObject } from "../../Logic/Osm/OsmObject"
|
import { OsmObject } from "../../Logic/Osm/OsmObject"
|
||||||
import { Changes } from "../../Logic/Osm/Changes"
|
|
||||||
import ChangeLocationAction from "../../Logic/Osm/Actions/ChangeLocationAction"
|
import ChangeLocationAction from "../../Logic/Osm/Actions/ChangeLocationAction"
|
||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
|
||||||
import MoveConfig from "../../Models/ThemeConfig/MoveConfig"
|
import MoveConfig from "../../Models/ThemeConfig/MoveConfig"
|
||||||
import { ElementStorage } from "../../Logic/ElementStorage"
|
|
||||||
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
|
|
||||||
import BaseLayer from "../../Models/BaseLayer"
|
|
||||||
import SearchAndGo from "../BigComponents/SearchAndGo"
|
|
||||||
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
|
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
|
||||||
import { And } from "../../Logic/Tags/And"
|
import { And } from "../../Logic/Tags/And"
|
||||||
import { Tag } from "../../Logic/Tags/Tag"
|
import { Tag } from "../../Logic/Tags/Tag"
|
||||||
import { LoginToggle } from "./LoginButton"
|
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"
|
||||||
|
|
||||||
interface MoveReason {
|
interface MoveReason {
|
||||||
text: Translation | string
|
text: Translation | string
|
||||||
|
@ -43,14 +42,9 @@ export default class MoveWizard extends Toggle {
|
||||||
* The UI-element which helps moving a point
|
* The UI-element which helps moving a point
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
featureToMove: any,
|
featureToMove: Feature<Point>,
|
||||||
state: {
|
tags: UIEventSource<OsmTags>,
|
||||||
osmConnection: OsmConnection
|
state: SpecialVisualizationState,
|
||||||
featureSwitchUserbadge: UIEventSource<boolean>
|
|
||||||
changes: Changes
|
|
||||||
layoutToUse: LayoutConfig
|
|
||||||
allElements: ElementStorage
|
|
||||||
},
|
|
||||||
options: MoveConfig
|
options: MoveConfig
|
||||||
) {
|
) {
|
||||||
const t = Translations.t.move
|
const t = Translations.t.move
|
||||||
|
@ -130,56 +124,38 @@ export default class MoveWizard extends Toggle {
|
||||||
if (reason === undefined) {
|
if (reason === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
const loc = new UIEventSource<Loc>({
|
|
||||||
lon: lon,
|
|
||||||
lat: lat,
|
|
||||||
zoom: reason?.startZoom ?? 16,
|
|
||||||
})
|
|
||||||
|
|
||||||
let background: string[]
|
const mapProperties: Partial<MapProperties> = {
|
||||||
if (typeof reason.background == "string") {
|
minzoom: new UIEventSource(reason.minZoom),
|
||||||
background = [reason.background]
|
zoom: new UIEventSource(reason?.startZoom ?? 16),
|
||||||
} else {
|
location: new UIEventSource({ lon, lat }),
|
||||||
background = reason.background
|
bounds: new UIEventSource(undefined),
|
||||||
}
|
}
|
||||||
|
const value = new UIEventSource<{ lon: number; lat: number }>(undefined)
|
||||||
const preferredBackground = AvailableBaseLayers.SelectBestLayerAccordingTo(
|
const locationInput = new SvelteUIElement(LocationInput, {
|
||||||
loc,
|
mapProperties,
|
||||||
new UIEventSource(background)
|
value,
|
||||||
).data
|
|
||||||
|
|
||||||
const locationInput = new LocationInput({
|
|
||||||
minZoom: reason.minZoom,
|
|
||||||
centerLocation: loc,
|
|
||||||
mapBackground: new UIEventSource<BaseLayer>(preferredBackground), // We detach the layer
|
|
||||||
state: <any>state,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (reason.lockBounds) {
|
|
||||||
locationInput.installBounds(0.05, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
let searchPanel: BaseUIElement = undefined
|
let searchPanel: BaseUIElement = undefined
|
||||||
if (reason.includeSearch) {
|
if (reason.includeSearch) {
|
||||||
searchPanel = new SearchAndGo({
|
searchPanel = new SvelteUIElement(Geosearch, { bounds: mapProperties.bounds })
|
||||||
leafletMap: locationInput.leafletMap,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
locationInput.SetStyle("height: 17.5rem")
|
locationInput.SetStyle("height: 17.5rem")
|
||||||
|
|
||||||
const confirmMove = new SubtleButton(Svg.move_confirm_svg(), t.confirmMove)
|
const confirmMove = new SubtleButton(Svg.move_confirm_svg(), t.confirmMove)
|
||||||
confirmMove.onClick(async () => {
|
confirmMove.onClick(async () => {
|
||||||
const loc = locationInput.GetValue().data
|
const loc = value.data
|
||||||
await state.changes.applyAction(
|
await state.changes.applyAction(
|
||||||
new ChangeLocationAction(featureToMove.properties.id, [loc.lon, loc.lat], {
|
new ChangeLocationAction(featureToMove.properties.id, [loc.lon, loc.lat], {
|
||||||
reason: reason.changesetCommentValue,
|
reason: reason.changesetCommentValue,
|
||||||
theme: state.layoutToUse.id,
|
theme: state.layout.id,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
featureToMove.properties._lat = loc.lat
|
featureToMove.properties._lat = loc.lat
|
||||||
featureToMove.properties._lon = loc.lon
|
featureToMove.properties._lon = loc.lon
|
||||||
|
featureToMove.geometry.coordinates = [loc.lon, loc.lat]
|
||||||
if (reason.eraseAddressFields) {
|
if (reason.eraseAddressFields) {
|
||||||
await state.changes.applyAction(
|
await state.changes.applyAction(
|
||||||
new ChangeTagAction(
|
new ChangeTagAction(
|
||||||
|
@ -193,13 +169,13 @@ export default class MoveWizard extends Toggle {
|
||||||
featureToMove.properties,
|
featureToMove.properties,
|
||||||
{
|
{
|
||||||
changeType: "relocated",
|
changeType: "relocated",
|
||||||
theme: state.layoutToUse.id,
|
theme: state.layout.id,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.allElements.getEventSourceById(id).ping()
|
state.featureProperties.getStore(id).ping()
|
||||||
currentStep.setData("moved")
|
currentStep.setData("moved")
|
||||||
})
|
})
|
||||||
const zoomInFurhter = t.zoomInFurther.SetClass("alert block m-6")
|
const zoomInFurhter = t.zoomInFurther.SetClass("alert block m-6")
|
||||||
|
@ -209,7 +185,7 @@ export default class MoveWizard extends Toggle {
|
||||||
new Toggle(
|
new Toggle(
|
||||||
confirmMove,
|
confirmMove,
|
||||||
zoomInFurhter,
|
zoomInFurhter,
|
||||||
locationInput.GetValue().map((l) => l.zoom >= 19)
|
mapProperties.zoom.map((zoom) => zoom >= Constants.minZoomLevelToAddNewPoint)
|
||||||
),
|
),
|
||||||
]).SetClass("flex flex-col")
|
]).SetClass("flex flex-col")
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,33 +2,27 @@ import Toggle from "../Input/Toggle"
|
||||||
import Svg from "../../Svg"
|
import Svg from "../../Svg"
|
||||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||||
import { SubtleButton } from "../Base/SubtleButton"
|
import { SubtleButton } from "../Base/SubtleButton"
|
||||||
import Minimap from "../Base/Minimap"
|
|
||||||
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"
|
|
||||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||||
import { LeafletMouseEvent } from "leaflet"
|
|
||||||
import Combine from "../Base/Combine"
|
import Combine from "../Base/Combine"
|
||||||
import { Button } from "../Base/Button"
|
import { Button } from "../Base/Button"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
import SplitAction from "../../Logic/Osm/Actions/SplitAction"
|
import SplitAction from "../../Logic/Osm/Actions/SplitAction"
|
||||||
import Title from "../Base/Title"
|
import Title from "../Base/Title"
|
||||||
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
||||||
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"
|
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||||
import { BBox } from "../../Logic/BBox"
|
import { BBox } from "../../Logic/BBox"
|
||||||
import split_point from "../../assets/layers/split_point/split_point.json"
|
import split_point from "../../assets/layers/split_point/split_point.json"
|
||||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||||
import { Changes } from "../../Logic/Osm/Changes"
|
import { Changes } from "../../Logic/Osm/Changes"
|
||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||||
import { ElementStorage } from "../../Logic/ElementStorage"
|
|
||||||
import BaseLayer from "../../Models/BaseLayer"
|
|
||||||
import FilteredLayer from "../../Models/FilteredLayer"
|
import FilteredLayer from "../../Models/FilteredLayer"
|
||||||
import BaseUIElement from "../BaseUIElement"
|
import BaseUIElement from "../BaseUIElement"
|
||||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||||
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
|
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
|
||||||
import { LoginToggle } from "./LoginButton"
|
import { LoginToggle } from "./LoginButton"
|
||||||
|
import { SpecialVisualizationState } from "../SpecialVisualization"
|
||||||
|
|
||||||
export default class SplitRoadWizard extends Combine {
|
export default class SplitRoadWizard extends Combine {
|
||||||
// @ts-ignore
|
|
||||||
private static splitLayerStyling = new LayerConfig(
|
private static splitLayerStyling = new LayerConfig(
|
||||||
split_point,
|
split_point,
|
||||||
"(BUILTIN) SplitRoadWizard.ts",
|
"(BUILTIN) SplitRoadWizard.ts",
|
||||||
|
@ -43,22 +37,7 @@ export default class SplitRoadWizard extends Combine {
|
||||||
* @param id: The id of the road to remove
|
* @param id: The id of the road to remove
|
||||||
* @param state: the state of the application
|
* @param state: the state of the application
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(id: string, state: SpecialVisualizationState) {
|
||||||
id: string,
|
|
||||||
state: {
|
|
||||||
filteredLayers: UIEventSource<FilteredLayer[]>
|
|
||||||
backgroundLayer: UIEventSource<BaseLayer>
|
|
||||||
featureSwitchIsTesting: UIEventSource<boolean>
|
|
||||||
featureSwitchIsDebugging: UIEventSource<boolean>
|
|
||||||
featureSwitchShowAllQuestions: UIEventSource<boolean>
|
|
||||||
osmConnection: OsmConnection
|
|
||||||
featureSwitchUserbadge: UIEventSource<boolean>
|
|
||||||
changes: Changes
|
|
||||||
layoutToUse: LayoutConfig
|
|
||||||
allElements: ElementStorage
|
|
||||||
selectedElement: UIEventSource<any>
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
const t = Translations.t.split
|
const t = Translations.t.split
|
||||||
|
|
||||||
// Contains the points on the road that are selected to split on - contains geojson points with extra properties such as 'location' with the distance along the linestring
|
// Contains the points on the road that are selected to split on - contains geojson points with extra properties such as 'location' with the distance along the linestring
|
||||||
|
|
|
@ -26,18 +26,27 @@
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let htmlElem: HTMLElement;
|
let htmlElem: HTMLElement;
|
||||||
|
const _htmlElement = new UIEventSource<HTMLElement>(undefined);
|
||||||
|
$: _htmlElement.setData(htmlElem);
|
||||||
|
|
||||||
|
function setHighlighting() {
|
||||||
|
if (highlightedRendering === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (htmlElem === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const highlighted = highlightedRendering.data;
|
||||||
|
if (config.id === highlighted) {
|
||||||
|
htmlElem.classList.add("glowing-shadow");
|
||||||
|
} else {
|
||||||
|
htmlElem.classList.remove("glowing-shadow");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (highlightedRendering) {
|
if (highlightedRendering) {
|
||||||
$: onDestroy(highlightedRendering.addCallbackAndRun(highlighted => {
|
onDestroy(highlightedRendering?.addCallbackAndRun(() => setHighlighting()))
|
||||||
console.log("Highlighted rendering is", highlighted)
|
onDestroy(_htmlElement.addCallbackAndRun(() => setHighlighting()))
|
||||||
if(htmlElem === undefined){
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (config.id === highlighted) {
|
|
||||||
htmlElem.classList.add("glowing-shadow");
|
|
||||||
} else {
|
|
||||||
htmlElem.classList.remove("glowing-shadow");
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ import Maproulette from "../Logic/Maproulette"
|
||||||
import SvelteUIElement from "./Base/SvelteUIElement"
|
import SvelteUIElement from "./Base/SvelteUIElement"
|
||||||
import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
|
import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
|
||||||
import QuestionViz from "./Popup/QuestionViz"
|
import QuestionViz from "./Popup/QuestionViz"
|
||||||
import { Feature } from "geojson"
|
import { Feature, Point } from "geojson"
|
||||||
import { GeoOperations } from "../Logic/GeoOperations"
|
import { GeoOperations } from "../Logic/GeoOperations"
|
||||||
import CreateNewNote from "./Popup/CreateNewNote.svelte"
|
import CreateNewNote from "./Popup/CreateNewNote.svelte"
|
||||||
import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte"
|
import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte"
|
||||||
|
@ -76,6 +76,9 @@ import { SaveButton } from "./Popup/SaveButton"
|
||||||
import Lazy from "./Base/Lazy"
|
import Lazy from "./Base/Lazy"
|
||||||
import { CheckBox } from "./Input/Checkboxes"
|
import { CheckBox } from "./Input/Checkboxes"
|
||||||
import Slider from "./Input/Slider"
|
import Slider from "./Input/Slider"
|
||||||
|
import DeleteWizard from "./Popup/DeleteWizard"
|
||||||
|
import { OsmId, OsmTags } from "../Models/OsmFeature"
|
||||||
|
import MoveWizard from "./Popup/MoveWizard"
|
||||||
|
|
||||||
class NearbyImageVis implements SpecialVisualization {
|
class NearbyImageVis implements SpecialVisualization {
|
||||||
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
||||||
|
@ -226,6 +229,7 @@ class StealViz implements SpecialVisualization {
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
constr(state: SpecialVisualizationState, featureTags, args) {
|
constr(state: SpecialVisualizationState, featureTags, args) {
|
||||||
const [featureIdKey, layerAndtagRenderingIds] = args
|
const [featureIdKey, layerAndtagRenderingIds] = args
|
||||||
const tagRenderings: [LayerConfig, TagRenderingConfig][] = []
|
const tagRenderings: [LayerConfig, TagRenderingConfig][] = []
|
||||||
|
@ -273,6 +277,7 @@ class StealViz implements SpecialVisualization {
|
||||||
return [layerId]
|
return [layerId]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class SpecialVisualizations {
|
export default class SpecialVisualizations {
|
||||||
public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.initList()
|
public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.initList()
|
||||||
|
|
||||||
|
@ -521,6 +526,74 @@ export default class SpecialVisualizations {
|
||||||
new HistogramViz(),
|
new HistogramViz(),
|
||||||
new StealViz(),
|
new StealViz(),
|
||||||
new MinimapViz(),
|
new MinimapViz(),
|
||||||
|
{
|
||||||
|
funcName: "split_button",
|
||||||
|
docs: "Adds a button which allows to split a way",
|
||||||
|
args: [],
|
||||||
|
constr(
|
||||||
|
state: SpecialVisualizationState,
|
||||||
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
|
argument: string[],
|
||||||
|
feature: Feature,
|
||||||
|
layer: LayerConfig
|
||||||
|
): BaseUIElement {
|
||||||
|
return new VariableUiElement(
|
||||||
|
// TODO
|
||||||
|
tagSource
|
||||||
|
.map((tags) => tags.id)
|
||||||
|
.map((id) => new FixedUiElement("TODO: enable splitting")) // new SplitRoadWizard(id, state))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
funcName: "move_button",
|
||||||
|
docs: "Adds a button which allows to move the object to another location. The config will be read from the layer config",
|
||||||
|
args: [],
|
||||||
|
constr(
|
||||||
|
state: SpecialVisualizationState,
|
||||||
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
|
argument: string[],
|
||||||
|
feature: Feature,
|
||||||
|
layer: LayerConfig
|
||||||
|
): BaseUIElement {
|
||||||
|
if (feature.geometry.type !== "Point") {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MoveWizard(
|
||||||
|
<Feature<Point>>feature,
|
||||||
|
<UIEventSource<OsmTags>>tagSource,
|
||||||
|
state,
|
||||||
|
layer.allowMove
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
funcName: "delete_button",
|
||||||
|
docs: "Adds a button which allows to delete the object at this location. The config will be read from the layer config",
|
||||||
|
args: [],
|
||||||
|
constr(
|
||||||
|
state: SpecialVisualizationState,
|
||||||
|
tagSource: UIEventSource<Record<string, string>>,
|
||||||
|
argument: string[],
|
||||||
|
feature: Feature,
|
||||||
|
layer: LayerConfig
|
||||||
|
): BaseUIElement {
|
||||||
|
return new VariableUiElement(
|
||||||
|
tagSource
|
||||||
|
.map((tags) => tags.id)
|
||||||
|
.map(
|
||||||
|
(id) =>
|
||||||
|
new DeleteWizard(
|
||||||
|
<OsmId>id,
|
||||||
|
<UIEventSource<OsmTags>>tagSource,
|
||||||
|
state,
|
||||||
|
layer.deletion // Reading the configuration from the layerconfig is a bit cheating and should be factored out
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
new ShareLinkViz(),
|
new ShareLinkViz(),
|
||||||
new UploadToOsmViz(),
|
new UploadToOsmViz(),
|
||||||
new MultiApplyViz(),
|
new MultiApplyViz(),
|
||||||
|
|
|
@ -93,7 +93,7 @@
|
||||||
|
|
||||||
<div class="absolute top-0 right-0 mt-4 mr-4">
|
<div class="absolute top-0 right-0 mt-4 mr-4">
|
||||||
<If condition={state.featureSwitches.featureSwitchSearch}>
|
<If condition={state.featureSwitches.featureSwitchSearch}>
|
||||||
<Geosearch bounds={state.mapProperties.bounds} {selectedElement} {selectedLayer} {state}></Geosearch>
|
<Geosearch bounds={state.mapProperties.bounds} {selectedElement} {selectedLayer} perLayer={state.perLayer}></Geosearch>
|
||||||
</If>
|
</If>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
"id": "last_click",
|
"id": "last_click",
|
||||||
"description": "This layer defines how to render the 'last click'-location. By default, it will show a marker with the possibility to add a new point (if there are some presets) and/or to add a new note (if the 'note' layer attribute is set). If none are possible, this layer won't show up",
|
"description": "This layer defines how to render the 'last click'-location. By default, it will show a marker with the possibility to add a new point (if there are some presets) and/or to add a new note (if the 'note' layer attribute is set). If none are possible, this layer won't show up",
|
||||||
"source": "special",
|
"source": "special",
|
||||||
|
"isShown": {
|
||||||
|
"or": [
|
||||||
|
"has_presets=yes",
|
||||||
|
"has_note_layer=yes"
|
||||||
|
]
|
||||||
|
},
|
||||||
"name": null,
|
"name": null,
|
||||||
"titleIcons": [],
|
"titleIcons": [],
|
||||||
"title": {
|
"title": {
|
||||||
|
@ -153,4 +159,4 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@
|
||||||
"mappings": [
|
"mappings": [
|
||||||
{
|
{
|
||||||
"if": "theme=advertising",
|
"if": "theme=advertising",
|
||||||
"then": "./assets/themes/advertising/poster_box.svg"
|
"then": "./assets/themes/advertising/icon.svg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"if": "theme=aed",
|
"if": "theme=aed",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue