Add ToC to generated pages

This commit is contained in:
pietervdvn 2021-11-30 22:50:48 +01:00
parent b4529e4f63
commit 752538ec14
18 changed files with 346 additions and 243 deletions

View file

@ -5,8 +5,8 @@
## Table of contents ## Table of contents
1. [Special and other useful layers](#Special_and_other_useful_layers) 1. [Special and other useful layers](#special-and-other-useful-layers)
1. [Priviliged layers](#Priviliged_layers) 1. [Priviliged layers](#priviliged-layers)
+ [gps_location](#gps_location) + [gps_location](#gps_location)
+ [gps_location_history](#gps_location_history) + [gps_location_history](#gps_location_history)
+ [home_location](#home_location) + [home_location](#home_location)
@ -14,17 +14,17 @@
+ [type_node](#type_node) + [type_node](#type_node)
+ [conflation](#conflation) + [conflation](#conflation)
+ [left_right_style](#left_right_style) + [left_right_style](#left_right_style)
1. [Frequently reused layers](#Frequently_reused_layers) 1. [Frequently reused layers](#frequently-reused-layers)
+ [bicycle_library](#bicycle_library) + [bicycle_library](#bicycle_library)
* [Themes using this layer](#Themes_using_this_layer) * [Themes using this layer](#themes-using-this-layer)
+ [drinking_water](#drinking_water) + [drinking_water](#drinking_water)
* [Themes using this layer](#Themes_using_this_layer) * [Themes using this layer](#themes-using-this-layer)
+ [food](#food) + [food](#food)
* [Themes using this layer](#Themes_using_this_layer) * [Themes using this layer](#themes-using-this-layer)
+ [map](#map) + [map](#map)
* [Themes using this layer](#Themes_using_this_layer) * [Themes using this layer](#themes-using-this-layer)
+ [all_streets](#all_streets) + [all_streets](#all_streets)
* [Themes using this layer](#Themes_using_this_layer) * [Themes using this layer](#themes-using-this-layer)
MapComplete has a few data layers available in the theme which have special properties through builtin-hooks. Furthermore, there are some normal layers (which are built from normal Theme-config files) but are so general that they get a mention here. MapComplete has a few data layers available in the theme which have special properties through builtin-hooks. Furthermore, there are some normal layers (which are built from normal Theme-config files) but are so general that they get a mention here.

View file

@ -5,21 +5,21 @@
## Table of contents ## Table of contents
1. [Metatags](#Metatags) 1. [Metatags](#metatags)
- [Metatags calculated by MapComplete](#Metatags_calculated_by_MapComplete) - [Metatags calculated by MapComplete](#metatags-calculated-by-mapcomplete)
+ [_lat, _lon](#_lat,__lon) + [_lat, _lon](#_lat,-_lon)
+ [_layer](#_layer) + [_layer](#_layer)
+ [_surface, _surface:ha](#_surface,__surface:ha) + [_surface, _surface:ha](#_surface,-_surfaceha)
+ [_length, _length:km](#_length,__length:km) + [_length, _length:km](#_length,-_lengthkm)
+ [Theme-defined keys](#Theme-defined_keys) + [Theme-defined keys](#theme-defined-keys)
+ [_country](#_country) + [_country](#_country)
+ [_isOpen, _isOpen:description](#_isOpen,__isOpen:description) + [_isOpen, _isOpen:description](#_isopen,-_isopendescription)
+ [_direction:numerical, _direction:leftright](#_direction:numerical,__direction:leftright) + [_direction:numerical, _direction:leftright](#_directionnumerical,-_direction:leftright)
+ [_now:date, _now:datetime, _loaded:date, _loaded:_datetime](#_now:date,__now:datetime,__loaded:date,__loaded:_datetime) + [_now:date, _now:datetime, _loaded:date, _loaded:_datetime](#_nowdate,-_now:datetime,-_loaded:date,-_loaded:_datetime)
+ [_last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number, _backend](#_last_edit:contributor,__last_edit:contributor:uid,__last_edit:changeset,__last_edit:timestamp,__version_number,__backend) + [_last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number, _backend](#_last_editcontributor,-_last_edit:contributor:uid,-_last_edit:changeset,-_last_edit:timestamp,-_version_number,-_backend)
+ [sidewalk:left, sidewalk:right, generic_key:left:property, generic_key:right:property](#sidewalk:left,_sidewalk:right,_generic_key:left:property,_generic_key:right:property) + [sidewalk:left, sidewalk:right, generic_key:left:property, generic_key:right:property](#sidewalkleft,-sidewalk:right,-generic_key:left:property,-generic_key:right:property)
+ [distanceTo](#distanceTo) + [distanceTo](#distanceto)
+ [overlapWith](#overlapWith) + [overlapWith](#overlapwith)
+ [closest](#closest) + [closest](#closest)
+ [closestn](#closestn) + [closestn](#closestn)
+ [memberships](#memberships) + [memberships](#memberships)

View file

@ -5,29 +5,59 @@
## Table of contents ## Table of contents
1. [Available types for text fields](#Available_types_for_text_fields) 1. [Available types for text fields](#available-types-for-text-fields)
+ [string](#string)
+ [text](#text)
+ [date](#date)
+ [direction](#direction)
+ [length](#length)
+ [wikidata](#wikidata)
+ [int](#int)
+ [nat](#nat)
+ [pnat](#pnat)
+ [float](#float)
+ [pfloat](#pfloat)
+ [email](#email)
+ [url](#url)
+ [phone](#phone)
+ [opening_hours](#opening_hours)
+ [color](#color)
The listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them ## string The listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them
A basic string ### string
## text
A string, but allows input of longer strings more comfortably and supports newlines (a text area)
## date A basic string
A date ### text
## direction
A geographical direction, in degrees. 0° is north, 90° is east, ... Will return a value between 0 (incl) and 360 (excl)
## length A string, but allows input of longer strings more comfortably and supports newlines (a text area)
### date
A date
### direction
A geographical direction, in degrees. 0° is north, 90° is east, ... Will return a value between 0 (incl) and 360 (excl)
### length
A geographical length in meters (rounded at two points). Will give an extra minimap with a measurement tool. Arguments: [ zoomlevel, preferredBackgroundMapType (comma separated) ], e.g. `["21", "map,photo"]
### wikidata
A geographical length in meters (rounded at two points). Will give an extra minimap with a measurement tool. Arguments: [ zoomlevel, preferredBackgroundMapType (comma separated) ], e.g. `["21", "map,photo"]
## wikidata
A wikidata identifier, e.g. Q42. A wikidata identifier, e.g. Q42.
@ -68,41 +98,59 @@ removePostfixes | remove these snippets of text from the end of the passed strin
} }
] ]
} }
``` ```
## int ### int
A number
## nat
A positive number or zero A number
## pnat ### nat
A strict positive number
## float
A decimal A positive number or zero
## pfloat ### pnat
A positive decimal (incl zero)
## email
An email adress A strict positive number
## url ### float
A url
## phone
A phone number A decimal
### pfloat
A positive decimal (incl zero)
### email
An email adress
### url
A url
### phone
A phone number
### opening_hours
## opening_hours
Has extra elements to easily input when a POI is opened. Has extra elements to easily input when a POI is opened.
@ -139,9 +187,11 @@ postfix | Piece of text that will always be added to the end of the generated op
} }
``` ```
*Don't forget to pass the prefix and postfix in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )` *Don't forget to pass the prefix and postfix in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )`
### color
## color
Shows a color picker Shows a color picker

View file

@ -5,41 +5,41 @@
## Table of contents ## Table of contents
1. [Special tag renderings](#Special_tag_renderings) 1. [Special tag renderings](#special-tag-renderings)
+ [all_tags](#all_tags) + [all_tags](#all_tags)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [image_carousel](#image_carousel) + [image_carousel](#image_carousel)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [image_upload](#image_upload) + [image_upload](#image_upload)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [wikipedia](#wikipedia) + [wikipedia](#wikipedia)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [minimap](#minimap) + [minimap](#minimap)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [sided_minimap](#sided_minimap) + [sided_minimap](#sided_minimap)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [reviews](#reviews) + [reviews](#reviews)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [opening_hours_table](#opening_hours_table) + [opening_hours_table](#opening_hours_table)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [live](#live) + [live](#live)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [histogram](#histogram) + [histogram](#histogram)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [share_link](#share_link) + [share_link](#share_link)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [canonical](#canonical) + [canonical](#canonical)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [import_button](#import_button) + [import_button](#import_button)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [multi_apply](#multi_apply) + [multi_apply](#multi_apply)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [tag_apply](#tag_apply) + [tag_apply](#tag_apply)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [export_as_gpx](#export_as_gpx) + [export_as_gpx](#export_as_gpx)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
+ [clear_location_history](#clear_location_history) + [clear_location_history](#clear_location_history)
* [Example usage](#Example_usage) * [Example usage](#example-usage)
In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's. General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_ need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's. General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_ need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args

View file

@ -5,8 +5,8 @@
## Table of contents ## Table of contents
1. [URL-parameters and URL-hash](#URL-parameters_and_URL-hash) 1. [URL-parameters and URL-hash](#url-parameters-and-url-hash)
- [What is a URL parameter?](#what_is_a_url_parameter) - [What is a URL parameter?](#what-is-a-url-parameter)
- [fs-userbadge](#fs-userbadge) - [fs-userbadge](#fs-userbadge)
- [fs-search](#fs-search) - [fs-search](#fs-search)
- [fs-background](#fs-background) - [fs-background](#fs-background)
@ -24,10 +24,10 @@
- [test](#test) - [test](#test)
- [debug](#debug) - [debug](#debug)
- [fake-user](#fake-user) - [fake-user](#fake-user)
- [overpassUrl](#overpassUrl) - [overpassUrl](#overpassurl)
- [overpassTimeout](#overpassTimeout) - [overpassTimeout](#overpasstimeout)
- [overpassMaxZoom](#overpassMaxZoom) - [overpassMaxZoom](#overpassmaxzoom)
- [osmApiTileSize](#osmApiTileSize) - [osmApiTileSize](#osmapitilesize)
- [background](#background) - [background](#background)
- [layer-<layer-id>](#layer-<layer-id>) - [layer-<layer-id>](#layer-<layer-id>)

View file

@ -485,7 +485,6 @@ export default class SimpleMetaTagger {
public static HelpText(): BaseUIElement { public static HelpText(): BaseUIElement {
const subElements: (string | BaseUIElement)[] = [ const subElements: (string | BaseUIElement)[] = [
new Combine([ new Combine([
new Title("Metatags", 1),
"Metatags are extra tags available, in order to display more data or to give better questions.", "Metatags are extra tags available, in order to display more data or to give better questions.",
"The are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an overview of the available metatags.", "The are calculated automatically on every feature when the data arrives in the webbrowser. This document gives an overview of the available metatags.",
"**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object" "**Hint:** when using metatags, add the [query parameter](URL_Parameters.md) `debug=true` to the URL. This will include a box in the popup for features which shows all the properties of the object"

View file

@ -4,8 +4,6 @@
import {UIEventSource} from "../UIEventSource"; import {UIEventSource} from "../UIEventSource";
import Hash from "./Hash"; import Hash from "./Hash";
import {Utils} from "../../Utils"; import {Utils} from "../../Utils";
import Title from "../../UI/Base/Title";
import Combine from "../../UI/Base/Combine";
export class QueryParameters { export class QueryParameters {
@ -13,29 +11,11 @@ export class QueryParameters {
private static _wasInitialized: Set<string> = new Set() private static _wasInitialized: Set<string> = new Set()
private static knownSources = {}; private static knownSources = {};
private static initialized = false; private static initialized = false;
private static defaults = {} static defaults = {}
static documentation = {}
private static documentation = {}
private static QueryParamDocsIntro = "\n" +
"URL-parameters and URL-hash\n" +
"============================\n" +
"\n" +
"This document gives an overview of which URL-parameters can be used to influence MapComplete.\n" +
"\n" +
"What is a URL parameter?\n" +
"------------------------\n" +
"\n" +
"URL-parameters are extra parts of the URL used to set the state.\n" +
"\n" +
"For example, if the url is `https://mapcomplete.osm.be/cyclofix?lat=51.0&lon=4.3&z=5&test=true#node/1234`,\n" +
"the URL-parameters are stated in the part between the `?` and the `#`. There are multiple, all separated by `&`, namely:\n" +
"\n" +
"- The url-parameter `lat` is `51.0` in this instance\n" +
"- The url-parameter `lon` is `4.3` in this instance\n" +
"- The url-parameter `z` is `5` in this instance\n" +
"- The url-parameter `test` is `true` in this instance\n" +
"\n" +
"Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case."
public static GetQueryParameter(key: string, deflt: string, documentation?: string): UIEventSource<string> { public static GetQueryParameter(key: string, deflt: string, documentation?: string): UIEventSource<string> {
if (!this.initialized) { if (!this.initialized) {
@ -59,19 +39,7 @@ export class QueryParameters {
return QueryParameters.GetQueryParameter(key, deflt, documentation).map(str => str === "true", [], b => "" + b) return QueryParameters.GetQueryParameter(key, deflt, documentation).map(str => str === "true", [], b => "" + b)
} }
public static GenerateQueryParameterDocs(): string {
const docs = [QueryParameters.QueryParamDocsIntro];
for (const key in QueryParameters.documentation) {
const c = new Combine([
new Title(key, 2),
QueryParameters.documentation[key],
QueryParameters.defaults[key] === undefined ? "No default value set" : `The default value is _${QueryParameters.defaults[key]}_`
])
docs.push(c.AsMarkdown())
}
return docs.join("\n\n");
}
public static wasInitialized(key: string): boolean { public static wasInitialized(key: string): boolean {
return QueryParameters._wasInitialized.has(key) return QueryParameters._wasInitialized.has(key)
@ -108,9 +76,6 @@ export class QueryParameters {
} }
} }
window["mapcomplete_query_parameter_overview"] = () => {
console.log(QueryParameters.GenerateQueryParameterDocs())
}
} }
private static Serialize() { private static Serialize() {

View file

@ -47,18 +47,10 @@ export default class Combine extends BaseUIElement {
return el; return el;
} }
public getToC(): Title[]{ public getElements(): BaseUIElement[]{
const titles = [] return this.uiElements
for (const uiElement of this.uiElements) {
if(uiElement instanceof Combine){
titles.push(...uiElement.getToC())
}else if(uiElement instanceof Title){
titles.push(uiElement)
}
}
return titles
} }
} }

View file

@ -1,25 +1,26 @@
import BaseUIElement from "../BaseUIElement"; import BaseUIElement from "../BaseUIElement";
export class FixedUiElement extends BaseUIElement { export class FixedUiElement extends BaseUIElement {
private _html: string; public readonly content: string;
constructor(html: string) { constructor(html: string) {
super(); super();
this._html = html ?? ""; this.content = html ?? "";
} }
InnerRender(): string { InnerRender(): string {
return this._html; return this.content;
} }
AsMarkdown(): string { AsMarkdown(): string {
return this._html; return this.content;
} }
protected InnerConstructElement(): HTMLElement { protected InnerConstructElement(): HTMLElement {
const e = document.createElement("span") const e = document.createElement("span")
e.innerHTML = this._html e.innerHTML = this.content
return e; return e;
} }
} }

View file

@ -10,7 +10,7 @@ export default class List extends BaseUIElement {
super(); super();
this._ordered = ordered; this._ordered = ordered;
this.uiElements = Utils.NoNull(uiElements) this.uiElements = Utils.NoNull(uiElements)
.map(Translations.W); .map(s => Translations.W(s));
} }
AsMarkdown(): string { AsMarkdown(): string {

123
UI/Base/TableOfContents.ts Normal file
View file

@ -0,0 +1,123 @@
import Combine from "./Combine";
import BaseUIElement from "../BaseUIElement";
import {Translation} from "../i18n/Translation";
import {FixedUiElement} from "./FixedUiElement";
import Title from "./Title";
import List from "./List";
import Hash from "../../Logic/Web/Hash";
import Link from "./Link";
import {Utils} from "../../Utils";
export default class TableOfContents extends Combine {
private readonly titles: Title[]
constructor(elements: Combine | Title[], options?: {
noTopLevel: false | boolean,
maxDepth?: number
}) {
let titles: Title[]
if (elements instanceof Combine) {
titles = TableOfContents.getTitles(elements.getElements())
} else {
titles = elements
}
let els: { level: number, content: BaseUIElement }[] = []
for (const title of titles) {
let content: BaseUIElement
if (title.title instanceof Translation) {
content = title.title.Clone()
}else if(title.title instanceof FixedUiElement){
content = title.title
}else if(Utils.runningFromConsole){
content = new FixedUiElement(title.AsMarkdown())
} else {
content = new FixedUiElement(title.title.ConstructElement().innerText)
}
const vis = new Link(content, "#" + title.id)
Hash.hash.addCallbackAndRun(h => {
if (h === title.id) {
vis.SetClass("font-bold")
} else {
vis.RemoveClass("font-bold")
}
})
els.push({level: title.level, content: vis})
}
const minLevel = Math.min(...els.map(e => e.level))
if (options?.noTopLevel) {
els = els.filter(e => e.level !== minLevel )
}
if(options?.maxDepth){
els = els.filter(e => e.level <= (options.maxDepth + minLevel))
}
super(TableOfContents.mergeLevel(els).map(el => el.SetClass("mt-2")));
this.SetClass("flex flex-col")
this.titles = titles;
}
private static getTitles(elements: BaseUIElement[]): Title[]{
const titles = []
for (const uiElement of elements){
if(uiElement instanceof Combine){
titles.push(...TableOfContents.getTitles(uiElement.getElements()))
}else if(uiElement instanceof Title){
titles.push(uiElement)
}
}
return titles
}
private static mergeLevel(elements: { level: number, content: BaseUIElement }[]): BaseUIElement[] {
const maxLevel = Math.max(...elements.map(e => e.level))
const minLevel = Math.min(...elements.map(e => e.level))
if (maxLevel === minLevel) {
return elements.map(e => e.content)
}
const result: { level: number, content: BaseUIElement } [] = []
let running: BaseUIElement[] = []
for (const element of elements) {
if (element.level === maxLevel) {
running.push(element.content)
continue
}
if (running.length !== undefined) {
result.push({
content: new List(running),
level: maxLevel - 1
})
running = []
}
result.push(element)
}
if (running.length !== undefined) {
result.push({
content: new List(running),
level: maxLevel - 1
})
}
return TableOfContents.mergeLevel(result)
}
AsMarkdown(): string {
const depthIcons = ["1."," -"," +"," *"]
const lines = ["## Table of contents\n"];
const minLevel = Math.min(...this.titles.map(t => t.level))
for (const title of this.titles) {
const prefix = depthIcons[title.level - minLevel] ?? " ~"
const text = title.title.AsMarkdown().replace("\n","")
const link = title.id
lines.push(prefix + " ["+text+"](#"+link+")")
}
return lines.join("\n")+"\n\n"
}
}

View file

@ -1,6 +1,5 @@
import BaseUIElement from "../BaseUIElement"; import BaseUIElement from "../BaseUIElement";
import {FixedUiElement} from "./FixedUiElement"; import {FixedUiElement} from "./FixedUiElement";
import Hash from "../../Logic/Web/Hash";
export default class Title extends BaseUIElement { export default class Title extends BaseUIElement {
public readonly title: BaseUIElement; public readonly title: BaseUIElement;
@ -17,7 +16,19 @@ export default class Title extends BaseUIElement {
this.title = embedded this.title = embedded
} }
this.level = level; this.level = level;
this.id = this.title.ConstructElement()?.innerText?.replace(/ /g, '_') ?? ""
let innerText : string = undefined;
if(typeof embedded === "string" ) {
innerText = embedded
}else if(embedded instanceof FixedUiElement){
innerText = embedded.content
}else{
this.title.ConstructElement()?.innerText
}
this.id = innerText?.replace(/ /g, '-')
?.replace(/[?#.;:/]/, "")
?.toLowerCase() ?? ""
this.SetClass(Title.defaultClassesPerLevel[level] ?? "") this.SetClass(Title.defaultClassesPerLevel[level] ?? "")
} }

View file

@ -553,13 +553,15 @@ export default class ValidatedTextField {
return input; return input;
} }
public static HelpText(): string { public static HelpText(): BaseUIElement {
const explanations = ValidatedTextField.tpList.map(type => ["## " + type.name, "", type.explanation].join("\n")).join("\n\n") const explanations : BaseUIElement[]=
ValidatedTextField.tpList.map(type =>
new Combine([new Title(type.name,3), type.explanation]).SetClass("flex flex-col"))
return new Combine([ return new Combine([
new Title("Available types for text fields", 1), new Title("Available types for text fields", 1),
"The listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them", "The listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them",
explanations ...explanations
]).SetClass("flex flex-col").AsMarkdown() ]).SetClass("flex flex-col")
} }
private static tp(name: string, private static tp(name: string,

View file

@ -5,97 +5,10 @@ import Title from "./Base/Title";
import Toggleable, {Accordeon} from "./Base/Toggleable"; import Toggleable, {Accordeon} from "./Base/Toggleable";
import List from "./Base/List"; import List from "./Base/List";
import BaseUIElement from "./BaseUIElement"; import BaseUIElement from "./BaseUIElement";
import Link from "./Base/Link";
import LanguagePicker from "./LanguagePicker"; import LanguagePicker from "./LanguagePicker";
import Hash from "../Logic/Web/Hash";
import {Translation} from "./i18n/Translation";
import {SubtleButton} from "./Base/SubtleButton"; import {SubtleButton} from "./Base/SubtleButton";
import Svg from "../Svg"; import Svg from "../Svg";
import TableOfContents from "./Base/TableOfContents";
class TableOfContents extends Combine {
private readonly titles: Title[]
constructor(elements: Combine | Title[], options: {
noTopLevel: false | boolean,
maxDepth?: number
}) {
let titles: Title[]
if (elements instanceof Combine) {
titles = elements.getToC()
} else {
titles = elements
}
let els: { level: number, content: BaseUIElement }[] = []
for (const title of titles) {
let content: BaseUIElement
if (title.title instanceof Translation) {
content = title.title.Clone()
} else {
content = new FixedUiElement(title.title.ConstructElement().innerText)
}
const vis = new Link(content, "#" + title.id)
Hash.hash.addCallbackAndRun(h => {
if (h === title.id) {
vis.SetClass("font-bold")
} else {
vis.RemoveClass("font-bold")
}
})
els.push({level: title.level, content: vis})
}
if (options.noTopLevel) {
const minLevel = Math.min(...els.map(e => e.level))
els = els.filter(e => e.level !== minLevel && e.level <= (options.maxDepth + minLevel))
}
super(TableOfContents.mergeLevel(els).map(el => el.SetClass("mt-2")));
this.SetClass("flex flex-col")
this.titles = titles;
}
private static mergeLevel(elements: { level: number, content: BaseUIElement }[]): BaseUIElement[] {
const maxLevel = Math.max(...elements.map(e => e.level))
const minLevel = Math.min(...elements.map(e => e.level))
if (maxLevel === minLevel) {
return elements.map(e => e.content)
}
const result: { level: number, content: BaseUIElement } [] = []
let running: BaseUIElement[] = []
for (const element of elements) {
if (element.level === maxLevel) {
running.push(element.content)
continue
}
if (running.length !== undefined) {
result.push({
content: new List(running),
level: maxLevel - 1
})
running = []
}
result.push(element)
}
if (running.length !== undefined) {
result.push({
content: new List(running),
level: maxLevel - 1
})
}
return TableOfContents.mergeLevel(result)
}
AsMarkdown(): string {
return super.AsMarkdown();
}
}
class Snippet extends Toggleable { class Snippet extends Toggleable {
constructor(translations, ...extraContent: BaseUIElement[]) { constructor(translations, ...extraContent: BaseUIElement[]) {

View file

@ -0,0 +1,40 @@
import BaseUIElement from "./BaseUIElement";
import Combine from "./Base/Combine";
import Title from "./Base/Title";
import List from "./Base/List";
import Translations from "./i18n/Translations";
import {QueryParameters} from "../Logic/Web/QueryParameters";
export default class QueryParameterDocumentation {
private static QueryParamDocsIntro = ([
new Title("URL-parameters and URL-hash", 1),
"This document gives an overview of which URL-parameters can be used to influence MapComplete.",
new Title("What is a URL parameter?", 2),
"\"URL-parameters are extra parts of the URL used to set the state.",
"For example, if the url is `https://mapcomplete.osm.be/cyclofix?lat=51.0&lon=4.3&z=5&test=true#node/1234`, " +
"the URL-parameters are stated in the part between the `?` and the `#`. There are multiple, all separated by `&`, namely: ",
new List([
"The url-parameter `lat` is `51.0` in this instance",
"The url-parameter `lon` is `4.3` in this instance",
"The url-parameter `z` is `5` in this instance",
"The url-parameter `test` is `true` in this instance"
].map(s => Translations.W(s))
),
"Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case."
])
public static GenerateQueryParameterDocs(): BaseUIElement {
const docs : (string | BaseUIElement)[] = [...QueryParameterDocumentation.QueryParamDocsIntro];
for (const key in QueryParameters.documentation) {
const c = new Combine([
new Title(key, 2),
QueryParameters.documentation[key],
QueryParameters.defaults[key] === undefined ? "No default value set" : `The default value is _${QueryParameters.defaults[key]}_`
])
docs.push(c)
}
return new Combine(docs).SetClass("flex flex-col")
}
}

View file

@ -698,16 +698,10 @@ export default class SpecialVisualizations {
] ]
)); ));
const toc = new List(
SpecialVisualizations.specialVisualizations.map(viz => new Link(viz.funcName, "#" + viz.funcName))
)
return new Combine([ return new Combine([
new Title("Special tag renderings", 3), new Title("Special tag renderings", 1),
"In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's.", "In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's.",
"General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_ need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args", "General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssStyle}`. Note that you _do not_ need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args",
toc,
...helpTexts ...helpTexts
] ]
).SetClass("flex flex-col"); ).SetClass("flex flex-col");

View file

@ -15,8 +15,6 @@ import AvailableBaseLayersImplementation from "./Logic/Actors/AvailableBaseLayer
import ShowOverlayLayerImplementation from "./UI/ShowDataLayer/ShowOverlayLayerImplementation"; import ShowOverlayLayerImplementation from "./UI/ShowDataLayer/ShowOverlayLayerImplementation";
import {DefaultGuiState} from "./UI/DefaultGuiState"; import {DefaultGuiState} from "./UI/DefaultGuiState";
import {Browser} from "leaflet"; import {Browser} from "leaflet";
import win = Browser.win;
import ProfessionalGui from "./UI/ProfessionalGui";
// Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts running from console // Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts running from console
MinimapImplementation.initialize() MinimapImplementation.initialize()

View file

@ -11,20 +11,35 @@ import {QueryParameters} from "../Logic/Web/QueryParameters";
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
import Minimap from "../UI/Base/Minimap"; import Minimap from "../UI/Base/Minimap";
import FeatureSwitchState from "../Logic/State/FeatureSwitchState"; import FeatureSwitchState from "../Logic/State/FeatureSwitchState";
import AllKnownLayers from "../Customizations/AllKnownLayers";
import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
import TableOfContents from "../UI/Base/TableOfContents";
import Title from "../UI/Base/Title";
import QueryParameterDocumentation from "../UI/QueryParameterDocumentation";
Utils.runningFromConsole = true; Utils.runningFromConsole = true;
function WriteFile(filename, html: string | BaseUIElement, autogenSource: string[]): void { function WriteFile(filename, html: BaseUIElement, autogenSource: string[]): void {
if (html instanceof Combine) {
const toc = new TableOfContents(html);
const els = html.getElements();
html = new Combine(
[els.shift(),
toc,
...els
]
)
}
writeFileSync(filename, new Combine([Translations.W(html), writeFileSync(filename, new Combine([Translations.W(html),
"\n\nThis document is autogenerated from " + autogenSource.join(", ") "\n\nThis document is autogenerated from " + autogenSource.join(", ")
]).AsMarkdown()); ]).AsMarkdown());
} }
WriteFile("./Docs/SpecialRenderings.md", SpecialVisualizations.HelpMessage(), ["UI/SpecialVisualisations.ts"]) WriteFile("./Docs/SpecialRenderings.md", SpecialVisualizations.HelpMessage(), ["UI/SpecialVisualisations.ts"])
WriteFile("./Docs/CalculatedTags.md", new Combine([SimpleMetaTagger.HelpText(), ExtraFunction.HelpText()]).SetClass("flex-col"), WriteFile("./Docs/CalculatedTags.md", new Combine([new Title("Metatags", 1), SimpleMetaTagger.HelpText(), ExtraFunction.HelpText()]).SetClass("flex-col"),
["SimpleMetaTagger", "ExtraFunction"]) ["SimpleMetaTagger", "ExtraFunction"])
WriteFile("./Docs/SpecialInputElements.md", ValidatedTextField.HelpText(), ["ValidatedTextField.ts"]); WriteFile("./Docs/SpecialInputElements.md", ValidatedTextField.HelpText(), ["ValidatedTextField.ts"]);
WriteFile("./Docs/BuiltinLayers.md", AllKnownLayouts.GenLayerOverviewText(), ["AllKnownLayers.ts"]) WriteFile("./Docs/BuiltinLayers.md", AllKnownLayouts.GenLayerOverviewText(), ["AllKnownLayers.ts"])
@ -52,7 +67,7 @@ const dummyLayout = new LayoutConfig({
source: { source: {
osmTags: "id~*" osmTags: "id~*"
}, },
mapRendering: [] mapRendering: null,
} }
] ]
@ -62,7 +77,7 @@ new FeatureSwitchState(dummyLayout)
QueryParameters.GetQueryParameter("layer-<layer-id>", "true", "Wether or not the layer with id <layer-id> is shown") QueryParameters.GetQueryParameter("layer-<layer-id>", "true", "Wether or not the layer with id <layer-id> is shown")
WriteFile("./Docs/URL_Parameters.md", QueryParameters.GenerateQueryParameterDocs(), ["QueryParameters"]) WriteFile("./Docs/URL_Parameters.md", QueryParameterDocumentation.GenerateQueryParameterDocs(), ["QueryParameters"])
console.log("Generated docs") console.log("Generated docs")