mermaid-js / mermaid

Generation of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown
https://mermaid.js.org
MIT License
72.73k stars 6.63k forks source link

Reinitialize with new theme #1945

Open Unbinilium opened 3 years ago

Unbinilium commented 3 years ago

How to reinitialize with new theme?

Hi folks, I want mermaidjs rerender diagrams when I switched color-scheme, such as switching form 'dark' to 'light', I tried use mermaid.mermaidAPI.reinitialize() with different theme configurations, but it seems not working.

I see that it keep the theme as same as the first initial, when I changed the theme value, called reinitialize(newConfig), remove last rendered graphs and rerendered the new diagrams, it shows nothing different with the theme rendered before.

Is that I was misunderstood the reinitialize() function usage or there is something wrong with my code?

Thanks.

<pre class="mermaid-container">
  <pre id="mermaid-src-0" hidden>
    graph TD;
      A-->B;
      A-->C;
      B-->D;
      C-->D;
  </pre>
</pre>
var mermaidConfig = {
    startOnLoad: false,
    theme: 'neutral',
    logLevel: 'debug',
    fontFamily: '"Menlo", "Meslo LG", monospace',
    er: {
        minEntityWidth: 100,
        minEntityHeight: 55,
        entityPadding: 15
    },
    sequence: {
        width: 130,
        height: 30
    },
    gantt: {
        barHeight: 25,
        barGap: 4
    }
};

async function mermaidRender(mermaidSrc, mermaidOutId) {
    mermaid.mermaidAPI.render(mermaidOutId, mermaidSrc.textContent, async function (svgCode) {
        mermaidSrc.insertAdjacentHTML('afterend', svgCode);
    });
}

async function mermaidLoaded() {
    if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
        mermaidConfig['theme'] = 'dark';
    }
    mermaid.mermaidAPI.initialize(mermaidConfig);
    var mermaidContainer = document.getElementsByClassName('mermaid-container');
    for (var i = 0; i != mermaidContainer.length; ++i) {
        var mermaidSrc = mermaidContainer[i].firstElementChild;
        var mermaidOutId = mermaidSrc.id.replace('src', 'out');

        mermaidRender(mermaidSrc, mermaidOutId);
    }
}

async function mermaidReloaded() {
    mermaidConfig['theme'] = 'neutral';

    mermaid.mermaidAPI.reinitialize(mermaidConfig);
    var mermaidContainer = document.getElementsByClassName('mermaid-container');
    for (var i = 0; i != mermaidContainer.length; ++i) {
        var mermaidSrc = mermaidContainer[i].firstElementChild;
        var mermaidOutId = mermaidSrc.id.replace('src', 'out');

        mermaidContainer[i].querySelector('[id^=mermaid-out-]').remove();

        mermaidRender(mermaidSrc, mermaidOutId);
    }
}

if (document.getElementsByClassName('mermaid-container').length != 0) {
    var script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js';
    script.setAttribute('onload', 'mermaidLoaded();')
    document.head.appendChild(script);
}

window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', mermaidReloaded);
Unbinilium commented 3 years ago

I noticed that the reinitialize() function was commented like this in v8.9.2:

function reinitialize() {
  // `mermaidAPI.reinitialize: v${pkg.version}`,
  //   JSON.stringify(options),
  //   options.themeVariables.primaryColor;
  // // if (options.theme && theme[options.theme]) {
  // //   options.themeVariables = theme[options.theme].getThemeVariables(options.themeVariables);
  // // }
  // // Set default options
  // const config =
  //   typeof options === 'object' ? configApi.setConfig(options) : configApi.getSiteConfig();
  // updateRendererConfigs(config);
  // setLogLevel(config.logLevel);
  // log.debug('mermaidAPI.reinitialize: ', config);
}

And for initialize(options) function, it seems only the theme changes can not be applied, I am not sure whether the color changes is applied to render new svgs.

...
if (options && options.theme && theme[options.theme]) {
    // Todo merge with user options
    options.themeVariables = theme[options.theme].getThemeVariables(options.themeVariables);
  } else {
    if (options) options.themeVariables = theme.default.getThemeVariables(options.themeVariables);
  }
...
deoxykev commented 3 years ago

Same issue with me too...

matthiasg commented 2 years ago

Yes this is definitely required

tekstrand commented 2 years ago

+1

appotry commented 2 years ago

I want this too!!!

esotericman commented 2 years ago

+1

kinachan commented 2 years ago

+1

barockok commented 1 year ago

I managed to have a workaround for the reinitialize. by persisting the original content first, then initialize mermaid afterward. whenever the site theme (light/dark) it will reset the data-processed since mermaid has initialized before, and overide the content with the original content, then initialize new mermaid.

whenever the theme variable change in my site, I emit an event (dark-theme-set/light-theme-set), and register callback to initialize mermaid.

you can check the live site here

(function(window){
'use strict'

  const elementCode = '.language-mermaid'
  const loadMermaid = function(theme) {
    window.mermaid.initialize({theme})
    window.mermaid.init({theme}, document.querySelectorAll(elementCode))
  }
  const saveOriginalData = function(){
    return new Promise((resolve, reject) => {
      try {
        var els = document.querySelectorAll(elementCode),
            count = els.length;
        els.forEach(element => {
          element.setAttribute('data-original-code', element.innerHTML)
          count--
          if(count == 0){
            resolve()
          }
        });
      } catch (error) {
       reject(error) 
      }
    })
  }
  const resetProcessed = function(){
    return new Promise((resolve, reject) => {
      try {
        var els = document.querySelectorAll(elementCode),
            count = els.length;
        els.forEach(element => {
          if(element.getAttribute('data-original-code') != null){
            element.removeAttribute('data-processed')
            element.innerHTML = element.getAttribute('data-original-code')
          }
          count--
          if(count == 0){
            resolve()
          }
        });
      } catch (error) {
       reject(error) 
      }
    })
  } 

  const init = ()=>{
    saveOriginalData()
    .catch( console.error )
    document.body.addEventListener('dark-theme-set', ()=>{
      resetProcessed()
      .then(loadMermaid('dark'))
      .catch(console.error)
    })
    document.body.addEventListener('light-theme-set', ()=>{
      resetProcessed()
      .then(loadMermaid('default'))
      .catch(console.error)
    })
  }
  window.initMermaid = init
})(window);
giovanniberti commented 7 months ago

I managed to have a workaround for the reinitialize. by persisting the original content first, then initialize mermaid afterward. whenever the site theme (light/dark) it will reset the data-processed since mermaid has initialized before, and overide the content with the original content, then initialize new mermaid.

whenever the theme variable change in my site, I emit an event (dark-theme-set/light-theme-set), and register callback to initialize mermaid.

you can check the live site here

(function(window){
'use strict'

  const elementCode = '.language-mermaid'
  const loadMermaid = function(theme) {
    window.mermaid.initialize({theme})
    window.mermaid.init({theme}, document.querySelectorAll(elementCode))
  }
  const saveOriginalData = function(){
    return new Promise((resolve, reject) => {
      try {
        var els = document.querySelectorAll(elementCode),
            count = els.length;
        els.forEach(element => {
          element.setAttribute('data-original-code', element.innerHTML)
          count--
          if(count == 0){
            resolve()
          }
        });
      } catch (error) {
       reject(error) 
      }
    })
  }
  const resetProcessed = function(){
    return new Promise((resolve, reject) => {
      try {
        var els = document.querySelectorAll(elementCode),
            count = els.length;
        els.forEach(element => {
          if(element.getAttribute('data-original-code') != null){
            element.removeAttribute('data-processed')
            element.innerHTML = element.getAttribute('data-original-code')
          }
          count--
          if(count == 0){
            resolve()
          }
        });
      } catch (error) {
       reject(error) 
      }
    })
  } 

  const init = ()=>{
    saveOriginalData()
    .catch( console.error )
    document.body.addEventListener('dark-theme-set', ()=>{
      resetProcessed()
      .then(loadMermaid('dark'))
      .catch(console.error)
    })
    document.body.addEventListener('light-theme-set', ()=>{
      resetProcessed()
      .then(loadMermaid('default'))
      .catch(console.error)
    })
  }
  window.initMermaid = init
})(window);

Fix to make this work (as of 2024-04-25): replace

const elementCode = '.language-mermaid'

with

const elementCode = '.mermaid'