From 78abe914e02943ba675b546acd027bbb5a5861fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Weber?= Date: Mon, 21 Feb 2022 23:11:04 +0100 Subject: [PATCH] variant: allow page browsing with custom theme #188 --- .../content/basics/customization/_index.en.md | 2 +- .../content/basics/generator/_index.en.md | 7 +- .../content/basics/migration/_index.en.md | 6 +- layouts/partials/menu.html | 42 ++-- layouts/partials/stylesheet.html | 2 +- static/css/theme.css | 14 ++ static/js/variant.js | 224 +++++++++++++----- 7 files changed, 204 insertions(+), 93 deletions(-) diff --git a/exampleSite/content/basics/customization/_index.en.md b/exampleSite/content/basics/customization/_index.en.md index b83ea2ded1..359090a058 100644 --- a/exampleSite/content/basics/customization/_index.en.md +++ b/exampleSite/content/basics/customization/_index.en.md @@ -168,7 +168,7 @@ Then, set the `themeVariant` value with the name of your custom theme file. That ### Multiple variants -You can also set multiple variants. In this case, the first variant is the default choosen on first view and a theme switch will be shown in the menu footer. +You can also set multiple variants. In this case, the first variant is the default choosen on first view and a variant switch will be shown in the menu footer. ```toml [params] diff --git a/exampleSite/content/basics/generator/_index.en.md b/exampleSite/content/basics/generator/_index.en.md index 71106b98e9..d4d3d14b6d 100644 --- a/exampleSite/content/basics/generator/_index.en.md +++ b/exampleSite/content/basics/generator/_index.en.md @@ -13,16 +13,19 @@ The arrowed lines reflect how colors are inherited thru different parts of the t To better understand this select the `neon` variant and modify the differnet heading colors. There, colors for the heading `h2`, `h3` and `h4` are explicitly set. `h5` is not set and inherits its value from `h4`. `h6` is also not set and inherits its value from `h5`. +Once you've changed a color, the theme selector will show a "My custom variant" entry and your changes are stored. You can change pages and even close the browser without losing your changes. + Once you are satisfied, you can download the new variants file and install it in your site. {{% notice note %}} -You need to define at least one `themeVariant` in your `config.toml` to modify and download the styles. - This only works in modern browsers. {{% /notice %}} ## Variant generator +Download color variant +Reset variant +
Graph
Download color variant diff --git a/exampleSite/content/basics/migration/_index.en.md b/exampleSite/content/basics/migration/_index.en.md index fe9403dca8..cc267fe1cd 100644 --- a/exampleSite/content/basics/migration/_index.en.md +++ b/exampleSite/content/basics/migration/_index.en.md @@ -24,7 +24,7 @@ This document shows you what's new in the latest release. For a detailed list of This change will only affect your installation if you've not set the `themeVariant` parameter in your `config.toml`. [If you still want to use the Learn color variant]({{%relref "basics/customization/#learn-variant" %}}), you have to explicitly set `themeVariant="learn"` in your `config.toml`. - Note, that this will also affect your site if viewed with Internet Explorer 11 and can not be reconfigured as it does not support CSS variables. + Note, that this will also affect your site if viewed with Internet Explorer 11 but in this case it can not be reconfigured as Internet Explorer does not support CSS variables. - **Change**: Due to a bug, that we couldn't fix in a general manner for color variants, we decided to remove `--MENU-SEARCH-BOX-ICONS-color`. @@ -36,9 +36,9 @@ This document shows you what's new in the latest release. For a detailed list of - **New**: To make the creation of new variants easier for you, we've added a new interactive [theme variant generator]({{%relref "basics/generator" %}}). -- **New**: You can now configure multiple color variants in your `config.toml`. In this case, the first variant is the default chosen on first view and a theme switch will be shown in the menu footer. See the [documentation]({{%relref "basics/customization/#multiple-variants" %}}) for configuration. +- **New**: You can now configure multiple color variants in your `config.toml`. In this case, the first variant is the default chosen on first view and a variant switch will be shown in the menu footer. See the [documentation]({{%relref "basics/customization/#multiple-variants" %}}) for configuration. - Note, that the new theme switch will not work with Internet Explorer 11 as it does not support CSS variables. + Note, that the new variant switch will not work with Internet Explorer 11 as it does not support CSS variables. ## 2.9.0 diff --git a/layouts/partials/menu.html b/layouts/partials/menu.html index b838c3b7a5..b028c459eb 100644 --- a/layouts/partials/menu.html +++ b/layouts/partials/menu.html @@ -52,26 +52,22 @@ {{- end }} -
- {{- $showlangswitch := and .Site.IsMultiLingual (not .Site.Params.disableLanguageSwitchingButton) }} - {{- $themevariants := slice | append .Site.Params.themeVariant }} - {{- $showthemeswitch := gt (int (len $themevariants)) 1 }} + {{- $siteLanguages := .Site.Languages }} + {{- $showlangswitch := and .Site.IsMultiLingual (not .Site.Params.disableLanguageSwitchingButton) (gt (int (len $siteLanguages)) 1) }} + {{- $themevariants := slice | append (.Site.Params.themeVariant | default "relearn-light" ) }} + {{- $showvariantswitch := gt (int (len $themevariants)) 1 }} {{- $footer := partial "menu-footer.html" . }} {{- $showfooter := not (eq 0 (int (len ($footer | plainify)))) }} - {{- if or $showlangswitch $showvisitedlinks $showfooter }} -
- {{- end }} - {{- if or $showlangswitch $showthemeswitch $showvisitedlinks }} -
+ + + - {{- end }} - {{- if $showfooter }} - {{- define "section-tree-nav" }} diff --git a/layouts/partials/stylesheet.html b/layouts/partials/stylesheet.html index 9cc15d354c..4b2e4c60e3 100644 --- a/layouts/partials/stylesheet.html +++ b/layouts/partials/stylesheet.html @@ -5,7 +5,7 @@ - {{- $themevariants := slice | append .Site.Params.themeVariant }} + {{- $themevariants := slice | append (.Site.Params.themeVariant | default "relearn-light" ) }} {{- with index $themevariants 0 }} {{- end }} diff --git a/static/css/theme.css b/static/css/theme.css index efbe52364c..5b88e91cce 100644 --- a/static/css/theme.css +++ b/static/css/theme.css @@ -1452,3 +1452,17 @@ h6 a { display: none; } } + +.footerLangSwitch, +.footerVariantSwitch, +.footerVisitedLinks, +.footerFooter { + display: none; +} + +.showLangSwitch, +.showVariantSwitch, +.showVisitedLinks, +.showFooter { + display: initial; +} diff --git a/static/js/variant.js b/static/js/variant.js index 1067440731..24fbd3f1d2 100644 --- a/static/js/variant.js +++ b/static/js/variant.js @@ -3,7 +3,7 @@ var variants = { variant: '', variants: [], - customvariant: 'my-variant', + customvariantname: 'my-custom-variant', init: function( variants ){ this.variants = variants; @@ -11,20 +11,11 @@ var variants = { this.changeVariant( variant ); document.addEventListener( 'readystatechange', function(){ if( document.readyState == 'interactive' ){ - this.markSelectedVariant( this.getVariant() ); + this.markSelectedVariant(); } }.bind( this ) ); }, - parseVariantnameFromFilename: function ( s ){ - if( !s || !s.match ){ - return ''; - } - var matches = s.match(/^.*\/?theme-([^\/]*?)\.css.*$/); - var variant = matches && matches.length == 2 ? matches[ 1 ] : ''; - return variant; - }, - getVariant: function(){ return this.variant; }, @@ -34,12 +25,14 @@ var variants = { window.localStorage.setItem( 'variant', variant ); }, - markSelectedVariant: function( variant ){ + markSelectedVariant: function(){ + var variant = this.getVariant(); var select = document.querySelector( '#select-variant' ); if( !select ){ return; } - if( select.value != variant ){ + this.addCustomVariantOption(); + if( variant && select.value != variant ){ select.value = variant; } // remove selection, because if some uses an arrow navigation" @@ -50,59 +43,153 @@ var variants = { } }, - generateVariantPath( old_path ){ - var variant = this.getVariant(); + generateVariantPath( variant, old_path ){ var new_path = old_path.replace( /^(.*\/theme-).*?(\.css.*)$/, '$1' + variant + '$2' ); return new_path; }, - changeVariant: function( variant ){ - if( this.variants.indexOf( variant ) < 0 ){ - variant = this.variants.length ? this.variants[ 0 ] : ''; + addCustomVariantOption: function(){ + var variantbase = window.localStorage.getItem( 'customvariantbase' ); + if( this.variants.indexOf( variantbase ) < 0 ){ + variantbase = ''; } - this.setVariant( variant ); - if( !variant ){ + if( !window.localStorage.getItem( 'customvariant' ) ){ + variantbase = ''; + } + if( !variantbase ){ return; } - var link = document.querySelector( '#variant-style' ); - if( !link ){ + var select = document.querySelector( '#select-variant' ); + if( !select ){ return; } - var old_path = link.getAttribute( 'href' ); - var new_path = this.generateVariantPath( old_path ); - if( old_path != new_path ){ - link.setAttribute( 'href', new_path ); - this.markSelectedVariant( variant ); + var option = document.querySelector( '#' + this.customvariantname ); + if( !option ){ + option = document.createElement( 'option' ); + option.id = this.customvariantname; + option.value = this.customvariantname; + option.text = this.customvariantname.replace( /-/g, ' ' ).replace(/\w\S*/g, (w) => (w.replace(/^\w/g, (c) => c.toUpperCase()))); + select.appendChild( option ); + document.querySelectorAll( '.footerVariantSwitch' ).forEach( function( e ){ + e.classList.add( 'showVariantSwitch' ); + }); } }, + removeCustomVariantOption: function(){ + var option = document.querySelector( '#' + this.customvariantname ); + if( option ){ + option.remove(); + } + if( this.variants.length <= 1 ){ + document.querySelectorAll( '.footerVariantSwitch' ).forEach( function( e ){ + e.classList.remove( 'showVariantSwitch' ); + }); + } + }, + + saveCustomVariant: function(){ + if( this.getVariant() != this.customvariantname ){ + window.localStorage.setItem( 'customvariantbase', this.getVariant() ); + } + window.localStorage.setItem( 'customvariant', this.generateStylesheet() ); + this.setVariant( this.customvariantname ); + this.markSelectedVariant(); + }, + + loadCustomVariant: function(){ + var stylesheet = window.localStorage.getItem( 'customvariant' ); + + // temp styles to document + var head = document.querySelector( 'head' ); + var style = document.createElement( 'style' ); + style.id = 'custom-variant-style'; + style.appendChild( document.createTextNode( stylesheet ) ); + head.appendChild( style ); + + var interval_id = setInterval( function(){ + if( this.findLoadedStylesheet( 'variant-style' ) ){ + clearInterval( interval_id ); + // save the styles to the current variant stylesheet + this.variantvariables.forEach( function( e ){ + this.changeColor( e.name, true ); + }.bind( this ) ); + + // remove temp styles + style.remove(); + + this.saveCustomVariant(); + } + }.bind( this ), 25 ); + + }, + resetVariant: function(){ + var variantbase = window.localStorage.getItem( 'customvariantbase' ); + if( variantbase && confirm( 'You have made changes to your custom variant. Are you sure you want to reset all changes?' ) ){ + this.removeCustomVariantOption(); + this.changeVariant( variantbase ); + window.localStorage.removeItem( 'customvariantbase' ); + window.localStorage.removeItem( 'customvariant' ); + } + }, + + switchStylesheet: function( variant, without_check ){ var link = document.querySelector( '#variant-style' ); if( !link ){ return; } var old_path = link.getAttribute( 'href' ); - var new_path = this.generateVariantPath( old_path ); + var new_path = this.generateVariantPath( variant, old_path ); link.setAttribute( 'href', new_path ); }, + changeVariant: function( variant ){ + if( variant == this.customvariantname ){ + var variantbase = window.localStorage.getItem( 'customvariantbase' ); + if( this.variants.indexOf( variantbase ) < 0 ){ + variant = ''; + } + if( !window.localStorage.getItem( '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(); + } + }, + generator: function( vargenerator, vardownload, varreset ){ var graphDefinition = this.generateGraph(); - var element = document.querySelector( vargenerator ); - element.innerHTML = graphDefinition; + var graphs = document.querySelectorAll( vargenerator ); + graphs.forEach( function( e ){ e.innerHTML = graphDefinition; }); var interval_id = setInterval( function(){ if( document.querySelectorAll( vargenerator + '.mermaid > svg' ).length ){ clearInterval( interval_id ); this.styleGraph(); } - }.bind( this ), 100 ); + }.bind( this ), 25 ); - var download = document.querySelector( vardownload ); - download.addEventListener('click', this.getStylesheet.bind( this ) ); + var downloads = document.querySelectorAll( vardownload ); + downloads.forEach( function( e ){ e.addEventListener('click', this.getStylesheet.bind( this )); }.bind( this ) ); - var reset = document.querySelector( varreset ); - reset.addEventListener('click', this.resetVariant.bind( this ) ); + var resets = document.querySelectorAll( varreset ); + resets.forEach( function( e ){ e.addEventListener('click', this.resetVariant.bind( this )); }.bind( this ) ); }, download: function(data, mimetype, filename){ @@ -115,7 +202,7 @@ var variants = { }, getStylesheet: function(){ - this.download( this.generateStylesheet(), 'text/css', 'theme-' + this.customvariant + '.css' ); + this.download( this.generateStylesheet(), 'text/css', 'theme-' + this.customvariantname + '.css' ); }, adjustCSSRules: function(selector, props, sheets){ @@ -164,28 +251,33 @@ var variants = { return this.normalizeColor( getComputedStyle( document.documentElement ).getPropertyValue( '--INTERNAL-'+c ) ); }, - changeColor: function( c ){ + findLoadedStylesheet: function( id ){ var style = null; - var variant = this.variant; for( var n = 0; n < document.styleSheets.length; ++n ){ - if( variant = this.parseVariantnameFromFilename( document.styleSheets[n].href ) ){ - var s = document.styleSheets[n]; - for( var m = 0; m < s.rules.length; ++m ){ - if( s.rules[m].selectorText == ':root' ){ - style = s.rules[m].style; - break; + if( document.styleSheets[n].ownerNode.id == id ){ + var s = document.styleSheets[n]; + for( var m = 0; m < s.rules.length; ++m ){ + if( s.rules[m].selectorText == ':root' ){ + style = s.rules[m].style; + break; + } } - } - break; + break; } } - if( !style ){ - alert( 'Theme stylesheet for variant "' + variant + '" not set or found' ); - return; + return style; + }, + + changeColor: function( c, without_prompt ){ + without_prompt = without_prompt || false; + var read_style = this.findLoadedStylesheet( 'custom-variant-style' ); + var write_style = this.findLoadedStylesheet( 'variant-style' ); + if( !read_style ){ + read_style = write_style; } var e = this.findColor( c ); - var p = this.normalizeColor( style.getPropertyValue( '--'+c ) ).replace( '--INTERNAL-', '--' ); + var p = this.normalizeColor( read_style.getPropertyValue( '--'+c ) ).replace( '--INTERNAL-', '--' ); var f = this.getColorValue( e.fallback ); var v = this.getColorValue( e.name ); @@ -196,21 +288,35 @@ var variants = { v = p; } - var t = c + '\n\n' + e.tooltip + '\n'; - if( e.fallback ){ - t += '\nInherits value "' + f + '" from ' + e.fallback + ' if not set\n'; + var n = ''; + if( without_prompt ){ + n = v; } - if( e.default ){ - t += '\nDefaults to value "' + this.normalizeColor(e.default) + '" if not set\n'; + else{ + var t = c + '\n\n' + e.tooltip + '\n'; + if( e.fallback ){ + t += '\nInherits value "' + f + '" from ' + e.fallback + ' if not set\n'; + } + if( e.default ){ + t += '\nDefaults to value "' + this.normalizeColor(e.default) + '" if not set\n'; + } + n = prompt( t, v ); } - var n = prompt( t, v ); if( n ){ n = this.normalizeColor( n ).replace( '--INTERNAL-', '--' ).replace( '--', '--INTERNAL-' ); - style.setProperty( '--'+c, n ); + if( without_prompt || n != v ){ + write_style.setProperty( '--'+c, n ); + } + if( !without_prompt ){ + this.saveCustomVariant(); + } } else if( n !== null){ - style.removeProperty( '--'+c ); + write_style.removeProperty( '--'+c ); + if( !without_prompt ){ + this.saveCustomVariant(); + } } }, @@ -239,7 +345,7 @@ var variants = { generateStylesheet: function(){ var style = - '/* ' + this.customvariant + ' */\n' + + '/* ' + this.customvariantname + ' */\n' + ':root {\n' + this.variantvariables.sort( function( l, r ){ return l.name.localeCompare(r.name); } ).reduce( function( a, e ){ return a + this.generateColorVariable( e ); }.bind( this ), '' ) + '}\n';