Redmine小ネタ:Redmin v5系で「View Customize Plugin」を用いてサイドバーの表示・非表示を実現する方法について

Redmineをお使いの皆さん、Redmineのサイドバーはどのように非表示にされていますでしょうか? 本記事ではRedmine v5系で私が採用した「View Customize Plugin」を用いたサイドバーの非表示方法をご紹介します。

私はRedmine v3系を使用していた頃は、「View Customize Plugin」を入れて、 以下のページで紹介されているスクリプトを使用させて頂いておりました。

blog.enjoyxstudy.com

時は経ち、Redmine v5.0.5にバージョンアップすると、上記の方法だと思った通りに動作しなくなりました。

そこで調べると「redmine_hide_sidebar」というプラグインがあるということで導入をしました。 しかしこれはこれで、チケットなどの一覧で行が多いと一覧の下の方の行で右クリックメニューが表示されないという不具合がありました。 Redmineの運用者が一覧画面で大量のチケットをチェックして処理するという作業をしていて、この問題が発覚しました。

そこで、色々とネットを検索して、「View Customize Plugin」を使用して以下のスクリプトをつくりました。 元ネタはどこかのサイトなのですが、わからなくなってしまいました。すいません。

以下の機能が動作します。

  • サイドバーの表示・非表示
  • 画面が下にスクロールした時に、サイドバーの表示・非表示のボタンも一緒に上下に動く
  • 画面が下にスクロールした時に、サイドバーメニューも一緒に上下に動く
  • Cookieにサイドバー状態を保存し、次回ブラウザ表示時に最後の状態(サイドバー表示 or 非表示)を再現

ただ、何故か画面が下にスクロールした状態で、サイドバーの表示・非表示をすると、画面が一旦一番上まで移動してしまうので、元の表示場所に戻るようにしています。
その点がかっこ悪い所です。。。

$(function() {


  // トグルボタン
  const toggleButton = $('<a id="sidebar_button" href="#">&raquo;</a>')
    .css({
      'display': 'block',
      'font-size': '20px'
    });

  // トグルボタンを配置するdivタグ
  const wrapper = $('<div></div>')
    .append(toggleButton)
    .css({
      'float': 'left',
      'margin-left': '-17px',
    });

  // サイドバーのトップにトグルボタンを配置
  const sidebar = $('#sidebar').prepend(wrapper);

  // Open/Closeのフラグ
  let isOpen = true;


  // サイドバーの上部の位置を取得
  let sidebarBaseOffsetTop = sidebar.offset().top;
  // ウィンドウのスクロール量を取得
  let lastWindowScrollTop = $(window).scrollTop();

  // サイドバーの状態をクッキーにセット
  function setCookie(c_name, value, exdays) {
    let exdate = new Date();
    exdate.setDate(exdate.getDate() + exdays);

    let c_value = encodeURIComponent(value) + ((exdays == null) ? "" : "; expires=" + exdate.toUTCString());
    document.cookie = c_name + "=" + c_value + ";path=/";
  }

  // 以前のサイドバーの状態をクッキーから取得。
  function getCookie(c_name) {
    let i;
    let x;
    let y;
    let cookies = document.cookie.split(";");
    for (i = 0; i < cookies.length; i++) {
      x = cookies[i].substring(0, cookies[i].indexOf("="));
      y = cookies[i].substring(cookies[i].indexOf("=") + 1);
      x = x.replace(/^\s+|\s+$/g, "");
      if (x == c_name) {
        return decodeURIComponent(y);
      }
    }
  }

  // サイドバーの初期状態を設定
  function initSidebarState(){

    // クッキーを取得
    value = getCookie('original_sidebar_hide');

    // サイドバーの初期状態を設定
    if (value == 'open'){
      isOpen = false;
    } else if (value == 'close') {
      isOpen = true;
    }

  }


  // クッキーを取得してサイドバーの初期状態をセット
  initSidebarState();

  //-------------------
  // トグルボタンを押したときの動作
  //-------------------
  const toggle = function() {
    isOpen = !isOpen;

    let currentWindowScrollTop = lastWindowScrollTop;

    if (isOpen) {
      // close -> open
      toggleButton.html('&raquo;');

      sidebar.css({
        'width': '',
        'padding-right': ''
      });


      wrapper.nextAll().show(function() {
        $(window).scrollTop(currentWindowScrollTop);
      });

      // クッキーセット
      setCookie('original_sidebar_hide', 'open', 100);

    } else {
      // open -> close
      toggleButton.html('&laquo;');

      sidebar.css({
        'width': '0',
        'padding-right': '0'
      });


      wrapper.nextAll().hide(function() {
        $(window).scrollTop(currentWindowScrollTop);
      });

      // クッキーセット
      setCookie('original_sidebar_hide', 'close', 100);

    }
    
  }

  toggleButton.on('click', toggle);

  // default close
  toggle();


  //-------------------
  // 上下位置調整
  //-------------------
  // スクロールした時のアクション
  const onScroll = function() {

    // ウィンドウのスクロール量を取得
    var windowScrollTop = $(window).scrollTop();
    lastWindowScrollTop = windowScrollTop;


    if (windowScrollTop > sidebarBaseOffsetTop) {
      // スクロール量が、サイドバーの上部の位置よりも下の場合

      // 今のサイドバーのマージントップを取得
      let sidebarMerginTop = parseInt(sidebar.css('margin-top').replace(/px$/,'') || '0');
      // ウィンドウ上部の位置を取得
      let adjustMerginTop = windowScrollTop - sidebarBaseOffsetTop;

      // サイドバーをウィンドウ上部の位置に移動
      sidebar.css('margin-top', adjustMerginTop + 'px');

    } else {
      // スクロール量が、サイドバーの上部の位置よりも上の場合

      // サイドバーのマージントップを0にする。
      sidebar.css('margin-top','');
    }

  }

  $(window).scroll(onScroll);
  onScroll();

});

以下のような動きになります。

本記事は以上となります。
どなたかのお役に立てば幸いです。