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 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 {
|
||||||
|
|
||||||
|
@ -218,11 +219,35 @@ export default class LayerConfig {
|
||||||
this.dashArray = tr("dashArray", "");
|
this.dashArray = tr("dashArray", "");
|
||||||
|
|
||||||
|
|
||||||
if(json["showIf"] !== undefined){
|
if (json["showIf"] !== undefined) {
|
||||||
throw "Invalid key on layerconfig "+this.id+": showIf. Did you mean 'isShown' instead?";
|
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[] {
|
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))
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -34,39 +34,39 @@ 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 {
|
||||||
|
|
||||||
/* let cachedTranslations = Utils.getOrSetDefault(SubstitutedTranslation.cachedTranslations, SubstitutedTranslation.GenerateSubCache);
|
/* let cachedTranslations = Utils.getOrSetDefault(SubstitutedTranslation.cachedTranslations, SubstitutedTranslation.GenerateSubCache);
|
||||||
const innerMap = Utils.getOrSetDefault(cachedTranslations, translation, SubstitutedTranslation.GenerateMap);
|
const innerMap = Utils.getOrSetDefault(cachedTranslations, translation, SubstitutedTranslation.GenerateMap);
|
||||||
|
|
||||||
const cachedTranslation = innerMap.get(tags);
|
const cachedTranslation = innerMap.get(tags);
|
||||||
if (cachedTranslation !== undefined) {
|
if (cachedTranslation !== undefined) {
|
||||||
return cachedTranslation;
|
return cachedTranslation;
|
||||||
}*/
|
}*/
|
||||||
const st = new SubstitutedTranslation(translation, tags);
|
const st = new SubstitutedTranslation(translation, tags);
|
||||||
// innerMap.set(tags, st);
|
// innerMap.set(tags, st);
|
||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
if(this.content.length == 1){
|
if (this.content.length == 1) {
|
||||||
return this.content[0].Render();
|
return this.content[0].Render();
|
||||||
}
|
}
|
||||||
return new Combine(this.content).Render();
|
return new Combine(this.content).Render();
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue