More dashboard view, add documentation in dashboard view
This commit is contained in:
parent
89278f6d74
commit
a89f5a0e3e
10 changed files with 189 additions and 97 deletions
|
@ -25,6 +25,11 @@ export interface TagRenderingConfigJson {
|
|||
*/
|
||||
labels?: string[]
|
||||
|
||||
/**
|
||||
* A human-readable text explaining what this tagRendering does
|
||||
*/
|
||||
description?: string | any
|
||||
|
||||
/**
|
||||
* Renders this value. Note that "{key}"-parts are substituted by the corresponding values of the element.
|
||||
* If neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value.
|
||||
|
|
|
@ -28,6 +28,9 @@ import {And} from "../../Logic/Tags/And";
|
|||
import {Overpass} from "../../Logic/Osm/Overpass";
|
||||
import Constants from "../Constants";
|
||||
import {FixedUiElement} from "../../UI/Base/FixedUiElement";
|
||||
import Svg from "../../Svg";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {OsmTags} from "../OsmFeature";
|
||||
|
||||
export default class LayerConfig extends WithContextLoader {
|
||||
|
||||
|
@ -191,8 +194,8 @@ export default class LayerConfig extends WithContextLoader {
|
|||
this.doNotDownload = json.doNotDownload ?? false;
|
||||
this.passAllFeatures = json.passAllFeatures ?? false;
|
||||
this.minzoom = json.minzoom ?? 0;
|
||||
if(json["minZoom"] !== undefined){
|
||||
throw "At "+context+": minzoom is written all lowercase"
|
||||
if (json["minZoom"] !== undefined) {
|
||||
throw "At " + context + ": minzoom is written all lowercase"
|
||||
}
|
||||
this.minzoomVisible = json.minzoomVisible ?? this.minzoom;
|
||||
this.shownByDefault = json.shownByDefault ?? true;
|
||||
|
@ -352,7 +355,7 @@ export default class LayerConfig extends WithContextLoader {
|
|||
neededLayer: string;
|
||||
}[] = []
|
||||
, addedByDefault = false, canBeIncluded = true): BaseUIElement {
|
||||
const extraProps = []
|
||||
const extraProps : (string | BaseUIElement)[] = []
|
||||
|
||||
extraProps.push("This layer is shown at zoomlevel **" + this.minzoom + "** and higher")
|
||||
|
||||
|
@ -377,7 +380,11 @@ export default class LayerConfig extends WithContextLoader {
|
|||
}
|
||||
|
||||
if (this.source.geojsonSource !== undefined) {
|
||||
extraProps.push("<img src='../warning.svg' height='1rem'/> This layer is loaded from an external source, namely `" + this.source.geojsonSource + "`")
|
||||
extraProps.push(
|
||||
new Combine([
|
||||
Utils.runningFromConsole ? "<img src='../warning.svg' height='1rem'/>" : undefined,
|
||||
"This layer is loaded from an external source, namely ",
|
||||
new FixedUiElement( this.source.geojsonSource).SetClass("code")]));
|
||||
}
|
||||
} else {
|
||||
extraProps.push("This layer can **not** be included in a theme. It is solely used by [special renderings](SpecialRenderings.md) showing a minimap with custom data.")
|
||||
|
@ -409,16 +416,16 @@ export default class LayerConfig extends WithContextLoader {
|
|||
if (values == undefined) {
|
||||
return undefined
|
||||
}
|
||||
const embedded: (Link | string)[] = values.values?.map(v => Link.OsmWiki(values.key, v, true)) ?? ["_no preset options defined, or no values in them_"]
|
||||
const embedded: (Link | string)[] = values.values?.map(v => Link.OsmWiki(values.key, v, true).SetClass("mr-2")) ?? ["_no preset options defined, or no values in them_"]
|
||||
return [
|
||||
new Combine([
|
||||
new Link(
|
||||
"<img src='https://mapcomplete.osm.be/assets/svg/statistics.svg' height='18px'>",
|
||||
"https://taginfo.openstreetmap.org/keys/" + values.key + "#values"
|
||||
Utils.runningFromConsole ? "<img src='https://mapcomplete.osm.be/assets/svg/statistics.svg' height='18px'>" : Svg.statistics_svg().SetClass("w-4 h-4 mr-2"),
|
||||
"https://taginfo.openstreetmap.org/keys/" + values.key + "#values", true
|
||||
), Link.OsmWiki(values.key)
|
||||
]),
|
||||
]).SetClass("flex"),
|
||||
values.type === undefined ? "Multiple choice" : new Link(values.type, "../SpecialInputElements.md#" + values.type),
|
||||
new Combine(embedded)
|
||||
new Combine(embedded).SetClass("flex")
|
||||
];
|
||||
}))
|
||||
|
||||
|
@ -427,18 +434,27 @@ export default class LayerConfig extends WithContextLoader {
|
|||
quickOverview = new Combine([
|
||||
new FixedUiElement("Warning: ").SetClass("bold"),
|
||||
"this quick overview is incomplete",
|
||||
new Table(["attribute", "type", "values which are supported by this layer"], tableRows)
|
||||
new Table(["attribute", "type", "values which are supported by this layer"], tableRows).SetClass("zebra-table")
|
||||
]).SetClass("flex-col flex")
|
||||
}
|
||||
|
||||
const icon = this.mapRendering
|
||||
.filter(mr => mr.location.has("point"))
|
||||
.map(mr => mr.icon?.render?.txt)
|
||||
.find(i => i !== undefined)
|
||||
let iconImg = ""
|
||||
if (icon !== undefined) {
|
||||
// This is for the documentation, so we have to use raw HTML
|
||||
iconImg = `<img src='https://mapcomplete.osm.be/${icon}' height="100px"> `
|
||||
|
||||
let iconImg: BaseUIElement = new FixedUiElement("")
|
||||
|
||||
if (Utils.runningFromConsole) {
|
||||
const icon = this.mapRendering
|
||||
.filter(mr => mr.location.has("point"))
|
||||
.map(mr => mr.icon?.render?.txt)
|
||||
.find(i => i !== undefined)
|
||||
// This is for the documentation in a markdown-file, so we have to use raw HTML
|
||||
if (icon !== undefined) {
|
||||
iconImg = new FixedUiElement(`<img src='https://mapcomplete.osm.be/${icon}' height="100px"> `)
|
||||
}
|
||||
} else {
|
||||
iconImg = this.mapRendering
|
||||
.filter(mr => mr.location.has("point"))
|
||||
.map(mr => mr.GenerateLeafletStyle(new UIEventSource<OsmTags>({id:"node/-1"}), false, {includeBadges: false}).html)
|
||||
.find(i => i !== undefined)
|
||||
}
|
||||
|
||||
let overpassLink: BaseUIElement = undefined;
|
||||
|
@ -467,7 +483,7 @@ export default class LayerConfig extends WithContextLoader {
|
|||
new Title("Supported attributes", 2),
|
||||
quickOverview,
|
||||
...this.tagRenderings.map(tr => tr.GenerateDocumentation())
|
||||
]).SetClass("flex-col")
|
||||
]).SetClass("flex-col").SetClass("link-underline")
|
||||
}
|
||||
|
||||
public CustomCodeSnippets(): string[] {
|
||||
|
|
|
@ -14,6 +14,8 @@ import List from "../../UI/Base/List";
|
|||
import {MappingConfigJson, QuestionableTagRenderingConfigJson} from "./Json/QuestionableTagRenderingConfigJson";
|
||||
import {FixedUiElement} from "../../UI/Base/FixedUiElement";
|
||||
import {Paragraph} from "../../UI/Base/Paragraph";
|
||||
import spec = Mocha.reporters.spec;
|
||||
import SpecialVisualizations from "../../UI/SpecialVisualizations";
|
||||
|
||||
export interface Mapping {
|
||||
readonly if: TagsFilter,
|
||||
|
@ -37,6 +39,7 @@ export default class TagRenderingConfig {
|
|||
public readonly render?: TypedTranslation<object>;
|
||||
public readonly question?: TypedTranslation<object>;
|
||||
public readonly condition?: TagsFilter;
|
||||
public readonly description?: Translation;
|
||||
|
||||
public readonly configuration_warnings: string[] = []
|
||||
|
||||
|
@ -55,6 +58,7 @@ export default class TagRenderingConfig {
|
|||
public readonly mappings?: Mapping[]
|
||||
public readonly labels: string[]
|
||||
|
||||
|
||||
constructor(json: string | QuestionableTagRenderingConfigJson, context?: string) {
|
||||
if (json === undefined) {
|
||||
throw "Initing a TagRenderingConfig with undefined in " + context;
|
||||
|
@ -106,6 +110,7 @@ export default class TagRenderingConfig {
|
|||
this.labels = json.labels ?? []
|
||||
this.render = Translations.T(json.render, translationKey + ".render");
|
||||
this.question = Translations.T(json.question, translationKey + ".question");
|
||||
this.description = Translations.T(json.description, translationKey + ".description");
|
||||
this.condition = TagUtils.Tag(json.condition ?? {"and": []}, `${context}.condition`);
|
||||
if (json.freeform) {
|
||||
|
||||
|
@ -563,8 +568,8 @@ export default class TagRenderingConfig {
|
|||
new Combine(
|
||||
[
|
||||
new FixedUiElement(m.then.txt).SetClass("bold"),
|
||||
"corresponds with ",
|
||||
m.if.asHumanString(true, false, {})
|
||||
" corresponds with ",
|
||||
new FixedUiElement( m.if.asHumanString(true, false, {})).SetClass("code")
|
||||
]
|
||||
)
|
||||
]
|
||||
|
@ -599,12 +604,14 @@ export default class TagRenderingConfig {
|
|||
labels = new Combine([
|
||||
"This tagrendering has labels ",
|
||||
...this.labels.map(label => new FixedUiElement(label).SetClass("code"))
|
||||
])
|
||||
]).SetClass("flex")
|
||||
}
|
||||
|
||||
return new Combine([
|
||||
new Title(this.id, 3),
|
||||
this.description,
|
||||
this.question !== undefined ?
|
||||
new Combine(["The question is ", new FixedUiElement(this.question.txt).SetClass("bold")]) :
|
||||
new Combine(["The question is ", new FixedUiElement(this.question.txt).SetClass("font-bold bold")]) :
|
||||
new FixedUiElement(
|
||||
"This tagrendering has no question and is thus read-only"
|
||||
).SetClass("italic"),
|
||||
|
@ -613,6 +620,6 @@ export default class TagRenderingConfig {
|
|||
condition,
|
||||
group,
|
||||
labels
|
||||
]).SetClass("flex-col");
|
||||
]).SetClass("flex flex-col");
|
||||
}
|
||||
}
|
|
@ -26,9 +26,9 @@ export default class Link extends BaseUIElement {
|
|||
if (!hideKey) {
|
||||
k = key + "="
|
||||
}
|
||||
return new Link(k + value, `https://wiki.openstreetmap.org/wiki/Tag:${key}%3D${value}`)
|
||||
return new Link(k + value, `https://wiki.openstreetmap.org/wiki/Tag:${key}%3D${value}`, true)
|
||||
}
|
||||
return new Link(key, "https://wiki.openstreetmap.org/wiki/Key:" + key)
|
||||
return new Link(key, "https://wiki.openstreetmap.org/wiki/Key:" + key, true)
|
||||
}
|
||||
|
||||
AsMarkdown(): string {
|
||||
|
|
|
@ -23,30 +23,35 @@ import TagRenderingAnswer from "./Popup/TagRenderingAnswer";
|
|||
import Hash from "../Logic/Web/Hash";
|
||||
import FilterView from "./BigComponents/FilterView";
|
||||
import {FilterState} from "../Models/FilteredLayer";
|
||||
import Translations from "./i18n/Translations";
|
||||
import Constants from "../Models/Constants";
|
||||
import {Layer} from "leaflet";
|
||||
import doc = Mocha.reporters.doc;
|
||||
|
||||
|
||||
export default class DashboardGui {
|
||||
private readonly state: FeaturePipelineState;
|
||||
private readonly currentView: UIEventSource<string | BaseUIElement> = new UIEventSource<string | BaseUIElement>("No selection")
|
||||
private readonly currentView: UIEventSource<{ title: string | BaseUIElement, contents: string | BaseUIElement }> = new UIEventSource(undefined)
|
||||
|
||||
|
||||
constructor(state: FeaturePipelineState, guiState: DefaultGuiState) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
private viewSelector(shown: BaseUIElement, fullview: BaseUIElement, hash?: string): BaseUIElement {
|
||||
private viewSelector(shown: BaseUIElement, title: string | BaseUIElement, contents: string | BaseUIElement, hash?: string): BaseUIElement {
|
||||
const currentView = this.currentView
|
||||
const v = {title, contents}
|
||||
shown.SetClass("pl-1 pr-1 rounded-md")
|
||||
shown.onClick(() => {
|
||||
currentView.setData(fullview)
|
||||
currentView.setData(v)
|
||||
})
|
||||
Hash.hash.addCallbackAndRunD(h => {
|
||||
if (h === hash) {
|
||||
currentView.setData(fullview)
|
||||
currentView.setData(v)
|
||||
}
|
||||
})
|
||||
currentView.addCallbackAndRunD(cv => {
|
||||
if (cv == fullview) {
|
||||
if (cv == v) {
|
||||
shown.SetClass("bg-unsubtle")
|
||||
Hash.hash.setData(hash)
|
||||
} else {
|
||||
|
@ -64,16 +69,15 @@ export default class DashboardGui {
|
|||
}
|
||||
const tags = this.state.allElements.getEventSourceById(element.properties.id)
|
||||
const title = new Combine([new Title(new TagRenderingAnswer(tags, layer.title, this.state), 4),
|
||||
distance < 900 ? Math.floor(distance)+"m away":
|
||||
Utils.Round(distance / 1000) + "km away"
|
||||
distance < 900 ? Math.floor(distance) + "m away" :
|
||||
Utils.Round(distance / 1000) + "km away"
|
||||
]).SetClass("flex justify-between");
|
||||
|
||||
const info = new Lazy(() => new Combine([
|
||||
FeatureInfoBox.GenerateTitleBar(tags, layer, this.state),
|
||||
FeatureInfoBox.GenerateContent(tags, layer, this.state)]).SetStyle("overflox-x: hidden"));
|
||||
|
||||
|
||||
return this.viewSelector(title, info);
|
||||
return this.singleElementCache[element.properties.id] = this.viewSelector(title,
|
||||
new Lazy(() => FeatureInfoBox.GenerateTitleBar(tags, layer, this.state)),
|
||||
new Lazy(() => FeatureInfoBox.GenerateContent(tags, layer, this.state)),
|
||||
// element.properties.id
|
||||
);
|
||||
}
|
||||
|
||||
private mainElementsView(elements: { element: OsmFeature, layer: LayerConfig, distance: number }[]): BaseUIElement {
|
||||
|
@ -87,8 +91,8 @@ export default class DashboardGui {
|
|||
return new Combine(elements.map(e => self.singleElementView(e.element, e.layer, e.distance)))
|
||||
}
|
||||
|
||||
private visibleElements(map: MinimapObj & BaseUIElement, layers: Record<string, LayerConfig>): { distance: number, center: [number, number], element: OsmFeature, layer: LayerConfig }[]{
|
||||
const bbox= map.bounds.data
|
||||
private visibleElements(map: MinimapObj & BaseUIElement, layers: Record<string, LayerConfig>): { distance: number, center: [number, number], element: OsmFeature, layer: LayerConfig }[] {
|
||||
const bbox = map.bounds.data
|
||||
if (bbox === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
@ -101,10 +105,10 @@ export default class DashboardGui {
|
|||
let seenElements = new Set<string>()
|
||||
for (const elementsWithMetaElement of elementsWithMeta) {
|
||||
const layer = layers[elementsWithMetaElement.layer]
|
||||
const filtered = this.state.filteredLayers.data.find(fl => fl.layerDef == layer);
|
||||
for (const element of elementsWithMetaElement.features) {
|
||||
console.log("Inspecting ", element.properties.id)
|
||||
if(!filtered.isDisplayed.data){
|
||||
const filtered = this.state.filteredLayers.data.find(fl => fl.layerDef == layer);
|
||||
for (let i = 0; i < elementsWithMetaElement.features.length; i++) {
|
||||
const element = elementsWithMetaElement.features[i];
|
||||
if (!filtered.isDisplayed.data) {
|
||||
continue
|
||||
}
|
||||
if (seenElements.has(element.properties.id)) {
|
||||
|
@ -117,8 +121,8 @@ export default class DashboardGui {
|
|||
if (layer?.isShown?.GetRenderValue(element)?.Subs(element.properties)?.txt === "no") {
|
||||
continue
|
||||
}
|
||||
const activeFilters : FilterState[] = Array.from(filtered.appliedFilters.data.values());
|
||||
if(activeFilters.some(filter => !filter?.currentFilter?.matchesProperties(element.properties))){
|
||||
const activeFilters: FilterState[] = Array.from(filtered.appliedFilters.data.values());
|
||||
if (activeFilters.some(filter => !filter?.currentFilter?.matchesProperties(element.properties))) {
|
||||
continue
|
||||
}
|
||||
const center = GeoOperations.centerpointCoordinates(element);
|
||||
|
@ -138,7 +142,24 @@ export default class DashboardGui {
|
|||
|
||||
return elements;
|
||||
}
|
||||
|
||||
|
||||
private documentationButtonFor(layerConfig: LayerConfig): BaseUIElement {
|
||||
return this.viewSelector(Translations.W(layerConfig.name?.Clone() ?? layerConfig.id), new Combine(["Documentation about ", layerConfig.name?.Clone() ?? layerConfig.id]),
|
||||
layerConfig.GenerateDocumentation([]),
|
||||
"documentation-" + layerConfig.id)
|
||||
}
|
||||
|
||||
private allDocumentationButtons(): BaseUIElement {
|
||||
const layers = this.state.layoutToUse.layers.filter(l => Constants.priviliged_layers.indexOf(l.id) < 0)
|
||||
.filter(l => !l.id.startsWith("note_import_"));
|
||||
|
||||
if(layers.length === 1){
|
||||
return this.documentationButtonFor(layers[0])
|
||||
}
|
||||
return this.viewSelector(new FixedUiElement("Documentation"), "Documentation",
|
||||
new Combine(layers.map(l => this.documentationButtonFor(l).SetClass("flex flex-col"))))
|
||||
}
|
||||
|
||||
public setup(): void {
|
||||
|
||||
const state = this.state;
|
||||
|
@ -161,10 +182,11 @@ export default class DashboardGui {
|
|||
|
||||
const self = this;
|
||||
const elementsInview = new UIEventSource([]);
|
||||
function update(){
|
||||
elementsInview.setData( self.visibleElements(map, layers))
|
||||
|
||||
function update() {
|
||||
elementsInview.setData(self.visibleElements(map, layers))
|
||||
}
|
||||
|
||||
|
||||
map.bounds.addCallbackAndRun(update)
|
||||
state.featurePipeline.newDataLoadedSignal.addCallback(update);
|
||||
state.filteredLayers.addCallbackAndRun(fls => {
|
||||
|
@ -175,28 +197,36 @@ export default class DashboardGui {
|
|||
})
|
||||
|
||||
const welcome = new Combine([state.layoutToUse.description, state.layoutToUse.descriptionTail])
|
||||
self.currentView.setData(welcome)
|
||||
self.currentView.setData({title: state.layoutToUse.title, contents: welcome})
|
||||
new Combine([
|
||||
|
||||
new Combine([
|
||||
this.viewSelector(new Title(state.layoutToUse.title, 2), welcome),
|
||||
this.viewSelector(new Title(state.layoutToUse.title.Clone(), 2), state.layoutToUse.title.Clone(), welcome, "welcome"),
|
||||
map.SetClass("w-full h-64 shrink-0 rounded-lg"),
|
||||
new SearchAndGo(state),
|
||||
this.viewSelector(new Title(
|
||||
new VariableUiElement(elementsInview.map(elements => "There are " + elements?.length + " elements in view"))), new FixedUiElement("Stats")),
|
||||
|
||||
new VariableUiElement(elementsInview.map(elements => "There are " + elements?.length + " elements in view"))),
|
||||
"Statistics",
|
||||
new FixedUiElement("Stats"), "statistics"),
|
||||
|
||||
this.viewSelector(new FixedUiElement("Filter"),
|
||||
"Filters",
|
||||
new Lazy(() => {
|
||||
return new FilterView(state.filteredLayers, state.overlayToggles)
|
||||
})
|
||||
return new FilterView(state.filteredLayers, state.overlayToggles)
|
||||
}), "filters"
|
||||
),
|
||||
|
||||
new VariableUiElement(elementsInview.map(elements => this.mainElementsView(elements).SetClass("block mx-2")))
|
||||
.SetClass("block shrink-2 overflow-x-scroll h-full border-2 border-subtle rounded-lg"),
|
||||
new LanguagePicker(Object.keys(state.layoutToUse.title)).SetClass("mt-2")
|
||||
])
|
||||
.SetClass("w-1/2 m-4 flex flex-col"),
|
||||
new VariableUiElement(this.currentView).SetClass("w-1/2 overflow-y-auto m-4 ml-0 p-2 border-2 border-subtle rounded-xl m-y-8")
|
||||
|
||||
new VariableUiElement(elementsInview.map(elements => this.mainElementsView(elements).SetClass("block m-2")))
|
||||
.SetClass("block shrink-2 overflow-x-auto h-full border-2 border-subtle rounded-lg"),
|
||||
this.allDocumentationButtons(),
|
||||
new LanguagePicker(Object.keys(state.layoutToUse.title.translations)).SetClass("mt-2")
|
||||
]).SetClass("w-1/2 m-4 flex flex-col shrink-0 grow-0"),
|
||||
new VariableUiElement(this.currentView.map(({title, contents}) => {
|
||||
return new Combine([
|
||||
new Title(Translations.W(title), 2).SetClass("shrink-0 border-b-4 border-subtle"),
|
||||
Translations.W(contents).SetClass("shrink-2 overflow-y-auto block")
|
||||
]).SetClass("flex flex-col h-full")
|
||||
})).SetClass("w-1/2 m-4 p-2 border-2 border-subtle rounded-xl m-4 ml-0 mr-8 shrink-0 grow-0")
|
||||
]).SetClass("flex h-full")
|
||||
.AttachTo("leafletDiv")
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ import {SaveButton} from "./Popup/SaveButton";
|
|||
import {MapillaryLink} from "./BigComponents/MapillaryLink";
|
||||
import {CheckBox} from "./Input/Checkboxes";
|
||||
import Slider from "./Input/Slider";
|
||||
import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig";
|
||||
|
||||
export interface SpecialVisualization {
|
||||
funcName: string,
|
||||
|
@ -207,7 +208,7 @@ class NearbyImageVis implements SpecialVisualization {
|
|||
const nearby = new Lazy(() => {
|
||||
const towardsCenter = new CheckBox(t.onlyTowards, false)
|
||||
|
||||
const radiusValue = state?.osmConnection?.GetPreference("nearby-images-radius","300").sync(s => Number(s), [], i => ""+i) ?? new UIEventSource(300);
|
||||
const radiusValue = state?.osmConnection?.GetPreference("nearby-images-radius", "300").sync(s => Number(s), [], i => "" + i) ?? new UIEventSource(300);
|
||||
|
||||
const radius = new Slider(25, 500, {
|
||||
value:
|
||||
|
@ -285,7 +286,13 @@ export default class SpecialVisualizations {
|
|||
|
||||
public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.init()
|
||||
|
||||
public static DocumentationFor(viz: SpecialVisualization): BaseUIElement {
|
||||
public static DocumentationFor(viz: string | SpecialVisualization): BaseUIElement | undefined {
|
||||
if (typeof viz === "string") {
|
||||
viz = SpecialVisualizations.specialVisualizations.find(sv => sv.funcName === viz)
|
||||
}
|
||||
if(viz === undefined){
|
||||
return undefined;
|
||||
}
|
||||
return new Combine(
|
||||
[
|
||||
new Title(viz.funcName, 3),
|
||||
|
|
|
@ -1,21 +1,27 @@
|
|||
{
|
||||
"id": "shared_questions",
|
||||
"questions": {
|
||||
"description": "Show the images block at this location",
|
||||
"id": "questions"
|
||||
},
|
||||
"images": {
|
||||
"description": "This block shows the known images which are linked with the `image`-keys, but also via `mapillary` and `wikidata`",
|
||||
"render": "{image_carousel()}{image_upload()}{nearby_images(expandable)}"
|
||||
},
|
||||
"mapillary": {
|
||||
"description": "Shows a button to open Mapillary on this location",
|
||||
"render": "{mapillary()}"
|
||||
},
|
||||
"export_as_gpx": {
|
||||
"description": "Shows a button to export this feature as GPX. Especially useful for route relations",
|
||||
"render": "{export_as_gpx()}"
|
||||
},
|
||||
"export_as_geojson": {
|
||||
"description": "Shows a button to export this feature as geojson. Especially useful for debugging or using this in other programs",
|
||||
"render": "{export_as_geojson()}"
|
||||
},
|
||||
"wikipedia": {
|
||||
"description": "Shows a wikipedia box with the corresponding wikipedia article",
|
||||
"render": "{wikipedia():max-height:25rem}",
|
||||
"question": {
|
||||
"en": "What is the corresponding Wikidata entity?",
|
||||
|
@ -93,9 +99,12 @@
|
|||
}
|
||||
},
|
||||
"reviews": {
|
||||
"description": "Shows the reviews module (including the possibility to leave a review)",
|
||||
|
||||
"render": "{reviews()}"
|
||||
},
|
||||
"minimap": {
|
||||
"description": "Shows a small map with the feature. Added by default to every popup",
|
||||
"render": "{minimap(18, id): width:100%; height:8rem; border-radius:2rem; overflow: hidden; pointer-events: none;}"
|
||||
},
|
||||
"phone": {
|
||||
|
@ -855,7 +864,7 @@
|
|||
"render": "<div class='subtle' style='font-size: small; margin-top: 2em; margin-bottom: 0.5em;'><a href='https://www.openStreetMap.org/changeset/{_last_edit:changeset}' target='_blank'>Last edited on {_last_edit:timestamp}</a> by <a href='https://www.openStreetMap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a></div>"
|
||||
},
|
||||
"all_tags": {
|
||||
"#": "Prints all the tags",
|
||||
"description": "Shows a table with all the tags of the feature",
|
||||
"render": "{all_tags()}"
|
||||
},
|
||||
"level": {
|
||||
|
|
|
@ -819,6 +819,10 @@ video {
|
|||
margin: 1.25rem;
|
||||
}
|
||||
|
||||
.m-2 {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.m-0\.5 {
|
||||
margin: 0.125rem;
|
||||
}
|
||||
|
@ -831,10 +835,6 @@ video {
|
|||
margin: 0.75rem;
|
||||
}
|
||||
|
||||
.m-2 {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.m-6 {
|
||||
margin: 1.5rem;
|
||||
}
|
||||
|
@ -843,11 +843,6 @@ video {
|
|||
margin: 1px;
|
||||
}
|
||||
|
||||
.mx-2 {
|
||||
margin-left: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.my-2 {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
@ -879,6 +874,10 @@ video {
|
|||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.mr-8 {
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
@ -887,6 +886,10 @@ video {
|
|||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.mt-1 {
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
@ -903,10 +906,6 @@ video {
|
|||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
@ -1071,14 +1070,14 @@ video {
|
|||
height: 3rem;
|
||||
}
|
||||
|
||||
.h-1\/2 {
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
.h-4 {
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.h-1\/2 {
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
.h-screen {
|
||||
height: 100vh;
|
||||
}
|
||||
|
@ -1163,14 +1162,14 @@ video {
|
|||
width: 3rem;
|
||||
}
|
||||
|
||||
.w-0 {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.w-4 {
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.w-0 {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.w-screen {
|
||||
width: 100vw;
|
||||
}
|
||||
|
@ -1361,12 +1360,12 @@ video {
|
|||
overflow: scroll;
|
||||
}
|
||||
|
||||
.overflow-y-auto {
|
||||
overflow-y: auto;
|
||||
.overflow-x-auto {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.overflow-x-scroll {
|
||||
overflow-x: scroll;
|
||||
.overflow-y-auto {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.truncate {
|
||||
|
@ -1441,6 +1440,10 @@ video {
|
|||
border-width: 4px;
|
||||
}
|
||||
|
||||
.border-b-4 {
|
||||
border-bottom-width: 4px;
|
||||
}
|
||||
|
||||
.border-l-4 {
|
||||
border-left-width: 4px;
|
||||
}
|
||||
|
@ -2447,6 +2450,15 @@ input {
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.code {
|
||||
display: inline-block;
|
||||
background-color: lightgray;
|
||||
padding: 0.5em;
|
||||
word-break: break-word;
|
||||
color: black;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/** Switch layout **/
|
||||
|
||||
.small-image img {
|
||||
|
|
|
@ -580,6 +580,15 @@ input {
|
|||
}
|
||||
|
||||
|
||||
.code {
|
||||
display: inline-block;
|
||||
background-color: lightgray;
|
||||
padding: 0.5em;
|
||||
word-break: break-word;
|
||||
color: black;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/** Switch layout **/
|
||||
.small-image img {
|
||||
height: 1em;
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
import Combine from "../UI/Base/Combine";
|
||||
import BaseUIElement from "../UI/BaseUIElement";
|
||||
import Translations from "../UI/i18n/Translations";
|
||||
import {existsSync, mkdir, mkdirSync, writeFileSync} from "fs";
|
||||
import {existsSync, mkdirSync, writeFileSync} from "fs";
|
||||
import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
|
||||
import TableOfContents from "../UI/Base/TableOfContents";
|
||||
import SimpleMetaTaggers, {SimpleMetaTagger} from "../Logic/SimpleMetaTagger";
|
||||
import SimpleMetaTaggers from "../Logic/SimpleMetaTagger";
|
||||
import ValidatedTextField from "../UI/Input/ValidatedTextField";
|
||||
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
|
||||
import SpecialVisualizations from "../UI/SpecialVisualizations";
|
||||
import FeatureSwitchState from "../Logic/State/FeatureSwitchState";
|
||||
import {ExtraFunctions} from "../Logic/ExtraFunctions";
|
||||
import Title from "../UI/Base/Title";
|
||||
import Minimap from "../UI/Base/Minimap";
|
||||
import {QueryParameters} from "../Logic/Web/QueryParameters";
|
||||
import QueryParameterDocumentation from "../UI/QueryParameterDocumentation";
|
||||
import ScriptUtils from "./ScriptUtils";
|
||||
import List from "../UI/Base/List";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue