theme: fix flash of non-default variant, part II #757

adapt the variant generator to previous changes;
interestingly, the new mechanism caused the variant generator
to be greatly simplified; something, I didn't expect beforehand
This commit is contained in:
Sören Weber 2024-12-08 00:06:40 +01:00
parent 0505b36141
commit deee4ae6b7
No known key found for this signature in database
GPG key ID: BEC6D55545451B6D
22 changed files with 336 additions and 351 deletions

View file

@ -1,4 +1,5 @@
:root { :root {
/* blue */
--MAIN-TEXT-color: rgba(50, 50, 50, 1); /* Color of text by default */ --MAIN-TEXT-color: rgba(50, 50, 50, 1); /* Color of text by default */
--MAIN-TITLES-TEXT-color: rgba(94, 94, 94, 1); /* Color of titles h2-h3-h4-h5-h6 */ --MAIN-TITLES-TEXT-color: rgba(94, 94, 94, 1); /* Color of titles h2-h3-h4-h5-h6 */
--MAIN-TITLES-H1-TEXT-color: rgba(34, 34, 34, 1); /* text color of h1 titles */ --MAIN-TITLES-H1-TEXT-color: rgba(34, 34, 34, 1); /* text color of h1 titles */

View file

@ -1,4 +1,5 @@
:root { :root {
/* green */
--MAIN-TEXT-color: rgba(50, 50, 50, 1); /* Color of text by default */ --MAIN-TEXT-color: rgba(50, 50, 50, 1); /* Color of text by default */
--MAIN-TITLES-TEXT-color: rgba(94, 94, 94, 1); /* Color of titles h2-h3-h4-h5-h6 */ --MAIN-TITLES-TEXT-color: rgba(94, 94, 94, 1); /* Color of titles h2-h3-h4-h5-h6 */
--MAIN-TITLES-H1-TEXT-color: rgba(34, 34, 34, 1); /* text color of h1 titles */ --MAIN-TITLES-H1-TEXT-color: rgba(34, 34, 34, 1); /* text color of h1 titles */

View file

@ -1,4 +1,5 @@
:root { :root {
/* learn */
--MAIN-TEXT-color: rgba(50, 50, 50, 1); /* Color of text by default */ --MAIN-TEXT-color: rgba(50, 50, 50, 1); /* Color of text by default */
--MAIN-TITLES-TEXT-color: rgba(94, 94, 94, 1); /* Color of titles h2-h3-h4-h5-h6 */ --MAIN-TITLES-TEXT-color: rgba(94, 94, 94, 1); /* Color of titles h2-h3-h4-h5-h6 */
--MAIN-TITLES-H1-TEXT-color: rgba(34, 34, 34, 1); /* text color of h1 titles */ --MAIN-TITLES-H1-TEXT-color: rgba(34, 34, 34, 1); /* text color of h1 titles */

View file

@ -1,4 +1,5 @@
:root { :root {
/* neon */
--PRIMARY-color: rgba(243, 0, 178, 1); /* brand primary color */ --PRIMARY-color: rgba(243, 0, 178, 1); /* brand primary color */
--SECONDARY-color: rgb(50, 189, 243, 1); /* brand secondary color */ --SECONDARY-color: rgb(50, 189, 243, 1); /* brand secondary color */
--ACCENT-color: rgba(255, 255, 0, 1); /* brand accent color, used for search highlights */ --ACCENT-color: rgba(255, 255, 0, 1); /* brand accent color, used for search highlights */

View file

@ -1,4 +1,5 @@
:root { :root {
/* red */
--MAIN-TEXT-color: rgba(50, 50, 50, 1); /* Color of text by default */ --MAIN-TEXT-color: rgba(50, 50, 50, 1); /* Color of text by default */
--MAIN-TITLES-TEXT-color: rgba(94, 94, 94, 1); /* Color of titles h2-h3-h4-h5-h6 */ --MAIN-TITLES-TEXT-color: rgba(94, 94, 94, 1); /* Color of titles h2-h3-h4-h5-h6 */
--MAIN-TITLES-H1-TEXT-color: rgba(34, 34, 34, 1); /* text color of h1 titles */ --MAIN-TITLES-H1-TEXT-color: rgba(34, 34, 34, 1); /* text color of h1 titles */

View file

@ -1,4 +1,5 @@
:root { :root {
/* relearn-bright */
--PRIMARY-color: rgba(131, 201, 50, 1); /* brand primary color */ --PRIMARY-color: rgba(131, 201, 50, 1); /* brand primary color */
--SECONDARY-color: rgba(99, 128, 208, 1); /* brand secondary color */ --SECONDARY-color: rgba(99, 128, 208, 1); /* brand secondary color */
--ACCENT-color: rgb(255, 102, 78, 1); /* brand accent color, used for search highlights */ --ACCENT-color: rgb(255, 102, 78, 1); /* brand accent color, used for search highlights */

View file

@ -1,4 +1,5 @@
:root { :root {
/* relearn-dark */
--PRIMARY-color: rgba(125, 201, 3, 1); /* brand primary color */ --PRIMARY-color: rgba(125, 201, 3, 1); /* brand primary color */
--SECONDARY-color: rgba(108, 140, 227, 1); /* brand secondary color */ --SECONDARY-color: rgba(108, 140, 227, 1); /* brand secondary color */
--ACCENT-color: rgb(255, 102, 78, 1); /* brand accent color, used for search highlights */ --ACCENT-color: rgb(255, 102, 78, 1); /* brand accent color, used for search highlights */

View file

@ -1,4 +1,5 @@
:root { :root {
/* relearn-light */
--PRIMARY-color: rgba(125, 201, 3, 1); /* brand primary color */ --PRIMARY-color: rgba(125, 201, 3, 1); /* brand primary color */
--SECONDARY-color: rgba(72, 106, 201, 1); /* brand secondary color */ --SECONDARY-color: rgba(72, 106, 201, 1); /* brand secondary color */
--ACCENT-color: rgb(255, 102, 78); /* brand accent color, used for search highlights */ --ACCENT-color: rgb(255, 102, 78); /* brand accent color, used for search highlights */

View file

@ -1,4 +1,5 @@
:root { :root {
/* zen-dark */
--PRIMARY-color: rgba(47, 129, 235, 1); /* brand primary color */ --PRIMARY-color: rgba(47, 129, 235, 1); /* brand primary color */
--SECONDARY-color: rgba(47, 129, 235, 1); /* brand secondary color */ --SECONDARY-color: rgba(47, 129, 235, 1); /* brand secondary color */

View file

@ -1,4 +1,5 @@
:root { :root {
/* zen-light */
--PRIMARY-color: rgba(26, 115, 232, 1); /* brand primary color */ --PRIMARY-color: rgba(26, 115, 232, 1); /* brand primary color */
--SECONDARY-color: rgba(26, 115, 232, 1); /* brand secondary color */ --SECONDARY-color: rgba(26, 115, 232, 1); /* brand secondary color */

View file

@ -246,3 +246,10 @@ summaryLength = 10
siteparam.test.text = 'A **nested** option <b>with</b> formatting' siteparam.test.text = 'A **nested** option <b>with</b> formatting'
# Extension to the image effects only for the docs. # Extension to the image effects only for the docs.
imageEffects.bg-white = true imageEffects.bg-white = true
# This is for support of the variantgenerator in the exampleSite, you don't need this!
variantgenerator.force = true
[params.relearn.dependencies]
# This is for support of the variantgenerator in the exampleSite, you don't need this!
[params.relearn.dependencies.variantgenerator]
name = 'VariantGenerator'

View file

@ -80,7 +80,7 @@ themeVariant = [
# If set to `true`, further theme asset files besides the generated HTML files # If set to `true`, further theme asset files besides the generated HTML files
# will be minified during build. If no value is set, the theme will avoid # will be minified during build. If no value is set, the theme will avoid
# minification if you have started with `hugo server` and otherwise will minify. # minification if you have started with `hugo server` and otherwise will minify.
minify = true minify = ""
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# General # General

View file

@ -1,7 +1,6 @@
+++ +++
categories = ["tutorial"] categories = ["tutorial"]
description = "An interactive tool to generate color variant stylesheets" description = "An interactive tool to generate color variant stylesheets"
mermaid.force = true
options = ["themeVariant"] options = ["themeVariant"]
title = "Stylesheet Generator" title = "Stylesheet Generator"
weight = 4 weight = 4
@ -27,14 +26,4 @@ Once you are satisfied, you can download the new variants file and copy it into
See the docs for [further configuration options](configuration/branding/colors). See the docs for [further configuration options](configuration/branding/colors).
{{% /expand %}} {{% /expand %}}
{{% button style="secondary" icon="download" href="javascript:window.variants&&variants.getStylesheet();this.blur();" %}}Download variant{{% /button %}} {{% variantgenerator %}}
{{% button style="warning" icon="trash" href="javascript:window.variants&&variants.resetVariant();this.blur();" %}}Reset variant{{% /button %}}
<div id="R-vargenerator" class="mermaid zoomable" style="background-color: var(--INTERNAL-MAIN-TEXT-color);">Graph</div>
{{% button style="secondary" icon="download" href="javascript:window.variants&&variants.getStylesheet();this.blur();" %}}Download variant{{% /button %}}
{{% button style="warning" icon="trash" href="javascript:window.variants&&variants.resetVariant();this.blur();" %}}Reset variant{{% /button %}}
<script>
window.variants && variants.generator( '#R-vargenerator' );
</script>

View file

@ -1,7 +1,6 @@
+++ +++
categories = ["tutorial"] categories = ["tutorial"]
description = "An interactive tool to generate color variant stylesheets" description = "An interactive tool to generate color variant stylesheets"
mermaid.force = true
options = ["themeVariant"] options = ["themeVariant"]
title = "Stylesheet Generrrat'r" title = "Stylesheet Generrrat'r"
weight = 4 weight = 4

View file

@ -16,6 +16,12 @@ weight = -2
Due to these changes, `expand` and `notice` with `style=transparent` will now generate slightly different margins. Due to these changes, `expand` and `notice` with `style=transparent` will now generate slightly different margins.
- {{% badge style="note" title=" " %}}Change{{% /badge %}} This release fixes a bug, where the selection of a non-default variant may caused the page to flicker on load.
To achieve this, generation and handling of the theme variant stylesheets and the variant generator were completely rewritten. There are no changes required by you.
Anyways, please note that now the [variant generator](configuration/branding/generator) is not included in the theme release anymore but is only available in the exampleSite docs. As a sideeffect, less JavaScript will be loaded on your site.
### New ### New
- {{% badge style="info" icon="plus-circle" title=" " %}}New{{% /badge %}} This release fixes a long standing issue, where loading a page with a non-default variant may caused screen flashing. - {{% badge style="info" icon="plus-circle" title=" " %}}New{{% /badge %}} This release fixes a long standing issue, where loading a page with a non-default variant may caused screen flashing.

View file

@ -0,0 +1,8 @@
{{- $page := .page }}
{{- $location := .location }}
{{- if eq $location "header" }}
{{- with $page }}
{{- $assetBusting := partialCached "assetbusting.gotmpl" . }}
<script src="{{"js/variant.js" | relURL}}{{ $assetBusting }}"></script>
{{- end }}
{{- end }}

View file

@ -0,0 +1,45 @@
{{ partial "shortcodes/notice.html" (dict
"page" .Page
"style" "note"
"content" "The <code>CODE-theme</code> parameter can be changed in the generator but the change will not be reflected dynamically in the page preview."
)}}
{{ partial "shortcodes/button.html" (dict
"page" .Page
"href" "javascript:window.variants&&variants.getStylesheet();this.blur();"
"style" "secondary"
"icon" "download"
"content" "Download variant"
)}}
{{ partial "shortcodes/button.html" (dict
"page" .Page
"href" "javascript:window.variants&&variants.resetVariant();this.blur();"
"style" "warning"
"icon" "trash"
"content" "Reset variant"
)}}
<div id="R-vargenerator" class="mermaid zoomable" style="background-color: var(--INTERNAL-MAIN-TEXT-color);">
Graph
</div>
{{ partial "shortcodes/button.html" (dict
"page" .Page
"href" "javascript:window.variants&&variants.getStylesheet();this.blur();"
"style" "secondary"
"icon" "download"
"content" "Download variant"
)}}
{{ partial "shortcodes/button.html" (dict
"page" .Page
"href" "javascript:window.variants&&variants.resetVariant();this.blur();"
"style" "warning"
"icon" "trash"
"content" "Reset variant"
)}}
<script>
window.variants && variants.generator( '#R-vargenerator' );
</script>
{{- /* the variant generator also requires Mermaid; so as there is no Mermaid
shortcode involved here to create the graph, we have to take care
to load it our self; the quickest way to do this is, to set the
Mermaid dependency as well */}}
{{- .Page.Store.Set "hasMermaid" true }}
{{- .Page.Store.Set "hasVariantGenerator" true }}

View file

@ -1,9 +1,9 @@
{{- $page := . }} {{- $page := . }}
{{- $nonautothemevariants := slice }} {{- $nonautothemevariants := slice }}
{{- $formathtmlpre := ":root:not([data-r-output-format='print'])" }} {{- $formathtmlpre := ":root:not([data-r-output-format='print']):not([data-r-theme-variant='my-custom-variant'])" }}
{{- $formathtml := "" }} {{- $formathtml := "" }}
{{- $minify := not hugo.IsServer }} {{- $minify := not hugo.IsServer }}
{{- if isset site.Params "minify" }} {{- if and (isset site.Params "minify") (ne site.Params.minify "") }}
{{- $minify = site.Params.minify }} {{- $minify = site.Params.minify }}
{{- end }} {{- end }}
@ -30,6 +30,9 @@ Unification run:
{{- if not (isset $themevariant "auto") }} {{- if not (isset $themevariant "auto") }}
{{- $nonautothemevariants = $nonautothemevariants | append $themevariant.identifier }} {{- $nonautothemevariants = $nonautothemevariants | append $themevariant.identifier }}
{{- end }} {{- end }}
{{- if eq $themevariant.identifier "my-custom-variant" }}
{{- errorf "\"theme-%s.css\": the variant identifier '%s' is reserved for the theme's variant generator, instead rename it to something different" "my-custom-variant" "my-custom-variant" }}
{{- end }}
{{- $themevariants = $themevariants | append $themevariant }} {{- $themevariants = $themevariants | append $themevariant }}
{{- end }} {{- end }}

View file

@ -79,7 +79,7 @@
</div> </div>
<div class="clear"></div> <div class="clear"></div>
</div> </div>
<script>window.relearn.initVariant();</script> <script>window.relearn.markVariant();</script>
</li> </li>
<li class="footerVisitedLinks{{if $showvisitedlinks}} showVisitedLinks{{end}}"> <li class="footerVisitedLinks{{if $showvisitedlinks}} showVisitedLinks{{end}}">
<div class="padding menu-control"> <div class="padding menu-control">

View file

@ -11,12 +11,12 @@
<link href="{{"css/perfect-scrollbar.min.css" | relURL}}{{ $assetBusting }}" rel="stylesheet"> <link href="{{"css/perfect-scrollbar.min.css" | relURL}}{{ $assetBusting }}" rel="stylesheet">
{{- $themevariants := partialCached "_relearn/themeVariants.gotmpl" . }} {{- $themevariants := partialCached "_relearn/themeVariants.gotmpl" . }}
{{- $minify := not hugo.IsServer }} {{- $minify := not hugo.IsServer }}
{{- if isset site.Params "minify" }} {{- if and (isset site.Params "minify") (ne site.Params.minify "") }}
{{- $minify = site.Params.minify }} {{- $minify = site.Params.minify }}
{{- end }} {{- end }}
{{- $min := cond $minify ".min" "" }} {{- $min := cond $minify ".min" "" }}
<link href="{{(printf "css/theme%s.css" $min) | relURL}}{{ $assetBusting }}" rel="stylesheet"> <link href="{{(printf "css/theme%s.css" $min) | relURL}}{{ $assetBusting }}" rel="stylesheet">
<link href="{{(printf "css/format-%s%s.css" $outputFormat $min) | relURL}}{{ $assetBusting }}" rel="stylesheet"> <link href="{{(printf "css/format-%s%s.css" $outputFormat $min) | relURL}}{{ $assetBusting }}" rel="stylesheet" id="R-format-style">
<script> <script>
window.relearn = window.relearn || {}; window.relearn = window.relearn || {};
window.relearn.relBasePath='{{ partial "_relearn/relBasePath.gotmpl" . | safeJS }}'; window.relearn.relBasePath='{{ partial "_relearn/relBasePath.gotmpl" . | safeJS }}';
@ -30,32 +30,34 @@
{{- range $themevariants }} {{- range $themevariants }}
{{- $quotedthemevariants = $quotedthemevariants | append (printf "'%s'" .identifier) }} {{- $quotedthemevariants = $quotedthemevariants | append (printf "'%s'" .identifier) }}
{{- end }} {{- end }}
window.variants && variants.init( [ {{ delimit $quotedthemevariants ", " | safeJS }} ] ); window.relearn.themevariants = [ {{ delimit $quotedthemevariants ", " | safeJS }} ];
window.relearn.customvariantname = "my-custom-variant";
window.relearn.changeVariant = function(variant) { window.relearn.changeVariant = function(variant) {
var oldVariant = document.documentElement.dataset.rThemeVariant;
window.localStorage.setItem(window.relearn.absBaseUri + "/variant", variant);
document.documentElement.dataset.rThemeVariant = variant; document.documentElement.dataset.rThemeVariant = variant;
var old_variant = window.localStorage.getItem("R-theme-variant"); if (oldVariant != variant) {
if (old_variant != variant) { document.dispatchEvent( new CustomEvent('themeVariantLoaded', { detail: { variant, oldVariant } }) );
window.localStorage.setItem("R-theme-variant", variant); }
document.dispatchEvent( new CustomEvent('themeVariantLoaded', { detail: { variant: variant } }) ); }
window.relearn.markVariant = function() {
var variant = window.localStorage.getItem(window.relearn.absBaseUri + "/variant");
var select = document.querySelector("#R-select-variant");
if (select) {
select.value = variant;
} }
} }
window.relearn.initVariant = function() { window.relearn.initVariant = function() {
var variant = window.localStorage.getItem("R-theme-variant") ?? ""; var variant = window.localStorage.getItem(window.relearn.absBaseUri + "/variant") ?? "";
var el = document.querySelector("#R-select-variant"); if( variant == window.relearn.customvariantname ){
if (variant && el) { }else if( !variant || !window.relearn.themevariants.includes(variant) ){
var options = Array.from(el.options); variant = window.relearn.themevariants[0];
if( options.some(option => option.value == variant) ){ window.localStorage.setItem(window.relearn.absBaseUri + "/variant", variant);
el.value = variant;
}
else {
variant = options[0]?.value ?? "";
el.value = variant;
window.localStorage.setItem("R-theme-variant", variant);
}
} }
document.documentElement.dataset.rThemeVariant = variant; document.documentElement.dataset.rThemeVariant = variant;
} }
window.relearn.initVariant(); window.relearn.initVariant();
window.relearn.markVariant();
{{ "// translations" | safeJS }} {{ "// translations" | safeJS }}
{{ printf "window.T_Copy_to_clipboard = `%s`;" (T `Copy-to-clipboard`) | safeJS }} {{ printf "window.T_Copy_to_clipboard = `%s`;" (T `Copy-to-clipboard`) | safeJS }}
{{ printf "window.T_Copied_to_clipboard = `%s`;" (T `Copied-to-clipboard`) | safeJS }} {{ printf "window.T_Copied_to_clipboard = `%s`;" (T `Copied-to-clipboard`) | safeJS }}

View file

@ -1 +1 @@
7.1.1+2c5ac2b60022b3ec8e9e275bfeb2d5ebc77d9bc0 7.1.1+0505b36141f2a69dc36be8589c0678783cad98a9

View file

@ -12,89 +12,47 @@ function ready(fn) {
} }
var variants = { var variants = {
variant: '', variants: window.relearn.themevariants,
variants: [], customvariantname: window.relearn.customvariantname,
customvariantname: 'my-custom-variant',
isstylesheetloaded: true, isstylesheetloaded: true,
init: function (variants) { setup: function () {
this.variants = variants; this.addCustomVariantStyles();
var variant = window.localStorage.getItem(window.relearn.absBaseUri + '/variant') || (this.variants.length ? this.variants[0] : '');
this.changeVariant(variant);
document.addEventListener(
'readystatechange',
function () {
if (document.readyState == 'interactive') {
this.markSelectedVariant();
}
}.bind(this)
);
},
getVariant: function () { var customvariantstylesheet = window.localStorage.getItem(window.relearn.absBaseUri + '/customvariantstylesheet');
return this.variant; var customvariant = window.localStorage.getItem(window.relearn.absBaseUri + '/customvariant');
}, if (!customvariantstylesheet || !customvariant) {
customvariantstylesheet = '';
setVariant: function (variant) { window.localStorage.removeItem(window.relearn.absBaseUri + '/customvariantstylesheet');
this.variant = variant; customvariant = '';
window.localStorage.setItem(window.relearn.absBaseUri + '/variant', variant); window.localStorage.removeItem(window.relearn.absBaseUri + '/customvariant');
}, } else if (customvariant && !window.relearn.themevariants.includes(customvariant)) {
// this can only happen on initial load, if a previously selected variant is not available anymore
isVariantLoaded: function () { customvariant = window.relearn.themevariants[0];
return window.theme && this.isstylesheetloaded; window.localStorage.setItem(window.relearn.absBaseUri + '/customvariant', customvariant);
},
markSelectedVariant: function () {
var variant = this.getVariant();
var select = document.querySelector('#R-select-variant');
if (!select) {
return;
} }
this.updateCustomVariantStyles(customvariantstylesheet);
this.init();
ready(this.init.bind(this));
},
init: function (variant, old_path) {
this.addCustomVariantOption(); this.addCustomVariantOption();
if (variant && select.value != variant) { window.relearn.markVariant();
select.value = variant; window.relearn.changeVariant(window.localStorage.getItem(window.relearn.absBaseUri + '/variant'));
}
var interval_id = setInterval(
function () {
if (this.isVariantLoaded()) {
clearInterval(interval_id);
updateTheme({ variant: variant });
}
}.bind(this),
25
);
// remove selection, because if some uses an arrow navigation"
// by pressing the left or right cursor key, we will automatically
// select a different style
if (document.activeElement) {
document.activeElement.blur();
}
},
generateVariantPath: function (variant, old_path) {
var new_path = old_path.replace(new RegExp(`^(.*\/theme-).*?(\.css.*)$`), '$1' + variant + '$2');
return new_path;
}, },
addCustomVariantOption: function () { addCustomVariantOption: function () {
var variantbase = window.localStorage.getItem(window.relearn.absBaseUri + '/customvariantbase'); var customvariant = window.localStorage.getItem(window.relearn.absBaseUri + '/customvariant');
if (this.variants.indexOf(variantbase) < 0) {
variantbase = '';
}
if (!window.localStorage.getItem(window.relearn.absBaseUri + '/customvariant')) {
variantbase = '';
}
if (!variantbase) {
return;
}
var select = document.querySelector('#R-select-variant'); var select = document.querySelector('#R-select-variant');
if (!select) { if (!customvariant || !select) {
return; return;
} }
var option = document.querySelector('#' + this.customvariantname); var option = document.querySelector('#R-select-variant-' + this.customvariantname);
if (!option) { if (!option) {
option = document.createElement('option'); option = document.createElement('option');
option.id = this.customvariantname; option.id = 'R-select-variant-' + this.customvariantname;
option.value = this.customvariantname; option.value = this.customvariantname;
option.text = this.customvariantname.replace(/-/g, ' ').replace(/\w\S*/g, function (w) { option.text = this.customvariantname.replace(/-/g, ' ').replace(/\w\S*/g, function (w) {
return w.replace(/^\w/g, function (c) { return w.replace(/^\w/g, function (c) {
@ -109,116 +67,225 @@ var variants = {
}, },
removeCustomVariantOption: function () { removeCustomVariantOption: function () {
var option = document.querySelector('#' + this.customvariantname); var option = document.querySelector('#R-select-variant-' + this.customvariantname);
if (option) { if (option) {
option.remove(); option.remove();
if (this.variants.length <= 1) {
document.querySelectorAll('.footerVariantSwitch').forEach(function (e) {
e.classList.remove('showVariantSwitch');
});
}
} }
if (this.variants.length <= 1) { },
document.querySelectorAll('.footerVariantSwitch').forEach(function (e) {
e.classList.remove('showVariantSwitch'); addCustomVariantStyles: function () {
}); var head = document.querySelector('head');
var style = document.createElement('style');
style.id = 'R-variant-styles-' + this.customvariantname;
head.appendChild(style);
},
updateCustomVariantStyles: function (stylesheet) {
stylesheet = ":root:not([data-r-output-format='print'])[data-r-theme-variant='" + this.customvariantname + "'] {" + '\n&' + stylesheet + '\n}';
var style = document.querySelector('#R-variant-styles-' + this.customvariantname);
if (style) {
style.textContent = stylesheet;
} }
}, },
saveCustomVariant: function () { saveCustomVariant: function () {
if (this.getVariant() != this.customvariantname) { var variant = window.localStorage.getItem(window.relearn.absBaseUri + '/variant');
window.localStorage.setItem(window.relearn.absBaseUri + '/customvariantbase', this.getVariant()); if (variant != this.customvariantname) {
window.localStorage.setItem(window.relearn.absBaseUri + '/customvariant', variant);
} }
window.localStorage.setItem(window.relearn.absBaseUri + '/customvariant', this.generateStylesheet()); var stylesheet = this.generateStylesheet();
this.setVariant(this.customvariantname); window.localStorage.setItem(window.relearn.absBaseUri + '/variant', this.customvariantname);
this.markSelectedVariant(); window.localStorage.setItem(window.relearn.absBaseUri + '/customvariantstylesheet', stylesheet);
this.updateCustomVariantStyles(stylesheet);
this.addCustomVariantOption();
window.relearn.markVariant();
window.relearn.changeVariant(this.customvariantname);
}, },
loadCustomVariant: function () { normalizeColor: function (c) {
var stylesheet = window.localStorage.getItem(window.relearn.absBaseUri + '/customvariant'); if (!c || !c.trim) {
return c;
}
c = c.trim();
c = c.replace(/\s*\(\s*/g, '( ');
c = c.replace(/\s*\)\s*/g, ' )');
c = c.replace(/\s*,\s*/g, ', ');
c = c.replace(/0*\./g, '.');
c = c.replace(/ +/g, ' ');
return c;
},
// temp styles to document getColorValue: function (c) {
var head = document.querySelector('head'); return this.normalizeColor(getComputedStyle(document.documentElement).getPropertyValue('--INTERNAL-' + c));
var style = document.createElement('style'); },
style.id = 'R-custom-variant-style';
style.appendChild(document.createTextNode(stylesheet));
head.appendChild(style);
var interval_id = setInterval( getColorProperty: function (c, read_style) {
function () { var e = this.findColor(c);
if (this.findLoadedStylesheet('R-variant-style')) { var p = this.normalizeColor(read_style.getPropertyValue('--' + c)).replace('--INTERNAL-', '--');
clearInterval(interval_id); return p;
// save the styles to the current variant stylesheet },
this.variantvariables.forEach(
function (e) {
this.changeColor(e.name, true);
}.bind(this)
);
// remove temp styles findRootRule: function (rules, parentSelectors) {
style.remove(); for (let rule of rules ?? []) {
if ((rule.conditionText && rule.conditionText != 'screen') || (rule.selectorText && !rule.selectorText.startsWith(':root:not'))) {
this.saveCustomVariant(); return null;
}
if (parentSelectors.some((selector) => rule.selectorText === selector)) {
// Search nested rules for &:root
for (let nestedRule of rule.cssRules ?? []) {
if (nestedRule.selectorText === '&:root') {
return nestedRule.style;
}
} }
}.bind(this), return null;
25 }
); let result = this.findRootRule(rule.cssRules, parentSelectors);
if (result) {
return result;
}
}
return null;
},
findLoadedStylesheet: function (id, parentSelectors) {
for (let sheet of document.styleSheets) {
if (sheet.ownerNode.id === id) {
return this.findRootRule(sheet.cssRules, parentSelectors);
}
}
return null;
},
findColor: function (name) {
var f = this.variantvariables.find(function (x) {
return x.name == name;
});
return f;
},
generateColorVariable: function (e, read_style) {
var v = '';
var gen = this.getColorProperty(e.name, read_style);
if (gen) {
v += ' --' + e.name + ': ' + gen + '; /* ' + e.tooltip + ' */\n';
}
return v;
},
// ------------------------------------------------------------------------
// CSS download
// ------------------------------------------------------------------------
generateStylesheet: function () {
var customvariantbase = window.localStorage.getItem(window.relearn.absBaseUri + '/customvariant') ?? window.localStorage.getItem(window.relearn.absBaseUri + '/variant');
var base_style = this.findLoadedStylesheet('R-format-style', [':root:not([data-r-output-format="print"])[data-r-theme-variant="' + customvariantbase + '"]']);
if (!base_style) {
alert('There is nothing to be generated as auto mode variants will be generated by Hugo');
return;
}
var variant = this.customvariantname;
var custom_style = this.findLoadedStylesheet('R-variant-styles-' + variant, [':root:not([data-r-output-format="print"])[data-r-theme-variant="' + variant + '"]']);
if (!custom_style) {
variant = customvariantbase;
custom_style = base_style;
}
var style =
':root {\n' +
' /* ' +
variant +
' */\n' +
this.variantvariables.reduce(
function (a, e) {
return a + this.generateColorVariable(e, custom_style);
}.bind(this),
''
) +
'}\n';
return style;
},
download: function (data, mimetype, filename) {
var blob = new Blob([data], { type: mimetype });
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.setAttribute('href', url);
a.setAttribute('download', filename);
a.click();
},
// ------------------------------------------------------------------------
// external API
// ------------------------------------------------------------------------
changeColor: function (c) {
var customvariantbase = window.localStorage.getItem(window.relearn.absBaseUri + '/customvariant') ?? window.localStorage.getItem(window.relearn.absBaseUri + '/variant');
var base_style = this.findLoadedStylesheet('R-format-style', [':root:not([data-r-output-format="print"])[data-r-theme-variant="' + customvariantbase + '"]']);
if (!base_style) {
alert('An auto mode variant can not be changed. Please select its light/dark variant directly to make changes');
return;
}
var custom_style = this.findLoadedStylesheet('R-variant-styles-' + this.customvariantname, [':root:not([data-r-output-format="print"])[data-r-theme-variant="' + this.customvariantname + '"]']);
if (!custom_style) {
this.saveCustomVariant();
custom_style = this.findLoadedStylesheet('R-variant-styles-' + this.customvariantname, [':root:not([data-r-output-format="print"])[data-r-theme-variant="' + this.customvariantname + '"]']);
}
var e = this.findColor(c);
var v = this.getColorProperty(c, custom_style);
var t = c + '\n\n' + e.tooltip + '\n';
if (e.fallback) {
t += '\nInherits value "' + this.getColorValue(e.fallback) + '" from ' + e.fallback + ' if not set\n';
} else if (e.default) {
t += '\nDefaults to value "' + this.normalizeColor(e.default) + '" if not set\n';
}
var n = prompt(t, v);
if (n === null) {
// user canceld operation
return;
}
if (n) {
// value set to specific value
n = this.normalizeColor(n).replace('--INTERNAL-', '--').replace('--', '--INTERNAL-');
if (n != v) {
custom_style.setProperty('--' + c, n);
}
} else {
// value emptied, so delete it
custom_style.removeProperty('--' + c);
}
this.saveCustomVariant();
}, },
resetVariant: function () { resetVariant: function () {
var variantbase = window.localStorage.getItem(window.relearn.absBaseUri + '/customvariantbase'); var customvariant = window.localStorage.getItem(window.relearn.absBaseUri + '/customvariant');
if (variantbase && confirm('You have made changes to your custom variant. Are you sure you want to reset all changes?')) { if (customvariant && confirm('You have made changes to your custom variant. Are you sure you want to reset all changes?')) {
window.localStorage.removeItem(window.relearn.absBaseUri + '/customvariantbase');
window.localStorage.removeItem(window.relearn.absBaseUri + '/customvariant'); window.localStorage.removeItem(window.relearn.absBaseUri + '/customvariant');
window.localStorage.removeItem(window.relearn.absBaseUri + '/customvariantstylesheet');
window.localStorage.setItem(window.relearn.absBaseUri + '/variant', customvariant);
this.updateCustomVariantStyles('');
this.removeCustomVariantOption(); this.removeCustomVariantOption();
if (this.getVariant() == this.customvariantname) { window.relearn.markVariant();
this.changeVariant(variantbase); window.relearn.changeVariant(customvariant);
}
} }
}, },
onLoadStylesheet: function () { getStylesheet: function () {
variants.isstylesheetloaded = true; var style = this.generateStylesheet();
}, if (style) {
console.log(style);
switchStylesheet: function (variant, without_check) { this.download(style, 'text/css', 'theme-' + this.customvariantname + '.css');
var link = document.querySelector('#R-variant-style');
if (!link) {
return;
}
var old_path = link.getAttribute('href');
var new_path = this.generateVariantPath(variant, old_path);
this.isstylesheetloaded = false;
// Chrome needs a new element to trigger the load callback again
var new_link = document.createElement('link');
new_link.id = 'R-variant-style';
new_link.rel = 'stylesheet';
new_link.onload = this.onLoadStylesheet;
new_link.setAttribute('href', new_path);
link.parentNode.replaceChild(new_link, link);
},
changeVariant: function (variant) {
if (variant == this.customvariantname) {
var variantbase = window.localStorage.getItem(window.relearn.absBaseUri + '/customvariantbase');
if (this.variants.indexOf(variantbase) < 0) {
variant = '';
}
if (!window.localStorage.getItem(window.relearn.absBaseUri + '/customvariant')) {
variant = '';
}
this.setVariant(variant);
if (!variant) {
return;
}
this.switchStylesheet(variantbase);
this.loadCustomVariant();
} else {
if (this.variants.indexOf(variant) < 0) {
variant = this.variants.length ? this.variants[0] : '';
}
this.setVariant(variant);
if (!variant) {
return;
}
this.switchStylesheet(variant);
this.markSelectedVariant();
} }
}, },
@ -240,23 +307,9 @@ var variants = {
); );
}, },
download: function (data, mimetype, filename) { // ------------------------------------------------------------------------
var blob = new Blob([data], { type: mimetype }); // Mermaid graph stuff
var url = window.URL.createObjectURL(blob); // ------------------------------------------------------------------------
var a = document.createElement('a');
a.setAttribute('href', url);
a.setAttribute('download', filename);
a.click();
},
getStylesheet: function () {
var style = this.generateStylesheet();
if (!style) {
alert('There is nothing to be generated as auto mode variants will be generated by Hugo');
return;
}
this.download(style, 'text/css', 'theme-' + this.customvariantname + '.css');
},
adjustCSSRules: function (selector, props, sheets) { adjustCSSRules: function (selector, props, sheets) {
// get stylesheet(s) // get stylesheet(s)
@ -291,145 +344,6 @@ var variants = {
} }
}, },
normalizeColor: function (c) {
if (!c || !c.trim) {
return c;
}
c = c.trim();
c = c.replace(/\s*\(\s*/g, '( ');
c = c.replace(/\s*\)\s*/g, ' )');
c = c.replace(/\s*,\s*/g, ', ');
c = c.replace(/0*\./g, '.');
c = c.replace(/ +/g, ' ');
return c;
},
getColorValue: function (c) {
return this.normalizeColor(getComputedStyle(document.documentElement).getPropertyValue('--INTERNAL-' + c));
},
getColorProperty: function (c, read_style) {
var e = this.findColor(c);
var p = this.normalizeColor(read_style.getPropertyValue('--' + c)).replace('--INTERNAL-', '--');
return p;
},
findLoadedStylesheet: function (id) {
for (var n = 0; n < document.styleSheets.length; ++n) {
if (document.styleSheets[n].ownerNode.id == id) {
var s = document.styleSheets[n];
if (s.rules && s.rules.length) {
for (var m = 0; m < s.rules.length; ++m) {
if (s.rules[m].selectorText == ':root') {
return s.rules[m].style;
}
if (s.rules[m].cssRules && s.rules[m].cssRules.length) {
for (var o = 0; o < s.rules[m].cssRules.length; ++o) {
if (s.rules[m].cssRules[o].selectorText == ':root') {
return s.rules[m].cssRules[o].style;
}
}
}
}
}
break;
}
}
return null;
},
changeColor: function (c, without_prompt) {
var with_prompt = !(without_prompt || false);
var read_style = this.findLoadedStylesheet('R-custom-variant-style');
var write_style = this.findLoadedStylesheet('R-variant-style');
if (!read_style) {
read_style = write_style;
}
if (!read_style) {
if (with_prompt) {
alert('An auto mode variant can not be changed. Please select its light/dark variant directly to make changes');
}
return;
}
var e = this.findColor(c);
var v = this.getColorProperty(c, read_style);
var n = '';
if (!with_prompt) {
n = v;
} else {
var t = c + '\n\n' + e.tooltip + '\n';
if (e.fallback) {
t += '\nInherits value "' + this.getColorValue(e.fallback) + '" from ' + e.fallback + ' if not set\n';
} else if (e.default) {
t += '\nDefaults to value "' + this.normalizeColor(e.default) + '" if not set\n';
}
n = prompt(t, v);
if (n === null) {
// user canceld operation
return;
}
}
if (n) {
// value set to specific value
n = this.normalizeColor(n).replace('--INTERNAL-', '--').replace('--', '--INTERNAL-');
if (!with_prompt || n != v) {
write_style.setProperty('--' + c, n);
}
} else {
// value emptied, so delete it
write_style.removeProperty('--' + c);
}
if (with_prompt) {
this.saveCustomVariant();
}
},
findColor: function (name) {
var f = this.variantvariables.find(function (x) {
return x.name == name;
});
return f;
},
generateColorVariable: function (e, read_style) {
var v = '';
var gen = this.getColorProperty(e.name, read_style);
if (gen) {
v += ' --' + e.name + ': ' + gen + '; /* ' + e.tooltip + ' */\n';
}
return v;
},
generateStylesheet: function () {
var read_style = this.findLoadedStylesheet('R-custom-variant-style');
var write_style = this.findLoadedStylesheet('R-variant-style');
if (!read_style) {
read_style = write_style;
}
if (!read_style) {
return;
}
var style =
'/* ' +
this.customvariantname +
' */\n' +
':root {\n' +
this.variantvariables.reduce(
function (a, e) {
return a + this.generateColorVariable(e, read_style);
}.bind(this),
''
) +
'}\n';
console.log(style);
return style;
},
styleGraphGroup: function (selector, colorvar) { styleGraphGroup: function (selector, colorvar) {
this.adjustCSSRules('#R-body svg ' + selector + ' > rect', 'color: var(--INTERNAL-' + colorvar + '); fill: var(--INTERNAL-' + colorvar + '); stroke: #80808080;'); this.adjustCSSRules('#R-body svg ' + selector + ' > rect', 'color: var(--INTERNAL-' + colorvar + '); fill: var(--INTERNAL-' + colorvar + '); stroke: #80808080;');
this.adjustCSSRules('#R-body svg ' + selector + ' > .label .nodeLabel', 'color: var(--INTERNAL-' + colorvar + '); fill: var(--INTERNAL-' + colorvar + '); stroke: #80808080;'); this.adjustCSSRules('#R-body svg ' + selector + ' > .label .nodeLabel', 'color: var(--INTERNAL-' + colorvar + '); fill: var(--INTERNAL-' + colorvar + '); stroke: #80808080;');
@ -688,3 +602,5 @@ var variants = {
{ name: 'BOX-WARNING-TEXT-color', group: 'colored boxes', fallback: 'BOX-RED-TEXT-color', tooltip: 'text color of warning boxes' }, { name: 'BOX-WARNING-TEXT-color', group: 'colored boxes', fallback: 'BOX-RED-TEXT-color', tooltip: 'text color of warning boxes' },
], ],
}; };
variants.setup();