MapComplete/src/UI/SpecialVisualisations/UISpecialVisualisations.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

236 lines
9.6 KiB
TypeScript
Raw Normal View History

import { SpecialVisualizationState, SpecialVisualizationSvelte } from "../SpecialVisualization"
import SvelteUIElement from "../Base/SvelteUIElement"
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
import { Feature } from "geojson"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import Questionbox from "../Popup/TagRendering/Questionbox.svelte"
import MinimapViz from "../Popup/MinimapViz.svelte"
import SplitRoadWizard from "../Popup/SplitRoadWizard.svelte"
import MoveWizard from "../Popup/MoveWizard.svelte"
import DeleteWizard from "../Popup/DeleteFlow/DeleteWizard.svelte"
import QrCode from "../Popup/QrCode.svelte"
import NothingKnown from "../Popup/NothingKnown.svelte"
import { ShareLinkViz } from "../Popup/ShareLinkViz"
import { GeoOperations } from "../../Logic/GeoOperations"
import AddNewPoint from "../Popup/AddNewPoint/AddNewPoint.svelte"
/**
* Thin wrapper around QuestionBox.svelte to include it into the special Visualisations
*/
class QuestionViz implements SpecialVisualizationSvelte {
funcName = "questions"
needsUrls = []
docs =
"The special element which shows the questions which are unkown. Added by default if not yet there"
args = [
{
name: "labels",
2025-02-10 02:04:58 +01:00
doc: "One or more ';'-separated labels. If these are given, only questions with these labels will be given. Use `unlabeled` for all questions that don't have an explicit label. If none given, all questions will be shown",
},
{
name: "blacklisted-labels",
doc: "One or more ';'-separated labels of questions which should _not_ be included. Note that the questionbox which is added by default will blacklist 'hidden'"
2025-02-10 02:04:58 +01:00
},
{
name: "show_all",
default: "user-preference",
doc: "Either `no`, `yes` or `user-preference`. Indicates if all questions should be shown at once"
}
]
svelteBased = true
group: "default"
constr(
state: SpecialVisualizationState,
tags: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature,
layer: LayerConfig
): SvelteUIElement {
const labels = args[0]
?.split(";")
?.map((s) => s.trim())
?.filter((s) => s !== "")
2025-03-06 16:21:55 +01:00
const blacklist = args[1]
?.split(";")
?.map((s) => s.trim())
?.filter((s) => s !== "")
const showAll = args[2]
let showAllQuestionsAtOnce: Store<boolean> = state.userRelatedState?.showAllQuestionsAtOnce
if (showAll === "yes") {
showAllQuestionsAtOnce = new ImmutableStore(true)
} else if (showAll === "no") {
showAllQuestionsAtOnce = new ImmutableStore(false)
}
return new SvelteUIElement(Questionbox, {
layer,
tags,
selectedElement: feature,
state,
onlyForLabels: labels,
2025-02-10 02:04:58 +01:00
notForLabels: blacklist,
showAllQuestionsAtOnce
})
}
}
export class UISpecialVisualisations {
2025-02-10 02:04:58 +01:00
public static initList(): SpecialVisualizationSvelte[] {
return [
new QuestionViz(),
{
funcName: "minimap",
docs: "A small map showing the selected feature.",
needsUrls: [],
group: "default",
args: [
{
doc: "The (maximum) zoomlevel: the target zoomlevel after fitting the entire feature. The minimap will fit the entire feature, then zoom out to this zoom level. The higher, the more zoomed in with 1 being the entire world and 19 being really close",
name: "zoomlevel",
2025-02-10 02:04:58 +01:00
defaultValue: "18",
},
{
doc: "(Matches all resting arguments) This argument should be the key of a property of the feature. The corresponding value is interpreted as either the id or the a list of ID's. The features with these ID's will be shown on this minimap. (Note: if the key is 'id', list interpration is disabled)",
name: "idKey",
2025-02-10 02:04:58 +01:00
defaultValue: "id",
},
],
example:
"`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`",
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
args: string[],
feature: Feature
): SvelteUIElement {
return new SvelteUIElement(MinimapViz, { state, args, feature, tagSource })
2025-02-10 02:04:58 +01:00
},
},
{
funcName: "split_button",
docs: "Adds a button which allows to split a way",
args: [],
group: "default",
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>
): SvelteUIElement {
2025-02-10 02:04:58 +01:00
return new SvelteUIElement(SplitRoadWizard, {
id: tagSource.map((pr) => pr.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: [],
group: "default",
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
): SvelteUIElement {
if (feature.geometry.type !== "Point") {
return undefined
}
return new SvelteUIElement(MoveWizard, {
state,
featureToMove: feature,
2025-02-10 02:04:58 +01:00
layer,
})
2025-02-10 02:04:58 +01:00
},
},
{
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: [],
group: "default",
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
): SvelteUIElement {
if (!layer.deletion) {
return undefined
}
return new SvelteUIElement(DeleteWizard, {
tags: tagSource,
deleteConfig: layer.deletion,
state,
feature,
2025-02-10 02:04:58 +01:00
layer,
})
2025-02-10 02:04:58 +01:00
},
},
{
funcName: "qr_code",
args: [],
group: "default",
docs: "Generates a QR-code to share the selected object",
constr(
state: SpecialVisualizationState,
tags: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature
): SvelteUIElement {
return new SvelteUIElement(QrCode, { state, tags, feature })
2025-02-10 02:04:58 +01:00
},
},
{
funcName: "if_nothing_known",
args: [
{
name: "text",
doc: "Text to show",
2025-02-10 02:04:58 +01:00
required: true,
},
2025-02-10 02:04:58 +01:00
{ name: "cssClasses", doc: "Classes to apply onto the text" },
],
group: "default",
docs: "Shows a 'nothing is currently known-message if there is at least one unanswered question and no known (answerable) question",
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
argument: string[],
feature: Feature,
layer: LayerConfig
): SvelteUIElement {
const text = argument[0]
const cssClasses = argument[1]
return new SvelteUIElement(NothingKnown, {
state,
tags: tagSource,
layer,
text,
2025-02-10 02:04:58 +01:00
cssClasses,
})
2025-02-10 02:04:58 +01:00
},
},
new ShareLinkViz(),
{
funcName: "add_new_point",
docs: "An element which allows to add a new point on the 'last_click'-location. Only makes sense in the layer `last_click`",
args: [],
group: "default",
constr(state: SpecialVisualizationState, _, __, feature): SvelteUIElement {
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
return new SvelteUIElement(AddNewPoint, {
state,
2025-02-10 02:04:58 +01:00
coordinate: { lon, lat },
})
2025-02-10 02:04:58 +01:00
},
},
]
}
}