2023-03-28 05:13:48 +02:00
import { SpecialVisualization , SpecialVisualizationState } from "../SpecialVisualization"
2022-11-02 14:44:06 +01:00
import BaseUIElement from "../BaseUIElement"
import { UIEventSource } from "../../Logic/UIEventSource"
import { VariableUiElement } from "../Base/VariableUIElement"
2023-02-08 01:14:21 +01:00
import all_languages from "../../assets/language_translations.json"
2022-11-02 14:44:06 +01:00
import { Translation } from "../i18n/Translation"
import Combine from "../Base/Combine"
import Title from "../Base/Title"
import Lazy from "../Base/Lazy"
import { SubstitutedTranslation } from "../SubstitutedTranslation"
import List from "../Base/List"
import { AllLanguagesSelector } from "./AllLanguagesSelector"
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
import { And } from "../../Logic/Tags/And"
import { Tag } from "../../Logic/Tags/Tag"
import { EditButton , SaveButton } from "./SaveButton"
import Translations from "../i18n/Translations"
import Toggle from "../Input/Toggle"
2023-03-28 05:13:48 +02:00
import { Feature } from "geojson"
2022-10-29 03:03:51 +02:00
export class LanguageElement implements SpecialVisualization {
funcName : string = "language_chooser"
2022-11-02 14:44:06 +01:00
docs : string | BaseUIElement =
"The language element allows to show and pick all known (modern) languages. The key can be set"
2022-10-29 03:03:51 +02:00
2022-11-02 14:44:06 +01:00
args : { name : string ; defaultValue? : string ; doc : string ; required? : boolean } [ ] = [
{
2022-10-29 03:03:51 +02:00
name : "key" ,
required : true ,
2022-11-02 14:44:06 +01:00
doc : "What key to use, e.g. `language`, `tactile_writing:braille:language`, ... If a language is supported, the language code will be appended to this key, resulting in `language:nl=yes` if nl is picked " ,
2022-10-29 03:03:51 +02:00
} ,
2022-11-02 14:44:06 +01:00
{
name : "question" ,
required : true ,
doc : "What to ask if no questions are known" ,
} ,
{
name : "render_list_item" ,
doc : "How a single language will be shown in the list of languages. Use `{language}` to indicate the language (which it must contain)." ,
defaultValue : "{language()}" ,
} ,
{
name : "render_single_language" ,
doc : "What will be shown if the feature only supports a single language" ,
required : true ,
} ,
{
name : "render_all" ,
doc : "The full rendering. Use `{list}` to show where the list of languages must come. Optional if mode=single" ,
defaultValue : "{list()}" ,
} ,
{
name : "no_known_languages" ,
doc : "The text that is shown if no languages are known for this key. If this text is omitted, the languages will be prompted instead" ,
} ,
{
name : "mode" ,
doc : "If one or many languages can be selected. Should be 'multi' or 'single'" ,
defaultValue : "multi" ,
} ,
]
2022-10-29 03:03:51 +02:00
example : `
\ ` \` \` json
{ "special" :
"type" : "language_chooser" ,
"key" : "school:language" ,
"question" : { "en" : "What are the main (and administrative) languages spoken in this school?" } ,
"render_single_language" : { "en" : "{language()} is spoken on this school" } ,
"render_list_item" : { "en" : "{language()}" } ,
"render_all" : { "en" : "The following languages are spoken here:{list()}" }
"mode" : "multi"
}
\ ` \` \`
`
2022-11-02 14:44:06 +01:00
constr (
2023-03-28 05:13:48 +02:00
state : SpecialVisualizationState ,
tagSource : UIEventSource < Record < string , string > > ,
argument : string [ ] ,
feature : Feature
2022-11-02 14:44:06 +01:00
) : BaseUIElement {
let [ key , question , item_render , single_render , all_render , on_no_known_languages , mode ] =
argument
2022-10-29 03:03:51 +02:00
if ( mode === undefined || mode . length == 0 ) {
mode = "multi"
}
2022-12-31 02:53:25 +01:00
if ( item_render === undefined || item_render . trim ( ) === "" ) {
2022-10-29 03:03:51 +02:00
item_render = "{language()}"
}
if ( all_render === undefined || all_render . length == 0 ) {
all_render = "{list()}"
}
if ( mode !== "single" && mode !== "multi" ) {
2022-11-02 14:44:06 +01:00
throw (
"Error while calling language_chooser: mode must be either 'single' or 'multi' but it is " +
mode
)
2022-10-29 03:03:51 +02:00
}
2022-12-31 02:53:25 +01:00
if ( single_render . indexOf ( "{language()" ) < 0 ) {
throw (
"Error while calling language_chooser: render_single_language must contain '{language()}' but it is " +
single_render
)
}
if ( item_render . indexOf ( "{language()" ) < 0 ) {
throw (
"Error while calling language_chooser: render_list_item must contain '{language()}' but it is " +
item_render
)
2022-10-29 03:03:51 +02:00
}
if ( all_render . indexOf ( "{list()" ) < 0 ) {
throw "Error while calling language_chooser: render_all must contain '{list()}'"
}
const prefix = key + ":"
2022-11-02 14:44:06 +01:00
const foundLanguages = tagSource . map ( ( tags ) = > {
const foundLanguages : string [ ] = [ ]
for ( const k in tags ) {
const v = tags [ k ]
if ( v !== "yes" ) {
continue
2022-10-29 03:03:51 +02:00
}
2022-11-02 14:44:06 +01:00
if ( k . startsWith ( prefix ) ) {
foundLanguages . push ( k . substring ( prefix . length ) )
2022-10-29 03:03:51 +02:00
}
2022-11-02 14:44:06 +01:00
}
return foundLanguages
} )
const forceInputMode = new UIEventSource ( false )
const inputEl = new Lazy ( ( ) = > {
const selector = new AllLanguagesSelector ( {
mode : mode === "single" ? "select-one" : "select-many" ,
currentCountry : tagSource.map ( ( tgs ) = > tgs [ "_country" ] ) ,
} )
const cancelButton = Toggle . If ( forceInputMode , ( ) = >
Translations . t . general . cancel
2022-10-29 03:03:51 +02:00
. Clone ( )
2022-11-02 14:44:06 +01:00
. SetClass ( "btn btn-secondary" )
. onClick ( ( ) = > forceInputMode . setData ( false ) )
)
2022-10-29 03:03:51 +02:00
const saveButton = new SaveButton (
2022-11-02 14:44:06 +01:00
selector . GetValue ( ) . map ( ( lngs ) = > ( lngs . length > 0 ? "true" : undefined ) ) ,
2023-05-09 00:06:51 +02:00
state
2022-10-29 03:03:51 +02:00
) . onClick ( ( ) = > {
const selectedLanguages = selector . GetValue ( ) . data
const currentLanguages = foundLanguages . data
2022-11-02 14:44:06 +01:00
const selection : Tag [ ] = selectedLanguages . map ( ( ln ) = > new Tag ( prefix + ln , "yes" ) )
2022-10-29 03:03:51 +02:00
for ( const currentLanguage of currentLanguages ) {
if ( selectedLanguages . indexOf ( currentLanguage ) >= 0 ) {
continue
}
// Erase language that is not spoken anymore
selection . push ( new Tag ( prefix + currentLanguage , "" ) )
}
if ( state . featureSwitchIsTesting . data ) {
for ( const tag of selection ) {
tagSource . data [ tag . key ] = tag . value
}
tagSource . ping ( )
} else {
2022-11-02 14:44:06 +01:00
; ( state ? . changes )
2022-10-29 03:03:51 +02:00
. applyAction (
2022-11-02 14:44:06 +01:00
new ChangeTagAction (
tagSource . data . id ,
new And ( selection ) ,
tagSource . data ,
{
2023-03-28 05:13:48 +02:00
theme : state?.layout?.id ? ? "unkown" ,
2022-11-02 14:44:06 +01:00
changeType : "answer" ,
}
)
2022-10-29 03:03:51 +02:00
)
. then ( ( _ ) = > {
console . log ( "Tagchanges applied" )
} )
}
forceInputMode . setData ( false )
} )
2022-11-02 14:44:06 +01:00
return new Combine ( [
new Title ( question ) ,
selector ,
new Combine ( [ cancelButton , saveButton ] ) . SetClass ( "flex justify-end" ) ,
] ) . SetClass ( "flex flex-col question disable-links" )
2022-10-29 03:03:51 +02:00
} )
const editButton = new EditButton ( state . osmConnection , ( ) = > forceInputMode . setData ( true ) )
2022-11-02 14:44:06 +01:00
return new VariableUiElement (
foundLanguages . map (
( foundLanguages ) = > {
if ( forceInputMode . data ) {
return inputEl
2022-10-29 03:03:51 +02:00
}
2022-11-02 14:44:06 +01:00
if ( foundLanguages . length === 0 ) {
// No languages found - we show the question and the input element
if (
on_no_known_languages !== undefined &&
on_no_known_languages . length > 0
) {
return new Combine ( [ on_no_known_languages , editButton ] ) . SetClass (
"flex justify-end"
2022-10-29 03:03:51 +02:00
)
2022-11-02 14:44:06 +01:00
}
return inputEl
}
2022-10-29 03:03:51 +02:00
2022-11-02 14:44:06 +01:00
let rendered : BaseUIElement
if ( foundLanguages . length === 1 ) {
const ln = foundLanguages [ 0 ]
let mapping = new Map < string , BaseUIElement > ( )
mapping . set ( "language" , new Translation ( all_languages [ ln ] ) )
rendered = new SubstitutedTranslation (
new Translation ( { "*" : single_render } , undefined ) ,
tagSource ,
state ,
mapping
)
} else {
let mapping = new Map < string , BaseUIElement > ( )
const languagesList = new List (
foundLanguages . map ( ( ln ) = > {
let mappingLn = new Map < string , BaseUIElement > ( )
mappingLn . set ( "language" , new Translation ( all_languages [ ln ] ) )
return new SubstitutedTranslation (
new Translation ( { "*" : item_render } , undefined ) ,
tagSource ,
state ,
mappingLn
)
} )
)
mapping . set ( "list" , languagesList )
rendered = new SubstitutedTranslation (
new Translation ( { "*" : all_render } , undefined ) ,
tagSource ,
state ,
mapping
)
}
return new Combine ( [ rendered , editButton ] ) . SetClass ( "flex justify-between" )
} ,
[ forceInputMode ]
)
)
2022-10-29 03:03:51 +02:00
}
}