mar10/fancytree

View on GitHub
src/jquery.fancytree.columnview.js

Summary

Maintainability
C
7 hrs
Test Coverage
/*!
 * jquery.fancytree.columnview.js
 *
 * Render tree like a Mac Finder's column view.
 * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
 *
 * Copyright (c) 2008-2023, Martin Wendt (https://wwWendt.de)
 *
 * Released under the MIT license
 * https://github.com/mar10/fancytree/wiki/LicenseInfo
 *
 * @version @VERSION
 * @date @DATE
 */

(function (factory) {
    if (typeof define === "function" && define.amd) {
        // AMD. Register as an anonymous module.
        define(["jquery", "./jquery.fancytree"], factory);
    } else if (typeof module === "object" && module.exports) {
        // Node/CommonJS
        require("./jquery.fancytree");
        module.exports = factory(require("jquery"));
    } else {
        // Browser globals
        factory(jQuery);
    }
})(function ($) {
    "use strict";

    /*******************************************************************************
     * Private functions and variables
     */
    var _assert = $.ui.fancytree.assert,
        FT = $.ui.fancytree;

    /*******************************************************************************
     * Private functions and variables
     */
    $.ui.fancytree.registerExtension({
        name: "columnview",
        version: "@VERSION",
        // Default options for this extension.
        options: {},
        // Overide virtual methods for this extension.
        // `this`       : is this extension object
        // `this._base` : the Fancytree instance
        // `this._super`: the virtual function that was overriden (member of prev. extension or Fancytree)
        treeInit: function (ctx) {
            var $tdFirst,
                $ul,
                tree = ctx.tree,
                $table = tree.widget.element;

            tree.tr = $("tbody tr", $table)[0];
            tree.$tdList = $(">td", tree.tr);
            tree.columnCount = tree.$tdList.length;
            // Perform default behavior
            this._superApply(arguments);
            // Standard Fancytree created a root <ul>. Now move this into first table cell
            $ul = $(tree.rootNode.ul);
            $tdFirst = tree.$tdList.eq(0);

            _assert(
                $.inArray("table", this.options.extensions) < 0,
                "columnview extensions must not use ext-table"
            );
            _assert(
                tree.columnCount >= 2,
                "columnview target must be a table with at least two columns"
            );

            $ul.removeClass("fancytree-container").removeAttr("tabindex");
            tree.$container = $table;
            $table
                .addClass("fancytree-container fancytree-ext-columnview")
                .attr("tabindex", "0");

            $tdFirst.empty();
            $ul.detach().appendTo($tdFirst);

            // Force some required options
            tree.widget.options.autoCollapse = true;
            // tree.widget.options.autoActivate = true;
            tree.widget.options.toggleEffect = false;
            tree.widget.options.clickFolderMode = 1;

            $table
                // Make sure that only active path is expanded when a node is activated:
                .on("fancytreeactivate", function (event, data) {
                    var node = data.node,
                        tree = data.tree,
                        level = node.getLevel();

                    tree._callHook("nodeCollapseSiblings", node);
                    // Clear right neighbours
                    if (!node.expanded) {
                        tree.$tdList.eq(level).nextAll().empty();
                    }
                    // Expand nodes on activate, so we populate the right neighbor cell
                    if (!node.expanded && (node.children || node.lazy)) {
                        node.setExpanded();
                    }
                })
                // Adjust keyboard behaviour:
                .on("fancytreekeydown", function (event, data) {
                    var next = null,
                        handled = true,
                        node = data.node || data.tree.getFirstChild();

                    if (node.getLevel() >= tree.columnCount) {
                        return;
                    }
                    switch (FT.eventToString(event)) {
                        case "down":
                            next = node.getNextSibling();
                            break;
                        case "left":
                            if (!node.isTopLevel()) {
                                next = node.getParent();
                            }
                            break;
                        case "right":
                            next = node.getFirstChild();
                            if (!next) {
                                // default processing: expand or ignore
                                return;
                            }
                            // Prefer an expanded child if any
                            next.visitSiblings(function (n) {
                                if (n.expanded) {
                                    next = n;
                                    return false;
                                }
                            }, true);
                            break;
                        case "up":
                            next = node.getPrevSibling();
                            break;
                        default:
                            handled = false;
                    }
                    if (next) {
                        next.setActive();
                    }
                    return !handled;
                });
        },
        nodeSetExpanded: function (ctx, flag, callOpts) {
            var $wait,
                node = ctx.node,
                tree = ctx.tree,
                level = node.getLevel();

            if (flag !== false && !node.expanded && node.isUndefined()) {
                $wait = $(
                    "<span class='fancytree-icon fancytree-icon-loading'>"
                );
                tree.$tdList.eq(level).empty().append($wait);
            }
            return this._superApply(arguments);
        },
        nodeRemoveChildren: function (ctx) {
            // #899: node's children removed: remove child marker...
            $(ctx.node.span).find("span.fancytree-cv-right").remove();
            // ...and clear right columns
            ctx.tree.$tdList.eq(ctx.node.getLevel()).nextAll().empty();
            return this._superApply(arguments);
        },
        nodeRender: function (ctx, force, deep, collapsed, _recursive) {
            // Render standard nested <ul> - <li> hierarchy
            this._super(ctx, force, deep, collapsed, _recursive);
            // Remove expander and add a trailing triangle instead
            var level,
                $tdChild,
                $ul,
                tree = ctx.tree,
                node = ctx.node,
                $span = $(node.span);

            $span.find("span.fancytree-expander").remove();
            if (
                node.hasChildren() !== false &&
                !$span.find("span.fancytree-cv-right").length
            ) {
                $span.append(
                    $("<span class='fancytree-icon fancytree-cv-right'>")
                );
            }
            // Move <ul> with children into the appropriate <td>
            if (node.ul && node.expanded) {
                node.ul.style.display = ""; // might be hidden if RIGHT was pressed
                level = node.getLevel();
                if (level < tree.columnCount) {
                    // only if we are not in the last column
                    $tdChild = tree.$tdList.eq(level);
                    $ul = $(node.ul).detach();
                    $tdChild.empty().append($ul);
                }
            }
        },
    });
    // Value returned by `require('jquery.fancytree..')`
    return $.ui.fancytree;
}); // End of closure