Add further support for special UI-elements; add documentation, fix a few bugs
This commit is contained in:
parent
3ab3cef249
commit
07e611bf10
12 changed files with 113 additions and 55 deletions
|
@ -145,7 +145,6 @@ export class FromJSON {
|
||||||
json = "{image_carousel()}{image_upload()}";
|
json = "{image_carousel()}{image_upload()}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.warn("Possible literal rendering:", json)
|
|
||||||
|
|
||||||
return new TagRenderingOptions({
|
return new TagRenderingOptions({
|
||||||
freeform: {
|
freeform: {
|
||||||
|
|
|
@ -17,6 +17,7 @@ export class ElementStorage {
|
||||||
|
|
||||||
addElement(element): UIEventSource<any> {
|
addElement(element): UIEventSource<any> {
|
||||||
const eventSource = new UIEventSource<any>(element.properties);
|
const eventSource = new UIEventSource<any>(element.properties);
|
||||||
|
console.log("Creating a new tag storate for ", element.properties.id)
|
||||||
this._elements[element.properties.id] = eventSource;
|
this._elements[element.properties.id] = eventSource;
|
||||||
return eventSource;
|
return eventSource;
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,10 @@ export class FilteredLayer {
|
||||||
|
|
||||||
if (this.filters.matches(tags)) {
|
if (this.filters.matches(tags)) {
|
||||||
const centerPoint = GeoOperations.centerpoint(feature);
|
const centerPoint = GeoOperations.centerpoint(feature);
|
||||||
feature.properties["_surface"] = "" + GeoOperations.surfaceAreaInSqMeters(feature);
|
const sqMeters = GeoOperations.surfaceAreaInSqMeters(feature);
|
||||||
|
feature.properties["_surface"] = "" + sqMeters;
|
||||||
|
feature.properties["_surface:ha"] = "" + Math.floor(sqMeters / 1000)/10;
|
||||||
|
|
||||||
const lat = centerPoint.geometry.coordinates[1];
|
const lat = centerPoint.geometry.coordinates[1];
|
||||||
const lon = centerPoint.geometry.coordinates[0]
|
const lon = centerPoint.geometry.coordinates[0]
|
||||||
feature.properties["_lon"] = "" + lat; // We expect a string here for lat/lon
|
feature.properties["_lon"] = "" + lat; // We expect a string here for lat/lon
|
||||||
|
@ -252,7 +255,7 @@ export class FilteredLayer {
|
||||||
const popup = L.popup({}, marker);
|
const popup = L.popup({}, marker);
|
||||||
let uiElement: UIElement;
|
let uiElement: UIElement;
|
||||||
let content = undefined;
|
let content = undefined;
|
||||||
let p = marker.bindPopup(popup)
|
let p = marker.bindPopup(popup)
|
||||||
.on("popupopen", () => {
|
.on("popupopen", () => {
|
||||||
if (content === undefined) {
|
if (content === undefined) {
|
||||||
uiElement = self._showOnPopup(eventSource, feature);
|
uiElement = self._showOnPopup(eventSource, feature);
|
||||||
|
|
|
@ -28,7 +28,7 @@ export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> {
|
||||||
private readonly _commons = new UIEventSource<string>("");
|
private readonly _commons = new UIEventSource<string>("");
|
||||||
|
|
||||||
|
|
||||||
constructor(tags: UIEventSource<any>) {
|
constructor(tags: UIEventSource<any>, imagePrefix = "image", loadSpecial = true) {
|
||||||
super([]);
|
super([]);
|
||||||
|
|
||||||
this._tags = tags;
|
this._tags = tags;
|
||||||
|
@ -40,17 +40,17 @@ export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> {
|
||||||
this._commons.addCallback(() => self.LoadCommons());
|
this._commons.addCallback(() => self.LoadCommons());
|
||||||
|
|
||||||
|
|
||||||
this._tags.addCallbackAndRun(() => self.LoadImages());
|
this._tags.addCallbackAndRun(() => self.LoadImages(imagePrefix, loadSpecial));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AddImage(key: string, url: string) {
|
private AddImage(key: string, url: string) {
|
||||||
if (url === undefined || url === null || url === "") {
|
if (url === undefined || url === null || url === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const el of this.data) {
|
for (const el of this.data) {
|
||||||
if (el.url === url) {
|
if (el.url === url) {
|
||||||
|
// This url is already seen -> don't add it
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,17 +102,18 @@ export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoadImages(imagePrefix: string = "image", loadAdditional = true): void {
|
private LoadImages(imagePrefix: string, loadAdditional: boolean): void {
|
||||||
const imageTag = this._tags.data.image;
|
console.log("Loading images from",this._tags)
|
||||||
|
const imageTag = this._tags.data[imagePrefix];
|
||||||
if (imageTag !== undefined) {
|
if (imageTag !== undefined) {
|
||||||
const bareImages = imageTag.split(";");
|
const bareImages = imageTag.split(";");
|
||||||
for (const bareImage of bareImages) {
|
for (const bareImage of bareImages) {
|
||||||
this.AddImage("image", bareImage);
|
this.AddImage(imagePrefix, bareImage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key in this._tags.data) {
|
for (const key in this._tags.data) {
|
||||||
if (key.startsWith("image:")) {
|
if (key.startsWith(imagePrefix+":")) {
|
||||||
const url = this._tags.data[key]
|
const url = this._tags.data[key]
|
||||||
this.AddImage(key, url);
|
this.AddImage(key, url);
|
||||||
}
|
}
|
||||||
|
@ -130,7 +131,7 @@ export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._tags.data.mapillary) {
|
if (this._tags.data.mapillary) {
|
||||||
this.AddImage("mapillary", "https://www.mapillary.com/map/im/" + this._tags.data.mapillary)
|
this.AddImage(undefined,"https://www.mapillary.com/map/im/" + this._tags.data.mapillary)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ export class Changes {
|
||||||
if(pending.length === 0){
|
if(pending.length === 0){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log("Sending ping",eventSource)
|
||||||
eventSource.ping();
|
eventSource.ping();
|
||||||
this.uploadAll([], pending);
|
this.uploadAll([], pending);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import State from "../../State";
|
||||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
import {FromJSON} from "../../Customizations/JSON/FromJSON";
|
import {FromJSON} from "../../Customizations/JSON/FromJSON";
|
||||||
import ValidatedTextField from "../Input/ValidatedTextField";
|
import ValidatedTextField from "../Input/ValidatedTextField";
|
||||||
|
import SpecialVisualizations from "../SpecialVisualizations";
|
||||||
|
|
||||||
export default class TagRenderingPanel extends InputElement<TagRenderingConfigJson> {
|
export default class TagRenderingPanel extends InputElement<TagRenderingConfigJson> {
|
||||||
|
|
||||||
|
@ -83,7 +84,10 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
|
||||||
const settings: (string | SingleSetting<any>)[] = [
|
const settings: (string | SingleSetting<any>)[] = [
|
||||||
setting(
|
setting(
|
||||||
options?.noLanguage ? new TextField({placeholder:"Rendering"}) :
|
options?.noLanguage ? new TextField({placeholder:"Rendering"}) :
|
||||||
new MultiLingualTextFields(languages), "render", "Value to show", " Renders this value. Note that <span class='literal-code'>{key}</span>-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."),
|
new MultiLingualTextFields(languages), "render", "Value to show",
|
||||||
|
"Renders this value. Note that <span class='literal-code'>{key}</span>-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." +
|
||||||
|
"<br/><br/>" +
|
||||||
|
"Furhtermore, some special functions are supported:"+SpecialVisualizations.HelpMessage.Render()),
|
||||||
|
|
||||||
questionsNotUnlocked ? `You need at least ${State.userJourney.themeGeneratorFullUnlock} changesets to unlock the 'question'-field and to use your theme to edit OSM data` : "",
|
questionsNotUnlocked ? `You need at least ${State.userJourney.themeGeneratorFullUnlock} changesets to unlock the 'question'-field and to use your theme to edit OSM data` : "",
|
||||||
...(options?.disableQuestions ? [] : questionSettings),
|
...(options?.disableQuestions ? [] : questionSettings),
|
||||||
|
|
|
@ -11,9 +11,9 @@ export class ImageCarousel extends TagDependantUIElement {
|
||||||
|
|
||||||
public readonly slideshow: SlideShow;
|
public readonly slideshow: SlideShow;
|
||||||
|
|
||||||
constructor(tags: UIEventSource<any>) {
|
constructor(tags: UIEventSource<any>, imagePrefix: string = "image", loadSpecial: boolean =true) {
|
||||||
super(tags);
|
super(tags);
|
||||||
const searcher : UIEventSource<{url:string}[]> = new ImageSearcher(tags);
|
const searcher : UIEventSource<{url:string}[]> = new ImageSearcher(tags, imagePrefix, loadSpecial);
|
||||||
const uiElements = searcher.map((imageURLS: {key: string, url:string}[]) => {
|
const uiElements = searcher.map((imageURLS: {key: string, url:string}[]) => {
|
||||||
const uiElements: UIElement[] = [];
|
const uiElements: UIElement[] = [];
|
||||||
for (const url of imageURLS) {
|
for (const url of imageURLS) {
|
||||||
|
|
|
@ -17,10 +17,12 @@ export class ImageUploadFlow extends UIElement {
|
||||||
private readonly _didFail: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
private readonly _didFail: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||||
private readonly _allDone: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
private readonly _allDone: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||||
private readonly _connectButton: UIElement;
|
private readonly _connectButton: UIElement;
|
||||||
|
private readonly _imagePrefix: string;
|
||||||
|
|
||||||
constructor(tags: UIEventSource<any>) {
|
constructor(tags: UIEventSource<any>, imagePrefix: string = "image") {
|
||||||
super(State.state.osmConnection.userDetails);
|
super(State.state.osmConnection.userDetails);
|
||||||
this._tags = tags;
|
this._tags = tags;
|
||||||
|
this._imagePrefix = imagePrefix;
|
||||||
|
|
||||||
this.ListenTo(this._isUploading);
|
this.ListenTo(this._isUploading);
|
||||||
this.ListenTo(this._didFail);
|
this.ListenTo(this._didFail);
|
||||||
|
@ -131,20 +133,21 @@ export class ImageUploadFlow extends UIElement {
|
||||||
|
|
||||||
private handleSuccessfulUpload(url) {
|
private handleSuccessfulUpload(url) {
|
||||||
const tags = this._tags.data;
|
const tags = this._tags.data;
|
||||||
let key = "image";
|
let key = this._imagePrefix;
|
||||||
if (tags["image"] !== undefined) {
|
if (tags[this._imagePrefix] !== undefined) {
|
||||||
|
|
||||||
let freeIndex = 0;
|
let freeIndex = 0;
|
||||||
while (tags["image:" + freeIndex] !== undefined) {
|
while (tags[this._imagePrefix + ":" + freeIndex] !== undefined) {
|
||||||
freeIndex++;
|
freeIndex++;
|
||||||
}
|
}
|
||||||
key = "image:" + freeIndex;
|
key = this._imagePrefix + ":" + freeIndex;
|
||||||
}
|
}
|
||||||
console.log("Adding image:" + key, url);
|
console.log("Adding image:" + key, url);
|
||||||
State.state.changes.addTag(tags.id, new Tag(key, url));
|
State.state.changes.addTag(tags.id, new Tag(key, url));
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleFiles(files) {
|
private handleFiles(files) {
|
||||||
|
console.log("Received images from the user, starting upload")
|
||||||
this._isUploading.setData(files.length);
|
this._isUploading.setData(files.length);
|
||||||
this._allDone.setData(false);
|
this._allDone.setData(false);
|
||||||
|
|
||||||
|
@ -189,7 +192,6 @@ export class ImageUploadFlow extends UIElement {
|
||||||
|
|
||||||
InnerUpdate(htmlElement: HTMLElement) {
|
InnerUpdate(htmlElement: HTMLElement) {
|
||||||
super.InnerUpdate(htmlElement);
|
super.InnerUpdate(htmlElement);
|
||||||
const user = State.state.osmConnection.userDetails.data;
|
|
||||||
|
|
||||||
this._licensePicker.Update()
|
this._licensePicker.Update()
|
||||||
const form = document.getElementById('fileselector-form-' + this.id) as HTMLFormElement
|
const form = document.getElementById('fileselector-form-' + this.id) as HTMLFormElement
|
||||||
|
@ -197,8 +199,7 @@ export class ImageUploadFlow extends UIElement {
|
||||||
const self = this
|
const self = this
|
||||||
|
|
||||||
function submitHandler() {
|
function submitHandler() {
|
||||||
const files = $(selector).prop('files');
|
self.handleFiles($(selector).prop('files'))
|
||||||
self.handleFiles(files)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selector != null && form != null) {
|
if (selector != null && form != null) {
|
||||||
|
@ -206,8 +207,6 @@ export class ImageUploadFlow extends UIElement {
|
||||||
submitHandler()
|
submitHandler()
|
||||||
}
|
}
|
||||||
form.addEventListener('submit', e => {
|
form.addEventListener('submit', e => {
|
||||||
console.log(e)
|
|
||||||
alert('wait')
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
submitHandler()
|
submitHandler()
|
||||||
})
|
})
|
||||||
|
|
|
@ -81,7 +81,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
this.currentTags = this._source.map(tags => {
|
this.currentTags = tags.map(tags => {
|
||||||
|
|
||||||
if (options.tagsPreprocessor === undefined) {
|
if (options.tagsPreprocessor === undefined) {
|
||||||
return tags;
|
return tags;
|
||||||
|
@ -96,6 +96,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
||||||
return newTags;
|
return newTags;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
tags.addCallback(() => self.currentTags.ping());
|
||||||
|
|
||||||
if (options.question !== undefined) {
|
if (options.question !== undefined) {
|
||||||
this._question = options.question;
|
this._question = options.question;
|
||||||
|
@ -516,7 +517,10 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
||||||
this._editButton.Update();
|
this._editButton.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
private answerCache = {}
|
private readonly answerCache = {}
|
||||||
|
// Makes sure that the elements receive updates
|
||||||
|
// noinspection JSMismatchedCollectionQueryUpdate
|
||||||
|
private readonly substitutedElements : UIElement[]= [];
|
||||||
|
|
||||||
private ApplyTemplate(template: string | Translation): UIElement {
|
private ApplyTemplate(template: string | Translation): UIElement {
|
||||||
const tr = Translations.WT(template);
|
const tr = Translations.WT(template);
|
||||||
|
@ -526,6 +530,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
||||||
// We have to cache these elemnts, otherwise it is to slow
|
// We have to cache these elemnts, otherwise it is to slow
|
||||||
const el = new SubstitutedTranslation(tr, this.currentTags);
|
const el = new SubstitutedTranslation(tr, this.currentTags);
|
||||||
this.answerCache[tr.id] = el;
|
this.answerCache[tr.id] = el;
|
||||||
|
this.substitutedElements.push(el);
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {ImageUploadFlow} from "./Image/ImageUploadFlow";
|
||||||
export class SubstitutedTranslation extends UIElement {
|
export class SubstitutedTranslation extends UIElement {
|
||||||
private readonly tags: UIEventSource<any>;
|
private readonly tags: UIEventSource<any>;
|
||||||
private readonly translation: Translation;
|
private readonly translation: Translation;
|
||||||
private content: UIElement;
|
private content: UIElement[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
translation: Translation,
|
translation: Translation,
|
||||||
|
@ -25,18 +25,19 @@ export class SubstitutedTranslation extends UIElement {
|
||||||
Locale.language.addCallbackAndRun(() => {
|
Locale.language.addCallbackAndRun(() => {
|
||||||
self.content = self.CreateContent();
|
self.content = self.CreateContent();
|
||||||
self.Update();
|
self.Update();
|
||||||
})
|
});
|
||||||
|
this.dumbMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
InnerRender(): string {
|
InnerRender(): string {
|
||||||
return this.content.Render();
|
return new Combine(this.content).Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CreateContent(): UIElement {
|
private CreateContent(): UIElement[] {
|
||||||
let txt = this.translation?.txt;
|
let txt = this.translation?.txt;
|
||||||
if (txt === undefined) {
|
if (txt === undefined) {
|
||||||
return new FixedUiElement("")
|
return []
|
||||||
}
|
}
|
||||||
const tags = this.tags.data;
|
const tags = this.tags.data;
|
||||||
for (const key in tags) {
|
for (const key in tags) {
|
||||||
|
@ -44,11 +45,10 @@ export class SubstitutedTranslation extends UIElement {
|
||||||
txt = txt.split("{" + key + "}").join(tags[key]);
|
txt = txt.split("{" + key + "}").join(tags[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.EvaluateSpecialComponents(txt);
|
||||||
return new Combine(this.EvaluateSpecialComponents(txt));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EvaluateSpecialComponents(template: string): UIElement[] {
|
private EvaluateSpecialComponents(template: string): UIElement[] {
|
||||||
|
|
||||||
for (const knownSpecial of SpecialVisualizations.specialVisualizations) {
|
for (const knownSpecial of SpecialVisualizations.specialVisualizations) {
|
||||||
|
|
||||||
|
@ -58,10 +58,22 @@ export class SubstitutedTranslation extends UIElement {
|
||||||
|
|
||||||
// We found a special component that should be brought to live
|
// We found a special component that should be brought to live
|
||||||
const partBefore = this.EvaluateSpecialComponents(matched[1]);
|
const partBefore = this.EvaluateSpecialComponents(matched[1]);
|
||||||
const argument = matched[2];
|
const argument = matched[2].trim();
|
||||||
const partAfter = this.EvaluateSpecialComponents(matched[3]);
|
const partAfter = this.EvaluateSpecialComponents(matched[3]);
|
||||||
try {
|
try {
|
||||||
const args = argument.trim().split(",").map(str => str.trim());
|
const args = knownSpecial.args.map(arg => arg.defaultValue ?? "");
|
||||||
|
if (argument.length > 0) {
|
||||||
|
const realArgs = argument.split(",").map(str => str.trim());
|
||||||
|
for (let i = 0; i < realArgs.length; i++) {
|
||||||
|
if (args.length <= i) {
|
||||||
|
args.push(realArgs[i]);
|
||||||
|
} else {
|
||||||
|
args[i] = realArgs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const element = knownSpecial.constr(this.tags, args);
|
const element = knownSpecial.constr(this.tags, args);
|
||||||
return [...partBefore, element, ...partAfter]
|
return [...partBefore, element, ...partAfter]
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -83,6 +95,7 @@ export default class SpecialVisualizations {
|
||||||
funcName: string,
|
funcName: string,
|
||||||
constr: ((tagSource: UIEventSource<any>, argument: string[]) => UIElement),
|
constr: ((tagSource: UIEventSource<any>, argument: string[]) => UIElement),
|
||||||
docs: string,
|
docs: string,
|
||||||
|
example?: string,
|
||||||
args: { name: string, defaultValue?: string, doc: string }[]
|
args: { name: string, defaultValue?: string, doc: string }[]
|
||||||
}[] =
|
}[] =
|
||||||
|
|
||||||
|
@ -91,16 +104,17 @@ export default class SpecialVisualizations {
|
||||||
funcName: "image_carousel",
|
funcName: "image_carousel",
|
||||||
docs: "Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links)",
|
docs: "Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links)",
|
||||||
args: [{
|
args: [{
|
||||||
name: "image tag(s)",
|
name: "image key/prefix",
|
||||||
defaultValue: "image,image:*,wikidata,wikipedia,wikimedia_commons",
|
defaultValue: "image",
|
||||||
doc: "Image tag(s) where images are searched"
|
doc: "The keys given to the images, e.g. if <span class='literal-code'>image</span> is given, the first picture URL will be added as <span class='literal-code'>image</span>, the second as <span class='literal-code'>image:0</span>, the third as <span class='literal-code'>image:1</span>, etc... "
|
||||||
}],
|
},
|
||||||
|
{
|
||||||
|
name: "smart search",
|
||||||
|
defaultValue: "true",
|
||||||
|
doc: "Also include images given via 'Wikidata', 'wikimedia_commons' and 'mapillary"
|
||||||
|
}],
|
||||||
constr: (tags, args) => {
|
constr: (tags, args) => {
|
||||||
if (args.length > 0) {
|
return new ImageCarousel(tags, args[0], args[1].toLowerCase() === "true");
|
||||||
console.error("TODO HANDLE THESE ARGS") // TODO FIXME
|
|
||||||
|
|
||||||
}
|
|
||||||
return new ImageCarousel(tags);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -108,15 +122,12 @@ export default class SpecialVisualizations {
|
||||||
funcName: "image_upload",
|
funcName: "image_upload",
|
||||||
docs: "Creates a button where a user can upload an image to IMGUR",
|
docs: "Creates a button where a user can upload an image to IMGUR",
|
||||||
args: [{
|
args: [{
|
||||||
|
name: "image-key",
|
||||||
doc: "Image tag to add the URL to (or image-tag:0, image-tag:1 when multiple images are added)",
|
doc: "Image tag to add the URL to (or image-tag:0, image-tag:1 when multiple images are added)",
|
||||||
defaultValue: "image", name: "image-key"
|
defaultValue: "image"
|
||||||
}],
|
}],
|
||||||
constr: (tags, args) => {
|
constr: (tags, args) => {
|
||||||
if (args.length > 0) {
|
return new ImageUploadFlow(tags, args[0])
|
||||||
console.error("TODO HANDLE THESE ARGS") // TODO FIXME
|
|
||||||
|
|
||||||
}
|
|
||||||
return new ImageUploadFlow(tags)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -125,7 +136,7 @@ export default class SpecialVisualizations {
|
||||||
args: [{
|
args: [{
|
||||||
name: "key",
|
name: "key",
|
||||||
defaultValue: "opening_hours",
|
defaultValue: "opening_hours",
|
||||||
doc: "The tag from which the table is constructed"
|
doc: "The tagkey from which the table is constructed."
|
||||||
}],
|
}],
|
||||||
constr: (tagSource: UIEventSource<any>, args) => {
|
constr: (tagSource: UIEventSource<any>, args) => {
|
||||||
let keyname = args[0];
|
let keyname = args[0];
|
||||||
|
@ -139,6 +150,7 @@ export default class SpecialVisualizations {
|
||||||
{
|
{
|
||||||
funcName: "live",
|
funcName: "live",
|
||||||
docs: "Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)}",
|
docs: "Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)}",
|
||||||
|
example: "{live({url},{url:format},hour)} {live(https://data.mobility.brussels/bike/api/counts/?request=live&featureID=CB2105,hour:data.hour_cnt;day:data.day_cnt;year:data.year_cnt,hour)}",
|
||||||
args: [{
|
args: [{
|
||||||
name: "Url", doc: "The URL to load"
|
name: "Url", doc: "The URL to load"
|
||||||
}, {
|
}, {
|
||||||
|
@ -157,5 +169,38 @@ export default class SpecialVisualizations {
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
|
static HelpMessage: UIElement = SpecialVisualizations.GenHelpMessage();
|
||||||
|
|
||||||
|
private static GenHelpMessage() {
|
||||||
|
|
||||||
|
const helpTexts =
|
||||||
|
SpecialVisualizations.specialVisualizations.map(viz => new Combine(
|
||||||
|
[
|
||||||
|
`<h3>${viz.funcName}</h3>`,
|
||||||
|
viz.docs,
|
||||||
|
"<ol>",
|
||||||
|
...viz.args.map(arg => new Combine([
|
||||||
|
"<li>",
|
||||||
|
"<b>" + arg.name + "</b>: ",
|
||||||
|
arg.doc,
|
||||||
|
arg.defaultValue === undefined ? "" : (" Default: <span class='literal-code'>" + arg.defaultValue + "</span>"),
|
||||||
|
"</li>"
|
||||||
|
])),
|
||||||
|
"</ol>",
|
||||||
|
"<b>Example usage: </b>",
|
||||||
|
new FixedUiElement(
|
||||||
|
viz.example ?? "{" + viz.funcName + "(" + viz.args.map(arg => arg.defaultValue).join(",") + ")}"
|
||||||
|
).SetClass("literal-code"),
|
||||||
|
|
||||||
|
]
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
return new Combine([
|
||||||
|
"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.",
|
||||||
|
...helpTexts
|
||||||
|
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
File diff suppressed because one or more lines are too long
2
test.ts
2
test.ts
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import SpecialVisualizations from "./UI/SpecialVisualizations";
|
import SpecialVisualizations from "./UI/SpecialVisualizations";
|
||||||
|
|
||||||
SpecialVisualizations.HelpMessage.AttachTo("maindivgi")
|
SpecialVisualizations.HelpMessage.AttachTo("maindiv")
|
||||||
|
|
||||||
|
|
||||||
/*/
|
/*/
|
||||||
|
|
Loading…
Reference in a new issue