2025-06-26 05:20:12 +02:00
import { SpecialVisualization , SpecialVisualizationState , SpecialVisualizationSvelte } from "../SpecialVisualization"
2025-01-27 04:50:44 +01:00
import SvelteUIElement from "../Base/SvelteUIElement"
2025-04-05 04:01:19 +02:00
import { ImmutableStore , Store , UIEventSource } from "../../Logic/UIEventSource"
2025-06-26 05:20:12 +02:00
import { Feature , GeoJSON } from "geojson"
2025-01-27 04:50:44 +01:00
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"
2025-01-28 23:37:42 +01:00
import { GeoOperations } from "../../Logic/GeoOperations"
import AddNewPoint from "../Popup/AddNewPoint/AddNewPoint.svelte"
2025-06-26 05:20:12 +02:00
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"
2025-01-27 04:50:44 +01:00
/ * *
* Thin wrapper around QuestionBox . svelte to include it into the special Visualisations
* /
2025-06-26 05:20:12 +02:00
class QuestionViz extends SpecialVisualizationSvelte {
2025-01-27 04:50:44 +01:00
funcName = "questions"
needsUrls = [ ]
docs =
2025-05-24 02:14:02 +02:00
"The special element which shows the questions which are unknown. Added by default if not yet there"
2025-01-27 04:50:44 +01:00
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" ,
2025-01-27 04:50:44 +01:00
} ,
{
name : "blacklisted-labels" ,
2025-06-06 18:15:50 +02:00
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." ,
2025-02-10 02:04:58 +01:00
} ,
2025-04-05 04:01:19 +02:00
{
name : "show_all" ,
default : "user-preference" ,
2025-04-15 18:18:44 +02:00
doc : "Either `no`, `yes` or `user-preference`. Indicates if all questions should be shown at once" ,
} ,
2025-01-27 04:50:44 +01:00
]
2025-06-26 05:20:12 +02:00
group = "default"
2025-01-27 04:50:44 +01:00
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 ]
2025-01-27 04:50:44 +01:00
? . split ( ";" )
? . map ( ( s ) = > s . trim ( ) )
? . filter ( ( s ) = > s !== "" )
2025-04-05 04:01:19 +02:00
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 )
}
2025-01-27 04:50:44 +01:00
return new SvelteUIElement ( Questionbox , {
layer ,
tags ,
selectedElement : feature ,
state ,
onlyForLabels : labels ,
2025-02-10 02:04:58 +01:00
notForLabels : blacklist ,
2025-04-15 18:18:44 +02:00
showAllQuestionsAtOnce ,
2025-01-27 04:50:44 +01:00
} )
}
}
2025-06-26 05:20:12 +02:00
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 ] + "}" )
}
}
2025-01-27 04:50:44 +01:00
export class UISpecialVisualisations {
2025-06-26 05:20:12 +02:00
public static initList ( ) : SpecialVisualization [ ] {
2025-02-10 02:04:58 +01:00
return [
new QuestionViz ( ) ,
2025-06-26 05:20:12 +02:00
new Minimap ( ) ,
new SplitButton ( ) ,
new MoveButton ( ) ,
new DeleteButton ( ) ,
new QrCodeVis ( ) ,
new IfNothingKnown ( ) ,
2025-01-28 23:37:42 +01:00
new ShareLinkViz ( ) ,
2025-06-26 05:20:12 +02:00
new AddNewPointVis ( ) ,
new Translated ( ) ,
new BracedVis ( ) ,
new TitleVis ( ) ,
2025-01-27 04:50:44 +01:00
]
}
}