JSFiddle - React, Tailwind, and code Playground

by Haze32

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.14.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.fancytree/2.38.4/jquery.fancytree.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/src/jquery.fancytree.childcounter.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/src/jquery.fancytree.filter.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.contextMenu.min.js"></script>


<h1>Handbook Tree Structure</h1>
<p style="margin-bottom: -5px;"><input type="text" id="filterInput" placeholder="Search by title or HWID..." /><button id="btnResetSearch" disabled="disabled">×</button><span id="matches"></span></p>
<p class="note">Note: Folders greyed out do not contain search object. Folders with regular text contain the object. Bold text match the search object</p>
<fieldset>
    <legend>Options</legend>
    <br>
    <label for="toggleFilterMode">
      <input type="checkbox" id="toggleFilterMode">
      Hide unmatched nodes
    </label>
    <label for="toggleAutoExpand">
      <input type="checkbox" id="toggleAutoExpand">
      Auto expand
    </label>    
  </fieldset>
<div id="tree"></div>

CSS

span.fancytree-node:not(.fancytree-folder) .fancytree-icon { display: none; }
.link-icon { margin-left: 5px; display: inline-block; }
.search-icon { margin-left: 5px; display: inline-block; }
span.fancytree-title i { font-size: 16px; }
.fa > span { font-weight: 400; }
#filterInput { margin: 10px 0 10px 10px; padding: 5px; width: 200px; border-radius: 6px 0 0 6px !important; border: 1px solid #666; height: 18px; }
#filterInput:focus-visible { outline: none; }
button#btnResetSearch { height: 30px; border-radius: 0 6px 6px 0; border: 1px solid #666; width: 28px; }
button#btnResetSearch:not(:disabled) { background: #140be3; color: #fff; cursor: pointer; }
.note { padding-left: 10px; font-size: 12px; color: grey; }

JavaScript

$(document).ready(function () {
  function updateLinkIcon(node) {
    var $nodeSpan = $(node.span)
    $nodeSpan.find(".link-icon").remove()
    $nodeSpan.find(".search-icon").remove()
    $nodeSpan.append(
      "<span class='link-icon'><a href='" +
        node.data.link +
        "' target='_blank'><i class='fa fa-link'></i></a></span>" +
        "<span class='search-icon'><a href='#' data-id='" +
        node.key +
        "'><i class='fa fa-search'></i></a></span>",
    )
    $nodeSpan
      .find("span.search-icon")
      .off("click")
      .on("click", function (e) {
        e.preventDefault() // Prevent default link behavior
        const dataId = $(this).find("a").attr("data-id")
        const input = document.querySelector("#filterInput")
        input.value = dataId
        // Dispatch input event to trigger Fancytree search
        input.dispatchEvent(
          new Event("input", { bubbles: true, cancelable: true }),
        )
      })
  }

  function toggleFilterMode(isChecked) {
    var tree = $.ui.fancytree.getTree("#tree")
    if (!tree || !tree.options.filter) {
      console.error("Tree or filter extension not initialized")
      return
    }
    // Determine new mode: checked = "hide", unchecked = "dimm"
    var newMode =
      isChecked !== undefined
        ? isChecked
          ? "hide"
          : "dimm"
        : tree.options.filter.mode === "dimm"
          ? "hide"
          : "dimm"
    // Update filter mode
    tree.options.filter.mode = newMode
    // Sync checkbox state
    $("#toggleFilterMode").prop("checked", newMode === "hide")
    // Get current search term
    var searchTerm = $("#filterInput").val()
    // Clear current filter
    tree.clearFilter()
    // Reapply filter with new mode if there's a search term
    if (searchTerm) {
      tree.filterNodes(
        function (node) {
          var titleMatch =
            node.title.toLowerCase().indexOf(searchTerm.toLowerCase()) >= 0
          var keyMatch =
           ...