forked from MapComplete/MapComplete
		
	Add HTML rendering options to icons
This commit is contained in:
		
							parent
							
								
									b3f02572d5
								
							
						
					
					
						commit
						4d5c250f8f
					
				
					 5 changed files with 77 additions and 34 deletions
				
			
		| 
						 | 
				
			
			@ -17,6 +17,7 @@ import {SubstitutedTranslation} from "../../UI/SubstitutedTranslation";
 | 
			
		|||
import SourceConfig from "./SourceConfig";
 | 
			
		||||
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
 | 
			
		||||
import {Tag} from "../../Logic/Tags/Tag";
 | 
			
		||||
import SubstitutingTag from "../../Logic/Tags/SubstitutingTag";
 | 
			
		||||
 | 
			
		||||
export default class LayerConfig {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -218,11 +219,35 @@ export default class LayerConfig {
 | 
			
		|||
        this.dashArray = tr("dashArray", "");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if(json["showIf"] !== undefined){
 | 
			
		||||
            throw "Invalid key on layerconfig "+this.id+": showIf. Did you mean 'isShown' instead?";
 | 
			
		||||
        if (json["showIf"] !== undefined) {
 | 
			
		||||
            throw "Invalid key on layerconfig " + this.id + ": showIf. Did you mean 'isShown' instead?";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Splits the parts of the icon, at ";" but makes sure that everything between "<html>" and "</html>" stays together
 | 
			
		||||
     * @param template
 | 
			
		||||
     * @constructor
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    private static SplitParts(template: string): string[] {
 | 
			
		||||
        const htmlParts = template.split("<html>");
 | 
			
		||||
        const parts = []
 | 
			
		||||
        for (const htmlPart of htmlParts) {
 | 
			
		||||
            if (htmlPart.indexOf("</html>") >= 0) {
 | 
			
		||||
                const subparts = htmlPart.split("</html>");
 | 
			
		||||
                if (subparts.length != 2) {
 | 
			
		||||
                    throw "Invalid rendering with embedded html: " + htmlPart;
 | 
			
		||||
                }
 | 
			
		||||
                parts.push("html:" + subparts[0]);
 | 
			
		||||
                parts.push(...subparts[1].split(";"))
 | 
			
		||||
            } else {
 | 
			
		||||
                parts.push(...htmlPart.split(";"))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return parts.filter(prt => prt != "");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CustomCodeSnippets(): string[] {
 | 
			
		||||
        if (this.calculatedTags === undefined) {
 | 
			
		||||
            return []
 | 
			
		||||
| 
						 | 
				
			
			@ -343,15 +368,16 @@ export default class LayerConfig {
 | 
			
		|||
        const iconUrlStatic = render(this.icon);
 | 
			
		||||
        const self = this;
 | 
			
		||||
        const mappedHtml = tags.map(tgs => {
 | 
			
		||||
            // What do you mean, 'tgs' is never read?
 | 
			
		||||
            // It is read implicitly in the 'render' method
 | 
			
		||||
            const iconUrl = render(self.icon);
 | 
			
		||||
            const rotation = render(self.rotation, "0deg");
 | 
			
		||||
 | 
			
		||||
            let htmlParts: UIElement[] = [];
 | 
			
		||||
            let sourceParts = iconUrl.split(";");
 | 
			
		||||
 | 
			
		||||
            function genHtmlFromString(sourcePart: string): UIElement {
 | 
			
		||||
                console.log("Got source part ", sourcePart)
 | 
			
		||||
                if (sourcePart.indexOf("html:") == 0) {
 | 
			
		||||
                    // We use § as a replacement for ;
 | 
			
		||||
                    const html = sourcePart.substring("html:".length)
 | 
			
		||||
                    const inner = new FixedUiElement(SubstitutingTag.substituteString(html, tgs)).SetClass("block w-min text-center")
 | 
			
		||||
                    const outer = new Combine([inner]).SetClass("flex flex-col items-center")
 | 
			
		||||
                    return outer;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`;
 | 
			
		||||
                let html: UIElement = new FixedUiElement(`<img src="${sourcePart}" style="${style}" />`);
 | 
			
		||||
                const match = sourcePart.match(/([a-zA-Z0-9_]*):([^;]*)/)
 | 
			
		||||
| 
						 | 
				
			
			@ -365,6 +391,14 @@ export default class LayerConfig {
 | 
			
		|||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // What do you mean, 'tgs' is never read?
 | 
			
		||||
            // It is read implicitly in the 'render' method
 | 
			
		||||
            const iconUrl = render(self.icon);
 | 
			
		||||
            const rotation = render(self.rotation, "0deg");
 | 
			
		||||
 | 
			
		||||
            let htmlParts: UIElement[] = [];
 | 
			
		||||
            let sourceParts = LayerConfig.SplitParts(iconUrl);
 | 
			
		||||
 | 
			
		||||
            for (const sourcePart of sourceParts) {
 | 
			
		||||
                htmlParts.push(genHtmlFromString(sourcePart))
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -377,7 +411,7 @@ export default class LayerConfig {
 | 
			
		|||
                }
 | 
			
		||||
                if (iconOverlay.badge) {
 | 
			
		||||
                    const badgeParts: UIElement[] = [];
 | 
			
		||||
                    const partDefs = iconOverlay.then.GetRenderValue(tgs).txt.split(";");
 | 
			
		||||
                    const partDefs = LayerConfig.SplitParts(iconOverlay.then.GetRenderValue(tgs).txt);
 | 
			
		||||
 | 
			
		||||
                    for (const badgePartStr of partDefs) {
 | 
			
		||||
                        badgeParts.push(genHtmlFromString(badgePartStr))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -105,6 +105,9 @@ export interface LayerConfigJson {
 | 
			
		|||
     * As a result, on could use a generic pin, then overlay it with a specific icon.
 | 
			
		||||
     * To make things even more practical, one can use all svgs from the folder "assets/svg" and _substitute the color_ in it.
 | 
			
		||||
     * E.g. to draw a red pin, use "pin:#f00", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`
 | 
			
		||||
     * 
 | 
			
		||||
     * Also note that one can specify to use HTML by entering some html between "<html>" and "</html" into here, for example
 | 
			
		||||
     * "icon": "some_icon.svg;<html><div style="margin-top: 50px; background: white; display: block">{name}</div>"
 | 
			
		||||
     */
 | 
			
		||||
    icon?: string | TagRenderingConfigJson;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,11 +18,11 @@ export default class SubstitutingTag implements TagsFilter {
 | 
			
		|||
        this._value = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static substituteString(template: string, dict: any): string {
 | 
			
		||||
    public static substituteString(template: string, dict: any): string {
 | 
			
		||||
        for (const k in dict) {
 | 
			
		||||
            template = template.replace(new RegExp("\\{" + k + "\\}", 'g'), dict[k])
 | 
			
		||||
        }
 | 
			
		||||
        return template;
 | 
			
		||||
        return template.replace(/{.*}/g, "");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    asHumanString(linkToWiki: boolean, shorten: boolean, properties) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,13 +34,6 @@ export class SubstitutedTranslation extends UIElement {
 | 
			
		|||
        this.SetClass("w-full")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static GenerateMap(){
 | 
			
		||||
        return new Map<UIEventSource<any>, SubstitutedTranslation>()
 | 
			
		||||
    }
 | 
			
		||||
    private static GenerateSubCache(){
 | 
			
		||||
        return new  Map<Translation, Map<UIEventSource<any>, SubstitutedTranslation>>();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public static construct(
 | 
			
		||||
        translation: Translation,
 | 
			
		||||
        tags: UIEventSource<any>): SubstitutedTranslation {
 | 
			
		||||
| 
						 | 
				
			
			@ -59,14 +52,21 @@ export class SubstitutedTranslation extends UIElement {
 | 
			
		|||
 | 
			
		||||
    public static SubstituteKeys(txt: string, tags: any) {
 | 
			
		||||
        for (const key in tags) {
 | 
			
		||||
            // Poor mans replace all
 | 
			
		||||
            txt = txt.split("{" + key + "}").join(tags[key]);
 | 
			
		||||
            txt = txt.replace(new RegExp("{" + key + "}", "g"), tags[key])
 | 
			
		||||
        }
 | 
			
		||||
        return txt;
 | 
			
		||||
        return txt.replace(/{.*}/g, "");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static GenerateMap() {
 | 
			
		||||
        return new Map<UIEventSource<any>, SubstitutedTranslation>()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static GenerateSubCache() {
 | 
			
		||||
        return new Map<Translation, Map<UIEventSource<any>, SubstitutedTranslation>>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    InnerRender(): string {
 | 
			
		||||
        if(this.content.length == 1){
 | 
			
		||||
        if (this.content.length == 1) {
 | 
			
		||||
            return this.content[0].Render();
 | 
			
		||||
        }
 | 
			
		||||
        return new Combine(this.content).Render();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,7 +37,13 @@
 | 
			
		|||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "icon": {
 | 
			
		||||
    "render": "./assets/themes/bookcases/bookcase.svg"
 | 
			
		||||
    "render": "./assets/themes/bookcases/bookcase.svg;",
 | 
			
		||||
    "mappings": [
 | 
			
		||||
      {
 | 
			
		||||
        "if": "name~*",
 | 
			
		||||
        "then": "./assets/themes/bookcases/bookcase.svg;<html><div style='margin-top: 42px; background: white; padding: 0.25em; border-radius:0.5em'>{name}</div></html>"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "color": {
 | 
			
		||||
    "render": "#0000ff"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue