「Shoelace」でツリービュー(TreeView)を作成する方法

「Shoelace」でツリービュー(TreeView)を作成する方法

今回は「Shoelace」の『sl-tree』と『sl-tree-item』タグに紹介です。

タグ「<sl-tree>」

タグ「<Tree Item>」

デモ

導入方法

JSファイルインクルード

「Shoelace」の全機能を利用する必要はなく、ツリービューだけ利用したい場合には、ツリーとツリー項目のJSファイル(tree.js,tree-item.js)だけ読み込んでください。

<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.3.0/dist/components/tree/tree.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.3.0/dist/components/tree-item/tree-item.js"></script>

「Shoelace」の全機能を利用する場合は、以下のファイルを読み込んでください。

<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0/dist/shoelace.js"></script>

CSSファイルインクルード

「Shoelace」のCSSファイルをインクルードします。

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0/dist/themes/light.css" />

HTMLマークアップ

タグ「<sl-tree>」と「<sl-tree-item>」でツリーを定義します。

まず、「<sl-tree>」でツリービューを定義します。

<sl-tree>
  ...
</sl-tree>

次に項目を定義します。

<sl-tree>
  <sl-tree-item>果物</sl-tree-item>
  <sl-tree-item>野菜</sl-tree-item>
  <sl-tree-item>肉類</sl-tree-item>
</sl-tree>

また、複数階層で定義することもできます。

<sl-tree>
  <sl-tree-item>
    果物
    <sl-tree-item>オレンジ</sl-tree-item>
    <sl-tree-item>
      りんご
      <sl-tree-item>ふじ</sl-tree-item>
      <sl-tree-item>ジョナゴールド</sl-tree-item>
      <sl-tree-item>王林</sl-tree-item>
    </sl-tree-item>
    <sl-tree-item value="banana">バナナ</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item>
    野菜
    <sl-tree-item>キャベツ</sl-tree-item>
    <sl-tree-item>レタス</sl-tree-item>
    <sl-tree-item>ほうれん草</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item>
    肉類
    <sl-tree-item>牛肉</sl-tree-item>
    <sl-tree-item>豚肉</sl-tree-item>
    <sl-tree-item>鶏肉</sl-tree-item>
  </sl-tree-item>
</sl-tree>

サンプル

果物 オレンジ りんご ふじ ジョナゴールド 王林 バナナ 野菜 キャベツ レタス ほうれん草 肉類 牛肉 豚肉 鶏肉

単一選択ツリー(single)

サンプル

果物 オレンジ りんご ふじ ジョナゴールド 王林 バナナ 野菜 キャベツ レタス ほうれん草 肉類 牛肉 豚肉 鶏肉 状態を確認

HTMLコード

<sl-tree selection="single">
  <sl-tree-item value="fruit">
    果物
    <sl-tree-item value="orange">オレンジ</sl-tree-item>
    <sl-tree-item value="apple">
      りんご
      <sl-tree-item value="fuji">ふじ</sl-tree-item>
      <sl-tree-item value="Jonagold">ジョナゴールド</sl-tree-item>
      <sl-tree-item value="orin">王林</sl-tree-item>
    </sl-tree-item>
    <sl-tree-item value="banana">バナナ</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item value="vegetable">
    野菜
    <sl-tree-item value="cabbage">キャベツ</sl-tree-item>
    <sl-tree-item value="lettuce">レタス</sl-tree-item>
    <sl-tree-item value="spinach">ほうれん草</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item value="meat">
    肉類
    <sl-tree-item value="beef">牛肉</sl-tree-item>
    <sl-tree-item value="pork">豚肉</sl-tree-item>
    <sl-tree-item value="chicken">鶏肉</sl-tree-item>
  </sl-tree-item>
</sl-tree>
<sl-button size="medium" pill  onclick="checkTree1();">状態を確認</sl-button>
<script>
  function checkTree1() {
    const treeview = document.querySelectorAll('[selection="single"] sl-tree-item');
    for ( var i=0; i < treeview.length; i++ ) {
      const item = treeview[i];
      /* 選択済みのツリー */
      if ( item.selected ) alert(item.innerText);
      if ( item.selected ) alert(item.getAttribute('value'));
    }
  }
</script>

ツリーには三つのモードがあります。

属性「selection」に『single』を指定すると、単一項目しか選択できないツリーになります。なお、『single』がデフォルトです。

選択項目を確認するには?

Javascriptを使えば、選択項目を確認できますます。

<script>
  function checkTree1() {
    const treeview = document.querySelectorAll('[selection="single"] sl-tree-item');
    for ( var i=0; i < treeview.length; i++ ) {
      const item = treeview[i];
      /* 選択済みのツリー */
      if ( item.selected ) alert(item.innerText);
      if ( item.selected ) alert(item.getAttribute('value'));
    }
  }
</script>

属性「value」を設定すれば、表示ラベルとは違う値を設定できます。

複数選択ツリー(multiple)

サンプル

果物 オレンジ りんご ふじ ジョナゴールド 王林 バナナ 野菜 キャベツ レタス ほうれん草 肉類 牛肉 豚肉 鶏肉 選択項目:

HTMLコード

<sl-tree id="treeview2" selection="multiple" >
  <sl-tree-item>
    果物
    <sl-tree-item>オレンジ</sl-tree-item>
    <sl-tree-item>
      りんご
      <sl-tree-item>ふじ</sl-tree-item>
      <sl-tree-item>ジョナゴールド</sl-tree-item>
      <sl-tree-item>王林</sl-tree-item>
    </sl-tree-item>
    <sl-tree-item>バナナ</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item>
    野菜
    <sl-tree-item>キャベツ</sl-tree-item>
    <sl-tree-item>レタス</sl-tree-item>
    <sl-tree-item>ほうれん草</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item>
    肉類
    <sl-tree-item>牛肉</sl-tree-item>
    <sl-tree-item>豚肉</sl-tree-item>
    <sl-tree-item>鶏肉</sl-tree-item>
  </sl-tree-item>
</sl-tree>
<span style="font-weight:bold">選択項目:</span><span id="tree2"></span>
<script>
  const treeview = document.querySelector('[selection="multiple"]');
  /* ツリービューの状態が変わった時のイベント */
  treeview.addEventListener('sl-selection-change', event => {
    var items = event.detail.selection;
    var labels = "";
    for ( var i=0; i < items.length;i++ ) {
      labels = labels + "," + items[i].innerText
    }
    document.getElementById('tree2').innerHTML = labels.substring(1);
  });
</script>

「selection」を『multiple』とすれば、複数の項目を選択状態にすることができるツリーを実装できます。

選択項目変更時のイベント

イベント『sl-selection-change』を使用すれば、項目の選択状態が変わった時、処理を設定することができます。

サンプルでは、選択したとき、または、選択を外した時、その時点で選択されている項目を画面のラベル項目に設定するようにしてあります。

最下位のみ選択可能ツリー(leaf)

サンプル

果物 オレンジ りんご ふじ ジョナゴールド 王林 バナナ 野菜 キャベツ レタス ほうれん草 肉類 牛肉 豚肉 鶏肉

HTMLコード

<sl-tree selection="leaf">
  <sl-tree-item expanded>
    果物
    <sl-tree-item>オレンジ</sl-tree-item>
    <sl-tree-item expanded>
      りんご
      <sl-tree-item>ふじ</sl-tree-item>
      <sl-tree-item selected>ジョナゴールド</sl-tree-item>
      <sl-tree-item>王林</sl-tree-item>
    </sl-tree-item>
    <sl-tree-item>バナナ</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item>
    野菜
    <sl-tree-item>キャベツ</sl-tree-item>
    <sl-tree-item>レタス</sl-tree-item>
    <sl-tree-item>ほうれん草</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item>
    肉類
    <sl-tree-item>牛肉</sl-tree-item>
    <sl-tree-item>豚肉</sl-tree-item>
    <sl-tree-item>鶏肉</sl-tree-item>
  </sl-tree-item>
</sl-tree>

属性「selection」で『leaf』を設定すると、ツリーの末端のみ選択可能なツリーとなります。

開いた状態のツリーにするには?

ツリーを初期表示で開いた状態にするには、属性『expanded』を設定してください。

項目を選択した状態で初期表示させるには?

項目を選択した状態で初期表示させるには、属性『selected』を設定してください。

インデントのライン付ツリー

サンプル

果物 オレンジ りんご ふじ ジョナゴールド 王林 バナナ 野菜 キャベツ レタス ほうれん草 肉類 牛肉 豚肉 鶏肉

HTMLコード

<style>
  .tree-with-lines {
    --indent-guide-width: 1px;
  }
</style>
<sl-tree class="tree-with-lines">
  <sl-tree-item value="fruit">
    果物
    <sl-tree-item value="orange">オレンジ</sl-tree-item>
    <sl-tree-item value="apple">
      りんご
      <sl-tree-item value="fuji">ふじ</sl-tree-item>
      <sl-tree-item value="Jonagold">ジョナゴールド</sl-tree-item>
      <sl-tree-item value="orin">王林</sl-tree-item>
    </sl-tree-item>
    <sl-tree-item value="banana">バナナ</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item value="vegetable">
    野菜
    <sl-tree-item value="cabbage">キャベツ</sl-tree-item>
    <sl-tree-item value="lettuce">レタス</sl-tree-item>
    <sl-tree-item value="spinach">ほうれん草</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item value="meat">
    肉類
    <sl-tree-item value="beef">牛肉</sl-tree-item>
    <sl-tree-item value="pork">豚肉</sl-tree-item>
    <sl-tree-item value="chicken">鶏肉</sl-tree-item>
  </sl-tree-item>
</sl-tree>

CSSのカスタムプロパティ「--indent-guide-width」に1以上の値を設定すると、インデントラインが表示されたツリーになります。

動的生成(Lazy Loading)

サンプル

利用可能ツリービュー

HTMLコード

<sl-tree>
  <sl-tree-item lazy>利用可能ツリービュー</sl-tree-item>
</sl-tree>
<script type="module">
  const lazyItem = document.querySelector('sl-tree-item[lazy]');
  lazyItem.addEventListener('sl-lazy-load', () => {
    // Simulate asynchronous loading
    setTimeout(() => {
      const subItems = ['果物', '野菜', '肉類', '魚類'];
      for (const item of subItems) {
        const treeItem = document.createElement('sl-tree-item');
        treeItem.innerText = item;
        lazyItem.append(treeItem);
      }
      // いったん項目が読み込まれるとlazyモードを無効にします
      lazyItem.lazy = false;
    }, 1000);
  });
</script>

属性『lazy』が指定された項目が選択されたとき実行されるイベント『sl-lazy-load』を使用すると、非同期でツリーを生成することができます。

「開く」「閉じる」アイコン付(回転無)

サンプル

> 果物 オレンジ りんご ふじ ジョナゴールド 王林 バナナ 野菜 キャベツ レタス ほうれん草 肉類 牛肉 豚肉 鶏肉

HTMLコード

<style>
  .custom-icons sl-tree-item::part(expand-button) {
    /* 開く/閉じるのアニメーションを無効化 */
    rotate: none;
  }
</style>
<sl-tree class="custom-icons">>
  <sl-icon name="plus-square" slot="expand-icon"></sl-icon>
  <sl-icon name="dash-square" slot="collapse-icon"></sl-icon>
  <sl-tree-item>
    果物
    <sl-tree-item>オレンジ</sl-tree-item>
    <sl-tree-item>
      りんご
      <sl-tree-item>ふじ</sl-tree-item>
      <sl-tree-item>ジョナゴールド</sl-tree-item>
      <sl-tree-item>王林</sl-tree-item>
    </sl-tree-item>
    <sl-tree-item>バナナ</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item>
    野菜
    <sl-tree-item>キャベツ</sl-tree-item>
    <sl-tree-item>レタス</sl-tree-item>
    <sl-tree-item>ほうれん草</sl-tree-item>
  </sl-tree-item>
  <sl-tree-item>
    肉類
    <sl-tree-item>牛肉</sl-tree-item>
    <sl-tree-item>豚肉</sl-tree-item>
    <sl-tree-item>鶏肉</sl-tree-item>
  </sl-tree-item>
</sl-tree>

<sl-tree>のslot『expand-icon』と『collapse-icon』を設定することで開く/閉じるのアイコンを変えることができます。

アイコンの回転を無効にするには?

<style>
  .custom-icons sl-tree-item::part(expand-button) {
    /* 開く/閉じるのアニメーションを無効化 */
    rotate: none;
  }
</style>

<sl-tree-item>のCSS Partsの定義で回転を無効にすれば、開く/閉じるアイコンの回転を無効化することができます。

アイコン付ツリー

サンプル

ドキュメント 写真 birds.jpg kitten.jpg puppy.jpg 書類 draft.txt final.pdf sales.xlsx

HTMLコード

<sl-tree class="tree-with-icons">
  <sl-tree-item expanded>
    <sl-icon name="folder"></sl-icon>
    ドキュメント
    <sl-tree-item>
      <sl-icon name="folder"></sl-icon>
      写真
      <sl-tree-item>
        <sl-icon name="image"></sl-icon>
        birds.jpg
      </sl-tree-item>
      <sl-tree-item>
        <sl-icon name="image"></sl-icon>
        kitten.jpg
      </sl-tree-item>
      <sl-tree-item>
        <sl-icon name="image"></sl-icon>
        puppy.jpg
      </sl-tree-item>
    </sl-tree-item>
    <sl-tree-item>
      <sl-icon name="folder"></sl-icon>
      書類
      <sl-tree-item>
        <sl-icon name="file"></sl-icon>
        draft.txt
      </sl-tree-item>
      <sl-tree-item>
        <sl-icon name="file-pdf"></sl-icon>
        final.pdf
      </sl-tree-item>
      <sl-tree-item>
        <sl-icon name="file-bar-graph"></sl-icon>
        sales.xlsx
      </sl-tree-item>
    </sl-tree-item>
  </sl-tree-item>
</sl-tree>

<sl-icon>などを利用すれば、アイコン付のツリーを作ることもできます。

ご参考

関連記事