forked from MapComplete/MapComplete
356 lines
12 KiB
TypeScript
356 lines
12 KiB
TypeScript
import { SpecialVisualization, SpecialVisualizationState, SpecialVisualizationSvelte } from "../SpecialVisualization"
|
|
import SvelteUIElement from "../Base/SvelteUIElement"
|
|
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
|
|
import { Feature, GeoJSON } 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"
|
|
import BaseUIElement from "../BaseUIElement"
|
|
import { VariableUiElement } from "../Base/VariableUIElement"
|
|
import { Translation } from "../i18n/Translation"
|
|
import { FixedUiElement } from "../Base/FixedUiElement"
|
|
import { default as FeatureTitle } from "../Popup/Title.svelte"
|
|
|
|
/**
|
|
* Thin wrapper around QuestionBox.svelte to include it into the special Visualisations
|
|
*/
|
|
class QuestionViz extends SpecialVisualizationSvelte {
|
|
funcName = "questions"
|
|
needsUrls = []
|
|
docs =
|
|
"The special element which shows the questions which are unknown. Added by default if not yet there"
|
|
args = [
|
|
{
|
|
name: "labels",
|
|
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'. If both a whitelist and a blacklist are given, will show questions having at least one label from the whitelist but none of the blacklist.",
|
|
},
|
|
{
|
|
name: "show_all",
|
|
default: "user-preference",
|
|
doc: "Either `no`, `yes` or `user-preference`. Indicates if all questions should be shown at once",
|
|
},
|
|
]
|
|
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 !== "")
|
|
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,
|
|
notForLabels: blacklist,
|
|
showAllQuestionsAtOnce,
|
|
})
|
|
}
|
|
}
|
|
|
|
class Minimap extends SpecialVisualizationSvelte {
|
|
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",
|
|
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",
|
|
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 })
|
|
}
|
|
}
|
|
|
|
class SplitButton extends SpecialVisualizationSvelte {
|
|
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 {
|
|
return new SvelteUIElement(SplitRoadWizard, {
|
|
id: tagSource.map((pr) => pr.id),
|
|
state,
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
class MoveButton extends SpecialVisualizationSvelte {
|
|
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,
|
|
layer,
|
|
})
|
|
}
|
|
}
|
|
|
|
class DeleteButton extends SpecialVisualizationSvelte {
|
|
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,
|
|
layer,
|
|
})
|
|
}
|
|
}
|
|
|
|
class QrCodeVis extends SpecialVisualizationSvelte {
|
|
funcName = "qr_code"
|
|
args = [
|
|
{
|
|
name: "text",
|
|
doc: "Extra text on the side of the QR-code",
|
|
},
|
|
{
|
|
name: "textClass",
|
|
doc: "CSS class of the the side text",
|
|
},
|
|
]
|
|
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 {
|
|
const sideText = argument[0]
|
|
const sideTextClass = argument[1] ?? ""
|
|
return new SvelteUIElement(QrCode, {
|
|
state,
|
|
tags,
|
|
feature,
|
|
sideText,
|
|
sideTextClass,
|
|
})
|
|
}
|
|
}
|
|
|
|
class IfNothingKnown extends SpecialVisualizationSvelte {
|
|
funcName = "if_nothing_known"
|
|
args = [
|
|
{
|
|
name: "text",
|
|
doc: "Text to show",
|
|
required: true,
|
|
},
|
|
{ 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,
|
|
cssClasses,
|
|
})
|
|
}
|
|
}
|
|
|
|
class AddNewPointVis extends SpecialVisualizationSvelte {
|
|
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: GeoJSON): SvelteUIElement {
|
|
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
|
return new SvelteUIElement(AddNewPoint, {
|
|
state,
|
|
coordinate: { lon, lat },
|
|
})
|
|
}
|
|
}
|
|
|
|
class Translated extends SpecialVisualization {
|
|
funcName = "translated"
|
|
docs = "If the given key can be interpreted as a JSON, only show the key containing the current language (or 'en'). This specialRendering is meant to be used by MapComplete studio and is not useful in map themes"
|
|
group = "UI"
|
|
args = [
|
|
{
|
|
name: "key",
|
|
type: "key",
|
|
doc: "The attribute to interpret as json",
|
|
defaultValue: "value",
|
|
},
|
|
]
|
|
|
|
constr(
|
|
state: SpecialVisualizationState,
|
|
tagSource: UIEventSource<Record<string, string>>,
|
|
argument: string[],
|
|
): BaseUIElement {
|
|
return new VariableUiElement(
|
|
tagSource.map((tags) => {
|
|
const v = tags[argument[0] ?? "value"]
|
|
try {
|
|
const tr = typeof v === "string" ? JSON.parse(v) : v
|
|
return new Translation(tr).SetClass("font-bold")
|
|
} catch (e) {
|
|
console.error("Cannot create a translation for", v, "due to", e)
|
|
return JSON.stringify(v)
|
|
}
|
|
}),
|
|
)
|
|
}
|
|
}
|
|
|
|
class TitleVis extends SpecialVisualizationSvelte {
|
|
group = "UI"
|
|
funcName = "title"
|
|
args = []
|
|
|
|
docs = "Shows the title of the popup. Useful for some cases, e.g. 'What is phone number of {title()}?'"
|
|
example =
|
|
"`What is the phone number of {title()}`, which might automatically become `What is the phone number of XYZ`."
|
|
|
|
constr(
|
|
state: SpecialVisualizationState,
|
|
tags: UIEventSource<Record<string, string>>,
|
|
_: string[],
|
|
feature: Feature,
|
|
layer: LayerConfig,
|
|
) {
|
|
return new SvelteUIElement(FeatureTitle, { state, tags, feature, layer })
|
|
}
|
|
}
|
|
|
|
class BracedVis extends SpecialVisualization {
|
|
group = "UI"
|
|
funcName = "braced"
|
|
docs = "Show a literal text within braces"
|
|
|
|
args = [
|
|
{
|
|
name: "text",
|
|
required: true,
|
|
doc: "The value to show",
|
|
},
|
|
]
|
|
|
|
constr(
|
|
state: SpecialVisualizationState,
|
|
tagSource: UIEventSource<Record<string, string>>,
|
|
args: string[],
|
|
feature: Feature,
|
|
layer: LayerConfig,
|
|
): BaseUIElement {
|
|
return new FixedUiElement("{" + args[0] + "}")
|
|
}
|
|
|
|
}
|
|
|
|
|
|
export class UISpecialVisualisations {
|
|
public static initList(): SpecialVisualization[] {
|
|
return [
|
|
new QuestionViz(),
|
|
new Minimap(),
|
|
new SplitButton(),
|
|
new MoveButton(),
|
|
new DeleteButton(),
|
|
new QrCodeVis(),
|
|
new IfNothingKnown(),
|
|
new ShareLinkViz(),
|
|
new AddNewPointVis(),
|
|
new Translated(),
|
|
new BracedVis(),
|
|
new TitleVis(),
|
|
]
|
|
}
|
|
}
|