Add HTML rendering options to icons

This commit is contained in:
pietervdvn 2021-04-06 18:17:07 +02:00
parent b3f02572d5
commit 4d5c250f8f
5 changed files with 77 additions and 34 deletions

View file

@ -17,6 +17,7 @@ import {SubstitutedTranslation} from "../../UI/SubstitutedTranslation";
import SourceConfig from "./SourceConfig"; import SourceConfig from "./SourceConfig";
import {TagsFilter} from "../../Logic/Tags/TagsFilter"; import {TagsFilter} from "../../Logic/Tags/TagsFilter";
import {Tag} from "../../Logic/Tags/Tag"; import {Tag} from "../../Logic/Tags/Tag";
import SubstitutingTag from "../../Logic/Tags/SubstitutingTag";
export default class LayerConfig { export default class LayerConfig {
@ -223,6 +224,30 @@ export default class LayerConfig {
} }
} }
/**
* 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[] { public CustomCodeSnippets(): string[] {
if (this.calculatedTags === undefined) { if (this.calculatedTags === undefined) {
return [] return []
@ -343,15 +368,16 @@ export default class LayerConfig {
const iconUrlStatic = render(this.icon); const iconUrlStatic = render(this.icon);
const self = this; const self = this;
const mappedHtml = tags.map(tgs => { 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 { 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`; 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}" />`); let html: UIElement = new FixedUiElement(`<img src="${sourcePart}" style="${style}" />`);
const match = sourcePart.match(/([a-zA-Z0-9_]*):([^;]*)/) 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) { for (const sourcePart of sourceParts) {
htmlParts.push(genHtmlFromString(sourcePart)) htmlParts.push(genHtmlFromString(sourcePart))
} }
@ -377,7 +411,7 @@ export default class LayerConfig {
} }
if (iconOverlay.badge) { if (iconOverlay.badge) {
const badgeParts: UIElement[] = []; 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) { for (const badgePartStr of partDefs) {
badgeParts.push(genHtmlFromString(badgePartStr)) badgeParts.push(genHtmlFromString(badgePartStr))

View file

@ -105,6 +105,9 @@ export interface LayerConfigJson {
* As a result, on could use a generic pin, then overlay it with a specific icon. * 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. * 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>` * 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; icon?: string | TagRenderingConfigJson;

View file

@ -18,11 +18,11 @@ export default class SubstitutingTag implements TagsFilter {
this._value = value; this._value = value;
} }
private static substituteString(template: string, dict: any): string { public static substituteString(template: string, dict: any): string {
for (const k in dict) { for (const k in dict) {
template = template.replace(new RegExp("\\{" + k + "\\}", 'g'), dict[k]) template = template.replace(new RegExp("\\{" + k + "\\}", 'g'), dict[k])
} }
return template; return template.replace(/{.*}/g, "");
} }
asHumanString(linkToWiki: boolean, shorten: boolean, properties) { asHumanString(linkToWiki: boolean, shorten: boolean, properties) {

View file

@ -34,13 +34,6 @@ export class SubstitutedTranslation extends UIElement {
this.SetClass("w-full") 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( public static construct(
translation: Translation, translation: Translation,
tags: UIEventSource<any>): SubstitutedTranslation { tags: UIEventSource<any>): SubstitutedTranslation {
@ -59,10 +52,17 @@ export class SubstitutedTranslation extends UIElement {
public static SubstituteKeys(txt: string, tags: any) { public static SubstituteKeys(txt: string, tags: any) {
for (const key in tags) { for (const key in tags) {
// Poor mans replace all txt = txt.replace(new RegExp("{" + key + "}", "g"), tags[key])
txt = txt.split("{" + key + "}").join(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 { InnerRender(): string {

View file

@ -37,7 +37,13 @@
] ]
}, },
"icon": { "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": { "color": {
"render": "#0000ff" "render": "#0000ff"