forked from MapComplete/MapComplete
Studio: add tagInput element
This commit is contained in:
parent
48e976e6b7
commit
2147b8d368
16 changed files with 413 additions and 42 deletions
49
Logic/Web/TagInfo.ts
Normal file
49
Logic/Web/TagInfo.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
import exp from "constants"
|
||||
import { Utils } from "../../Utils"
|
||||
|
||||
export interface TagInfoStats {
|
||||
/**
|
||||
* The total number of entries in the data array, **not** the total number of objects known in OSM!
|
||||
*
|
||||
* Use `data.find(item => item.type==="all").count` for this
|
||||
*/
|
||||
total: number
|
||||
data: {
|
||||
type: "all" | "nodes" | "ways" | "relations"
|
||||
count: number
|
||||
count_fraction: number
|
||||
}[]
|
||||
}
|
||||
|
||||
export default class TagInfo {
|
||||
private readonly _backend: string
|
||||
|
||||
public static readonly global = new TagInfo()
|
||||
|
||||
constructor(backend = "https://taginfo.openstreetmap.org/") {
|
||||
this._backend = backend
|
||||
}
|
||||
|
||||
public getStats(key: string, value?: string): Promise<TagInfoStats> {
|
||||
let url: string
|
||||
if (value) {
|
||||
url = `${this._backend}api/4/tag/stats?key=${key}&value=${value}`
|
||||
} else {
|
||||
url = `${this._backend}api/4/key/stats?key=${key}`
|
||||
}
|
||||
return Utils.downloadJsonCached(url, 1000 * 60 * 60)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the URL to the webpage containing more information
|
||||
* @param k
|
||||
* @param v
|
||||
*/
|
||||
webUrl(k: string, v: string) {
|
||||
if (v) {
|
||||
return `${this._backend}/tags/${k}=${v}#overview`
|
||||
} else {
|
||||
return `${this._backend}/keys/${k}#overview`
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import Combine from "../Base/Combine"
|
|||
import Title from "../Base/Title"
|
||||
import SimpleTagValidator from "./Validators/SimpleTagValidator"
|
||||
import ImageUrlValidator from "./Validators/ImageUrlValidator"
|
||||
import TagKeyValidator from "./Validators/TagKeyValidator";
|
||||
|
||||
export type ValidatorType = (typeof Validators.availableTypes)[number]
|
||||
|
||||
|
@ -60,8 +61,9 @@ export default class Validators {
|
|||
new PhoneValidator(),
|
||||
new OpeningHoursValidator(),
|
||||
new ColorValidator(),
|
||||
new SimpleTagValidator(),
|
||||
new ImageUrlValidator(),
|
||||
new SimpleTagValidator(),
|
||||
new TagKeyValidator()
|
||||
]
|
||||
|
||||
private static _byType = Validators._byTypeConstructor()
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import { Validator } from "../Validator"
|
||||
import { Translation } from "../../i18n/Translation"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import TagKeyValidator from "./TagKeyValidator"
|
||||
|
||||
/**
|
||||
* Checks that the input conforms `key=value`, where `key` and `value` don't have too much weird characters
|
||||
*/
|
||||
export default class SimpleTagValidator extends Validator {
|
||||
private static readonly KeyValidator = new TagKeyValidator()
|
||||
constructor() {
|
||||
super(
|
||||
"simple_tag",
|
||||
|
@ -13,7 +15,7 @@ export default class SimpleTagValidator extends Validator {
|
|||
)
|
||||
}
|
||||
|
||||
getFeedback(tag: string): Translation | undefined {
|
||||
getFeedback(tag: string, _): Translation | undefined {
|
||||
const parts = tag.split("=")
|
||||
if (parts.length < 2) {
|
||||
return Translations.T("A tag should contain a = to separate the 'key' and 'value'")
|
||||
|
@ -27,31 +29,23 @@ export default class SimpleTagValidator extends Validator {
|
|||
}
|
||||
|
||||
const [key, value] = parts
|
||||
if (key.length > 255) {
|
||||
return Translations.T("A `key` should be at most 255 characters")
|
||||
const keyFeedback = SimpleTagValidator.KeyValidator.getFeedback(key, _)
|
||||
if (keyFeedback) {
|
||||
return keyFeedback
|
||||
}
|
||||
|
||||
if (value.length > 255) {
|
||||
return Translations.T("A `value should be at most 255 characters")
|
||||
}
|
||||
|
||||
if (key.length == 0) {
|
||||
return Translations.T("A `key` should not be empty")
|
||||
}
|
||||
if (value.length == 0) {
|
||||
return Translations.T("A `value should not be empty")
|
||||
}
|
||||
|
||||
const keyRegex = /[a-zA-Z0-9:_]+/
|
||||
if (!key.match(keyRegex)) {
|
||||
return Translations.T(
|
||||
"A `key` should only have the characters `a-zA-Z0-9`, `:` or `_`"
|
||||
)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
isValid(tag: string): boolean {
|
||||
return this.getFeedback(tag) === undefined
|
||||
isValid(tag: string, _): boolean {
|
||||
return this.getFeedback(tag, _) === undefined
|
||||
}
|
||||
}
|
||||
|
|
30
UI/InputElement/Validators/TagKeyValidator.ts
Normal file
30
UI/InputElement/Validators/TagKeyValidator.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { Validator } from "../Validator"
|
||||
import { Translation } from "../../i18n/Translation"
|
||||
import Translations from "../../i18n/Translations"
|
||||
|
||||
export default class TagKeyValidator extends Validator {
|
||||
constructor() {
|
||||
super("key", "Validates a key, mostly that no weird characters are used")
|
||||
}
|
||||
|
||||
getFeedback(key: string, _?: () => string): Translation | undefined {
|
||||
if (key.length > 255) {
|
||||
return Translations.T("A `key` should be at most 255 characters")
|
||||
}
|
||||
|
||||
if (key.length == 0) {
|
||||
return Translations.T("A `key` should not be empty")
|
||||
}
|
||||
const keyRegex = /[a-zA-Z0-9:_]+/
|
||||
if (!key.match(keyRegex)) {
|
||||
return Translations.T(
|
||||
"A `key` should only have the characters `a-zA-Z0-9`, `:` or `_`"
|
||||
)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
isValid(key: string, getCountry?: () => string): boolean {
|
||||
return this.getFeedback(key, getCountry) === undefined
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ export default class EditLayerState {
|
|||
this.configuration.addCallback((config) => console.log("Current config is", config))
|
||||
}
|
||||
|
||||
public register(path: ReadonlyArray<string | number>, value: Store<string>) {
|
||||
public register(path: ReadonlyArray<string | number>, value: Store<any>) {
|
||||
value.addCallbackAndRun((v) => {
|
||||
let entry = this.configuration.data
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
|
|
18
UI/Studio/RegisteredTagInput.svelte
Normal file
18
UI/Studio/RegisteredTagInput.svelte
Normal file
|
@ -0,0 +1,18 @@
|
|||
<script lang="ts">
|
||||
import EditLayerState from "./EditLayerState";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import type {TagConfigJson} from "../../Models/ThemeConfig/Json/TagConfigJson";
|
||||
import TagInput from "./TagInput/TagInput.svelte";
|
||||
|
||||
/**
|
||||
* Thin wrapper around 'TagInput' which registers the output with the state
|
||||
*/
|
||||
export let path : (string | number)[]
|
||||
export let state : EditLayerState
|
||||
|
||||
let tag: UIEventSource<TagConfigJson> = new UIEventSource<TagConfigJson>(undefined)
|
||||
|
||||
state.register(path, tag)
|
||||
</script>
|
||||
|
||||
<TagInput {tag} />
|
|
@ -4,17 +4,21 @@
|
|||
import EditLayerState from "./EditLayerState";
|
||||
import SchemaBasedArray from "./SchemaBasedArray.svelte";
|
||||
import SchemaBaseMultiType from "./SchemaBaseMultiType.svelte";
|
||||
import RegisteredTagInput from "./RegisteredTagInput.svelte";
|
||||
|
||||
export let schema: ConfigMeta
|
||||
export let state: EditLayerState
|
||||
export let path: (string | number)[] = []
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
{#if schema.type === "array"}
|
||||
<SchemaBasedArray {path} {state} {schema}/>
|
||||
{:else if schema.hints.typehint === "tag"}
|
||||
<RegisteredTagInput {state} {path}/>
|
||||
{:else if schema.hints.types}
|
||||
<SchemaBaseMultiType {path} {state} {schema}></SchemaBaseMultiType>
|
||||
<SchemaBaseMultiType {path} {state} {schema}/>
|
||||
{:else}
|
||||
<SchemaBasedField {path} {state} {schema}/>
|
||||
{/if}
|
||||
|
|
95
UI/Studio/TagExpression.svelte
Normal file
95
UI/Studio/TagExpression.svelte
Normal file
|
@ -0,0 +1,95 @@
|
|||
<script lang="ts">/**
|
||||
* Allows to create `and` and `or` expressions graphically
|
||||
*/
|
||||
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import type {TagConfigJson} from "../../Models/ThemeConfig/Json/TagConfigJson";
|
||||
import BasicTagInput from "./TagInput/BasicTagInput.svelte";
|
||||
import {onDestroy} from "svelte";
|
||||
import TagInfoStats from "./TagInfoStats.svelte";
|
||||
import TagInput from "./TagInput/TagInput.svelte";
|
||||
import exp from "constants";
|
||||
|
||||
export let tag: UIEventSource<TagConfigJson>
|
||||
let mode: "and" | "or" = "and"
|
||||
|
||||
let basicTags: UIEventSource<UIEventSource<string>[]> = new UIEventSource([])
|
||||
|
||||
/**
|
||||
* Sub-expressions
|
||||
*/
|
||||
let expressions: UIEventSource<UIEventSource<TagConfigJson>[]> = new UIEventSource([])
|
||||
export let uploadableOnly: boolean
|
||||
export let overpassSupportNeeded: boolean
|
||||
|
||||
function update(_) {
|
||||
let config: TagConfigJson = <any>{}
|
||||
if (!mode) {
|
||||
return
|
||||
}
|
||||
const tags = []
|
||||
|
||||
const subpartSources = (<UIEventSource<string | TagConfigJson>[]>basicTags.data).concat(expressions.data)
|
||||
for (const src of subpartSources) {
|
||||
const t = src.data
|
||||
if (!t) {
|
||||
// We indicate upstream that this value is invalid
|
||||
tag.setData(undefined)
|
||||
return
|
||||
}
|
||||
tags.push(t)
|
||||
}
|
||||
config[mode] = tags
|
||||
tag.setData(config)
|
||||
}
|
||||
|
||||
|
||||
function addBasicTag() {
|
||||
const src = new UIEventSource(undefined)
|
||||
basicTags.data.push(src);
|
||||
basicTags.ping()
|
||||
src.addCallbackAndRunD(_ => update(_))
|
||||
}
|
||||
|
||||
function addExpression() {
|
||||
const src = new UIEventSource(undefined)
|
||||
expressions.data.push(src);
|
||||
expressions.ping()
|
||||
src.addCallbackAndRunD(_ => update(_))
|
||||
}
|
||||
|
||||
|
||||
$: update(mode)
|
||||
|
||||
|
||||
addBasicTag()
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<div class="flex items-center">
|
||||
|
||||
<select bind:value={mode}>
|
||||
<option value="and">and</option>
|
||||
{#if !uploadableOnly}
|
||||
<option value="or">or</option>
|
||||
{/if}
|
||||
</select>
|
||||
<div class="border-l-4 border-black flex flex-col ml-1 pl-1">
|
||||
{#each $basicTags as basicTag}
|
||||
<BasicTagInput {overpassSupportNeeded} {uploadableOnly} tag={basicTag}/>
|
||||
{/each}
|
||||
{#each $expressions as expression}
|
||||
<TagInput {overpassSupportNeeded} {uploadableOnly} tag={expression}/>
|
||||
{/each}
|
||||
<div class="flex">
|
||||
<button class="primary w-fit" on:click={addBasicTag}>
|
||||
Add a tag
|
||||
</button>
|
||||
<button class="w-fit" on:click={addExpression}>
|
||||
Add an expression
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
60
UI/Studio/TagInfoStats.svelte
Normal file
60
UI/Studio/TagInfoStats.svelte
Normal file
|
@ -0,0 +1,60 @@
|
|||
<script lang="ts">/**
|
||||
* A small component showing statistics from tagInfo.
|
||||
* Will show this in an 'alert' if very little (<250) tags are known
|
||||
*/
|
||||
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
||||
import {Store, UIEventSource} from "../../Logic/UIEventSource";
|
||||
import type {TagInfoStats} from "../../Logic/Web/TagInfo";
|
||||
import TagInfo from "../../Logic/Web/TagInfo";
|
||||
import {twMerge} from "tailwind-merge";
|
||||
import Loading from "../Base/Loading.svelte";
|
||||
|
||||
export let tag: UIEventSource<string>
|
||||
const tagStabilized = tag.stabilized(500)
|
||||
const tagInfoStats: Store<TagInfoStats> = tagStabilized.bind(tag => {
|
||||
if (!tag) {
|
||||
return undefined
|
||||
}
|
||||
try {
|
||||
|
||||
const t = TagUtils.Tag(tag)
|
||||
const k = t["key"]
|
||||
let v = t["value"]
|
||||
if (typeof v !== "string") {
|
||||
v = undefined
|
||||
}
|
||||
if (!k) {
|
||||
return undefined
|
||||
}
|
||||
return UIEventSource.FromPromise(TagInfo.global.getStats(k, v))
|
||||
} catch (e) {
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
const tagInfoUrl: Store<string> = tagStabilized.mapD(tag => {
|
||||
try {
|
||||
|
||||
const t = TagUtils.Tag(tag)
|
||||
const k = t["key"]
|
||||
let v = t["value"]
|
||||
if (typeof v !== "string") {
|
||||
v = undefined
|
||||
}
|
||||
if (!k) {
|
||||
return undefined
|
||||
}
|
||||
return TagInfo.global.webUrl(k, v)
|
||||
} catch (e) {
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
const total = tagInfoStats.mapD(data => data.data.find(i => i.type === "all").count)
|
||||
</script>
|
||||
|
||||
{#if $tagStabilized !== $tag}
|
||||
<Loading/>
|
||||
{:else if $tagInfoStats }
|
||||
<a href={$tagInfoUrl} target="_blank" class={twMerge(($total < 250) ? "alert" : "thanks", "w-fit link-underline")}>
|
||||
{$total} features on OSM have this tag
|
||||
</a>
|
||||
{/if}
|
98
UI/Studio/TagInput/BasicTagInput.svelte
Normal file
98
UI/Studio/TagInput/BasicTagInput.svelte
Normal file
|
@ -0,0 +1,98 @@
|
|||
<script lang="ts">
|
||||
|
||||
import ValidatedInput from "../../InputElement/ValidatedInput.svelte";
|
||||
import {UIEventSource} from "../../../Logic/UIEventSource";
|
||||
import {onDestroy} from "svelte";
|
||||
import Tr from "../../Base/Tr.svelte";
|
||||
import {TagUtils} from "../../../Logic/Tags/TagUtils";
|
||||
import TagInfoStats from "../TagInfoStats.svelte";
|
||||
|
||||
export let tag: UIEventSource<string> = new UIEventSource<string>(undefined)
|
||||
export let uploadableOnly: boolean
|
||||
export let overpassSupportNeeded: boolean
|
||||
tag.addCallback(tag => console.log("Current tag is", tag))
|
||||
|
||||
console.log({uploadableOnly, overpassSupportNeeded})
|
||||
|
||||
|
||||
let feedbackGlobal = tag.map(tag => {
|
||||
if (!tag) {
|
||||
return undefined
|
||||
}
|
||||
try {
|
||||
TagUtils.Tag(tag)
|
||||
return undefined
|
||||
} catch (e) {
|
||||
return e
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
let feedbackKey = new UIEventSource<string>(undefined)
|
||||
let keyValue = new UIEventSource<string>(undefined)
|
||||
|
||||
|
||||
let feedbackValue = new UIEventSource<string>(undefined)
|
||||
/**
|
||||
* The value of the tag. The name is a bit confusing
|
||||
*/
|
||||
let valueValue = new UIEventSource<string>(undefined)
|
||||
|
||||
|
||||
let mode: string = "="
|
||||
let modes: string[] = []
|
||||
|
||||
for (const k in TagUtils.modeDocumentation) {
|
||||
const docs = TagUtils.modeDocumentation[k]
|
||||
if (overpassSupportNeeded && !docs.overpassSupport) {
|
||||
continue
|
||||
}
|
||||
if (uploadableOnly && !docs.uploadable) {
|
||||
continue
|
||||
}
|
||||
modes.push(k)
|
||||
}
|
||||
if (!uploadableOnly && !overpassSupportNeeded) {
|
||||
modes.push(...TagUtils.comparators.map(c => c[0]))
|
||||
}
|
||||
|
||||
onDestroy(valueValue.addCallbackAndRun(setTag))
|
||||
onDestroy(keyValue.addCallbackAndRun(setTag))
|
||||
|
||||
$: {
|
||||
setTag(mode)
|
||||
}
|
||||
|
||||
function setTag(_) {
|
||||
const k = keyValue.data
|
||||
const v = valueValue.data
|
||||
tag.setData(k + mode + v)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<div>
|
||||
|
||||
<ValidatedInput feedback={feedbackKey} placeholder="The key of the tag" type="key"
|
||||
value={keyValue}></ValidatedInput>
|
||||
<select bind:value={mode}>
|
||||
{#each modes as option}
|
||||
<option value={option}>
|
||||
{option}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
<ValidatedInput feedback={feedbackValue} placeholder="The value of the tag" type="string"
|
||||
value={valueValue}></ValidatedInput>
|
||||
|
||||
{#if $feedbackKey}
|
||||
<Tr cls="alert" t={$feedbackKey}/>
|
||||
{:else if $feedbackValue}
|
||||
<Tr cls="alert" t={$feedbackValue}/>
|
||||
{:else if $feedbackGlobal}
|
||||
<Tr cls="alert" t={$feedbackGlobal}/>
|
||||
{/if}
|
||||
|
||||
<TagInfoStats {tag}/>
|
||||
</div>
|
16
UI/Studio/TagInput/TagInput.svelte
Normal file
16
UI/Studio/TagInput/TagInput.svelte
Normal file
|
@ -0,0 +1,16 @@
|
|||
<script lang="ts">/**
|
||||
* An element input a tag; has `and`, `or`, `regex`, ...
|
||||
*/
|
||||
import type {TagConfigJson} from "../../../Models/ThemeConfig/Json/TagConfigJson";
|
||||
import {UIEventSource} from "../../../Logic/UIEventSource";
|
||||
import TagExpression from "../TagExpression.svelte";
|
||||
|
||||
|
||||
export let tag: UIEventSource<TagConfigJson>
|
||||
export let uploadableOnly: boolean
|
||||
export let overpassSupportNeeded: boolean
|
||||
</script>
|
||||
|
||||
<div class="m-4">
|
||||
<TagExpression {overpassSupportNeeded} {tag} {uploadableOnly}/>
|
||||
</div>
|
|
@ -73,7 +73,7 @@
|
|||
"properties": {
|
||||
"osmTags": {
|
||||
"$ref": "#/definitions/TagConfigJson",
|
||||
"description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer."
|
||||
"description": "question: Which tags must be present on the feature to show it in this layer?\n\n Every source must set which tags have to be present in order to load the given layer."
|
||||
},
|
||||
"maxCacheAge": {
|
||||
"description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat",
|
||||
|
|
|
@ -320,7 +320,7 @@
|
|||
"properties": {
|
||||
"osmTags": {
|
||||
"$ref": "#/definitions/TagConfigJson",
|
||||
"description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer."
|
||||
"description": "question: Which tags must be present on the feature to show it in this layer?\n\n Every source must set which tags have to be present in order to load the given layer."
|
||||
},
|
||||
"maxCacheAge": {
|
||||
"description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat",
|
||||
|
@ -33700,7 +33700,7 @@
|
|||
"properties": {
|
||||
"osmTags": {
|
||||
"$ref": "#/definitions/TagConfigJson",
|
||||
"description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer."
|
||||
"description": "question: Which tags must be present on the feature to show it in this layer?\n\n Every source must set which tags have to be present in order to load the given layer."
|
||||
},
|
||||
"maxCacheAge": {
|
||||
"description": "question: How long (in seconds) is the data allowed to remain cached until it must be refreshed?\nThe maximum amount of seconds that a tile is allowed to linger in the cache\n\ntype: nat",
|
||||
|
|
|
@ -1589,10 +1589,6 @@ video {
|
|||
border-width: 2px;
|
||||
}
|
||||
|
||||
.border-8 {
|
||||
border-width: 8px;
|
||||
}
|
||||
|
||||
.border-x {
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
|
@ -1611,6 +1607,10 @@ video {
|
|||
border-bottom-width: 2px;
|
||||
}
|
||||
|
||||
.border-l-4 {
|
||||
border-left-width: 4px;
|
||||
}
|
||||
|
||||
.border-t {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
|
|
@ -25,10 +25,12 @@ import DependencyCalculator from "../Models/ThemeConfig/DependencyCalculator"
|
|||
import { AllSharedLayers } from "../Customizations/AllSharedLayers"
|
||||
import ThemeViewState from "../Models/ThemeViewState"
|
||||
import Validators from "../UI/InputElement/Validators"
|
||||
import { TagUtils } from "../Logic/Tags/TagUtils"
|
||||
import { Utils } from "../Utils"
|
||||
|
||||
function WriteFile(
|
||||
filename,
|
||||
html: BaseUIElement,
|
||||
html: string | BaseUIElement,
|
||||
autogenSource: string[],
|
||||
options?: {
|
||||
noTableOfContents: boolean
|
||||
|
@ -106,7 +108,7 @@ function GenerateDocumentationForTheme(theme: LayoutConfig): BaseUIElement {
|
|||
function GenLayerOverviewText(): BaseUIElement {
|
||||
for (const id of Constants.priviliged_layers) {
|
||||
if (!AllSharedLayers.sharedLayers.has(id)) {
|
||||
throw "Priviliged layer definition not found: " + id
|
||||
console.error("Priviliged layer definition not found: " + id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,17 +151,17 @@ function GenLayerOverviewText(): BaseUIElement {
|
|||
"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.",
|
||||
new Title("Priviliged layers", 1),
|
||||
new List(Constants.priviliged_layers.map((id) => "[" + id + "](#" + id + ")")),
|
||||
...Constants.priviliged_layers
|
||||
.map((id) => AllSharedLayers.sharedLayers.get(id))
|
||||
.map((l) =>
|
||||
l.GenerateDocumentation(
|
||||
themesPerLayer.get(l.id),
|
||||
layerIsNeededBy,
|
||||
DependencyCalculator.getLayerDependencies(l),
|
||||
Constants.added_by_default.indexOf(<any>l.id) >= 0,
|
||||
Constants.no_include.indexOf(<any>l.id) < 0
|
||||
)
|
||||
),
|
||||
...Utils.NoNull(
|
||||
Constants.priviliged_layers.map((id) => AllSharedLayers.sharedLayers.get(id))
|
||||
).map((l) =>
|
||||
l.GenerateDocumentation(
|
||||
themesPerLayer.get(l.id),
|
||||
layerIsNeededBy,
|
||||
DependencyCalculator.getLayerDependencies(l),
|
||||
Constants.added_by_default.indexOf(<any>l.id) >= 0,
|
||||
Constants.no_include.indexOf(<any>l.id) < 0
|
||||
)
|
||||
),
|
||||
new Title("Normal layers", 1),
|
||||
"The following layers are included in MapComplete:",
|
||||
new List(
|
||||
|
@ -350,6 +352,7 @@ WriteFile("./Docs/BuiltinQuestions.md", SharedTagRenderings.HelpText(), [
|
|||
"Customizations/SharedTagRenderings.ts",
|
||||
"assets/tagRenderings/questions.json",
|
||||
])
|
||||
WriteFile("./Docs/Tags_format.md", TagUtils.generateDocs(), ["Logic/Tags/TagUtils.ts"])
|
||||
|
||||
{
|
||||
// Generate the builtinIndex which shows interlayer dependencies
|
||||
|
|
10
test.ts
10
test.ts
|
@ -3,13 +3,12 @@ import * as theme from "./assets/generated/themes/bookcases.json"
|
|||
import ThemeViewState from "./Models/ThemeViewState"
|
||||
import Combine from "./UI/Base/Combine"
|
||||
import SpecialVisualizations from "./UI/SpecialVisualizations"
|
||||
import ValidatedInput from "./UI/InputElement/ValidatedInput.svelte"
|
||||
import SvelteUIElement from "./UI/Base/SvelteUIElement"
|
||||
import TagInput from "./UI/Studio/TagInput/TagInput.svelte"
|
||||
import { UIEventSource } from "./Logic/UIEventSource"
|
||||
import { Unit } from "./Models/Unit"
|
||||
import { Denomination } from "./Models/Denomination"
|
||||
import { TagsFilter } from "./Logic/Tags/TagsFilter"
|
||||
import { VariableUiElement } from "./UI/Base/VariableUIElement"
|
||||
import { FixedUiElement } from "./UI/Base/FixedUiElement"
|
||||
import { TagConfigJson } from "./Models/ThemeConfig/Json/TagConfigJson"
|
||||
|
||||
function testspecial() {
|
||||
const layout = new LayoutConfig(<any>theme, true) // qp.data === "" ? : new AllKnownLayoutsLazy().get(qp.data)
|
||||
|
@ -21,6 +20,9 @@ function testspecial() {
|
|||
new Combine(all).AttachTo("maindiv")
|
||||
}
|
||||
|
||||
const tag = new UIEventSource<TagConfigJson>(undefined)
|
||||
new SvelteUIElement(TagInput, { tag }).AttachTo("maindiv")
|
||||
new VariableUiElement(tag.map((t) => JSON.stringify(t))).AttachTo("extradiv")
|
||||
/*/
|
||||
testspecial()
|
||||
//*/
|
||||
|
|
Loading…
Reference in a new issue