0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitBucket Markdown Enhanced Plugin で目次を右側に固定配置するように修正しました

Posted at

概要

GitBucket用のプラグイン GitBucket Markdown Enhanced Pluginを開発しています。

GitBucket Markdown Enhanced Plugin は、GitBucket 標準のマークダウンレンダリングエンジンを置き換えるプラグインです。

目標は、Visual Studio CodeMarkdown Preview Enhanced 向けの markdown ファイルを軽易に Web で共有できる環境です。

これまで [TOC] という記法で生成した目次は、本文内に配置されていましたが、右側に独立させて固定配置するように修正し、バージョン 0.6.2 としてリリースしました。

issues や Pull Requests で使用した場合は、これまでどおり、本文に配置されます。
(必要ないですよね?)
また、複数箇所に [TOC] を記述した場合は、最初の目次のみ、右側に配置します。

前回の記事

Before After

これまで [TOC] という記法で生成した目次は、次の画像のように本文内に配置されていました。

before.png

今後は、次の画像のように本文の右側に固定配置されるようになります。

after.png

なお、ファイルリストが表示されている時など邪魔にならないようスクロール時に縦位置を調整するようにしています。

before_scroll.png

after_scroll.png

また、右上の Table of Contents というタイトルをクリックすると目次を折りたためます。

close_details.png

以下、ソースの修正について、解説します。

TocExtension の設定を変更し目次にクラスを付与

flexmark-javaTable of Contents Extension(TocExtension) の LIST_CLASS オプションを設定し、目次が格納される <ul> タグにクラスを付与しました。

src/main/scala/io/github/yasumichi/gme/MarkdownEnhancedRenderer.scala に以下の行を追加しています。

src/main/scala/io/github/yasumichi/gme/MarkdownEnhancedRenderer.scala
    options.set(TocExtension.LIST_CLASS, "toc")

目次のスタイル変更を行う JavaScript 関数の追加

src/main/resources/gme/assets/gme.js に以下の関数を追加しました。

src/main/resources/gme/assets/gme.js
    var updateTocStyle = function() {
        const toc = document.querySelector('.toc');
        if (toc) {
            const parent = toc.parentElement;
            const details = document.createElement('details');
            const summary = document.createElement('summary')
            if (!document.location.toString().includes("/wiki/")) {
                toc.style.height = `${document.documentElement.clientHeight * 0.7}px`
                toc.style.overflowY = "scroll";
            }
            details.classList.add("toc-wrapper");
            details.open = true;
            summary.textContent = "Table of contents"
            summary.classList.add("toc-summary");
            parent.insertBefore(details, toc);
            details.appendChild(summary);
            details.appendChild(toc);
            var parentY = parent.getClientRects()[0].y;
            details.style.position = "fixed";
            if (document.location.toString().includes("/wiki/")) {
                details.style.right = "270px";
            } else {
                details.style.right = "20px";
            }
            if (parentY > 0) {
                details.style.top = `${Math.floor(parentY)+2}px`;
            } else {
                details.style.top = "0px";
            }
            document.addEventListener("scrollend", (event) => {
                var parentY = parent.getClientRects()[0].y;
                if (parentY > 0) {
                    details.style.top = `${Math.floor(parentY)+2}px`;
                } else {
                    details.style.top = "0px";
                }
            });
        }
    }

順番に説明していきます。

src/main/resources/gme/assets/gme.js
        const toc = document.querySelector('.toc');
        if (toc) {
          // 中略
        }

class 属性が、toc に設定されているノードを取得します。

取得できた場合、if ブロック内を実行します。

src/main/resources/gme/assets/gme.js
            const parent = toc.parentElement;
            const details = document.createElement('details');
            const summary = document.createElement('summary')
  • toc の親ノードを parent に格納
  • 新たに <details> タグを作成し、 details に格納
  • 新たに <summary> タグを作成し、 summary に格納
src/main/resources/gme/assets/gme.js
            if (!document.location.toString().includes("/wiki/")) {
                toc.style.height = `${document.documentElement.clientHeight * 0.7}px`
                toc.style.overflowY = "scroll";
            }

wiki でないことを確認し、以下の処理を行います。

src/main/resources/gme/assets/gme.js
            details.classList.add("toc-wrapper");
            details.open = true;
            summary.textContent = "Table of contents"
            summary.classList.add("toc-summary");
            parent.insertBefore(details, toc);
            details.appendChild(summary);
            details.appendChild(toc);

以下の処理をしています。

src/main/resources/gme/assets/gme.js
            var parentY = parent.getClientRects()[0].y;
            details.style.position = "fixed";
            if (document.location.toString().includes("/wiki/")) {
                details.style.right = "270px";
            } else {
                details.style.right = "20px";
            }
            if (parentY > 0) {
                details.style.top = `${Math.floor(parentY)+2}px`;
            } else {
                details.style.top = "0px";
            }
  • 目次の元親要素の縦位置を取得
  • <details> タグの positionfixed
    設定
  • wiki かどうかにより分岐
    • wiki の場合、<details> タグの right270px に設定(カスタムサイドバーとの干渉を回避)
    • wiki でない場合、<details> タグの right20px に設定
  • 目次の元親要素の縦位置が 0 より大きいかで分岐
    • 0 より大きい場合、親要素の縦位置から計算し、<details> タグの top を計算
    • 0 以下の場合は、<details> タグの top を 0 に設定
src/main/resources/gme/assets/gme.js
            document.addEventListener("scrollend", (event) => {
                var parentY = parent.getClientRects()[0].y;
                if (parentY > 0) {
                    details.style.top = `${Math.floor(parentY)+2}px`;
                } else {
                    details.style.top = "0px";
                }
            });

documentscrollend イベントで目次の元親要素の縦位置から、<details> タグの top を変更します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?