diff --git a/whtree/modules/tollium/lib/components/svg.whlib b/whtree/modules/tollium/lib/components/svg.whlib index 6e84016abf..3a534d3837 100644 --- a/whtree/modules/tollium/lib/components/svg.whlib +++ b/whtree/modules/tollium/lib/components/svg.whlib @@ -1,6 +1,7 @@ dirtyflags.fully) { RECORD compinfo := CELL[ - src := this->svg_source, vars := this->css_vars, this->hint, unmasked_events := GetUnmask(this, ["click"]), action := GetComponentName(this->__action), this->opacity, ]; + IF (this->__res_url != "") + INSERT CELL url := this->__res_url INTO compinfo; + ELSE + INSERT CELL src := this->svg_source INTO compinfo; this->owner->tolliumcontroller->SendComponent(this, compinfo); } ELSE { - IF (this->dirtyflags.source) - this->ToddUpdate([ type := "source", src := this->svg_source ]); + IF (this->dirtyflags.source) { + RECORD compinfo := [ type := "source" ]; + IF (this->__res_url != "") + INSERT CELL url := this->__res_url INTO compinfo; + ELSE + INSERT CELL src := this->svg_source INTO compinfo; + this->ToddUpdate(compinfo); + } IF (this->dirtyflags.vars) this->ToddUpdate([ type := "vars", vars := this->css_vars ]); @@ -143,9 +154,10 @@ PUBLIC OBJECTTYPE TolliumSVGImage EXTEND TolliumComponentBase < RETURN "100px"; } - MACRO SetSVGSource(STRING svg_source) { - IF (this->__svg_source != svg_source) { + MACRO SetSVGSource(STRING svg_source, STRING res_url DEFAULTSTO "") { + IF (this->__svg_source != svg_source OR res_url != this->__res_url) { this->__svg_source := svg_source; + this->__res_url := res_url; this->ExtUpdatedSource(); } } @@ -232,6 +244,14 @@ PUBLIC OBJECTTYPE TolliumSVGImage EXTEND TolliumComponentBase < // Public API // + /** Load an svg by absolute resource path + @long This loads an svg using an absolute resource path (eg. `mod::mymodule/web/img/image.svg`). Only SVG files are + supported. When an empty string is given, the component is cleared. + + The SVG is sent as raw xml to the browser, unless the given resource is a public module asset (eg. + `mod::mymodule/public/image.svg`), in which case it is fetched client-side. + @param resource The resource to load + */ PUBLIC MACRO LoadResource(STRING resource) { IF (resource = "") { this->svg_source := ""; @@ -240,6 +260,15 @@ PUBLIC OBJECTTYPE TolliumSVGImage EXTEND TolliumComponentBase < RECORD svg := WrapBlob(RetrieveWebHareResource(resource).data, GetNameFromPath(resource)); IF (svg.mimetype != "image/svg+xml") THROW NEW TolliumException(this, `Only SVG resources can be loaded in an component`); - this->svg_source := BlobToString(svg.data); + // Only fetch public module assets client-side; other resource urls (eg. webdesign web/ folders) aren't always served + STRING res_url; + IF (resource LIKE "mod::?*/public/*") { + res_url := GetModuleResourceURL(resource); + // Add the modification time to the url, so the browser refetches the svg when it's changed on disk + RECORD props := GetDiskFileProperties(GetWebHareResourceDiskPath(resource)); + IF (res_url != "" AND RecordExists(props)) + res_url := res_url || "?mtime=" || GetUnixTimestamp(props.modified); + } + this->SetSVGSource(BlobToString(svg.data), res_url); } >; diff --git a/whtree/modules/tollium/webdesigns/webinterface/components/svg/svg.ts b/whtree/modules/tollium/webdesigns/webinterface/components/svg/svg.ts index 81c02d09fc..e7e15230e1 100644 --- a/whtree/modules/tollium/webdesigns/webinterface/components/svg/svg.ts +++ b/whtree/modules/tollium/webdesigns/webinterface/components/svg/svg.ts @@ -11,7 +11,10 @@ declare global { } interface SVGAttributes extends ActionableAttributes { - src: string; + // Raw SVG source (not set if url is set) + src?: string; + // URL to fetch the SVG from (eg. a public module asset) + url?: string; vars?: Record; opacity?: number; } @@ -19,6 +22,11 @@ interface SVGAttributes extends ActionableAttributes { type SVGUpdate = { type: "source"; src: string; + url?: never; +} | { + type: "source"; + url: string; + src?: never; } | { type: "opacity"; opacity: number; @@ -37,6 +45,9 @@ export class ObjSVGImage extends ActionableComponent { componenttype = "svg"; private clickable = false; private svgSrc = ""; + private svgUrl = ""; + // Incremented on every source change, so a slow fetch doesn't overwrite a newer source + private sourceGeneration = 0; private opacity = 1; private wrapperNode!: HTMLDivElement; // Shadow root holding the SVG, so its