Настройка контекстного меню jstree правой кнопкой мыши для разных типов узлов - PullRequest
79 голосов
/ 30 декабря 2010

Я видел где-то в сети пример, показывающий, как настроить внешний вид контекстного меню правой кнопкой мыши jstree (с помощью плагина contextmenu).

Например, разрешить моим пользователям удалять «документы», но не «папки» (скрывая опцию «удалить» из контекстного меню для папок).

Теперь я не могу найти этот пример. Может кто-то указать мне верное направление? Официальная документация не очень помогла.

Edit:

Поскольку мне нужно контекстное меню по умолчанию, содержащее только одно или два незначительных изменения, я бы предпочел не создавать заново все меню (хотя, конечно, я сделаю это, если это единственный способ). Я хотел бы сделать что-то вроде этого:

"contextmenu" : {
    items: {
        "ccp" : false,
        "create" : {
            // The item label
            "label" : "Create",
            // The function to execute upon a click
            "action": function (obj) { this.create(obj); },
            "_disabled": function (obj) { 
                alert("obj=" + obj); 
                return "default" != obj.attr('rel'); 
            }
        }
    }
}

но это не работает - элемент создания просто всегда отключен (предупреждение никогда не появляется).

Ответы [ 7 ]

134 голосов
/ 05 января 2011

Плагин contextmenu уже поддерживает это. Из документации, на которую вы ссылаетесь:

items: Ожидается объект или функция, которая должна возвращать объект . Если функция используется, она запускается в контексте дерева и получает один аргумент - узел, по которому щелкнули правой кнопкой мыши.

Таким образом, вместо того, чтобы дать contextmenu жестко запрограммированный объект для работы, вы можете предоставить следующую функцию. Он проверяет элемент, который был выбран для класса с именем «папка», и удаляет пункт меню «удалить», удаляя его из объекта:

function customMenu(node) {
    // The default set of all items
    var items = {
        renameItem: { // The "rename" menu item
            label: "Rename",
            action: function () {...}
        },
        deleteItem: { // The "delete" menu item
            label: "Delete",
            action: function () {...}
        }
    };

    if ($(node).hasClass("folder")) {
        // Delete the "delete" menu item
        delete items.deleteItem;
    }

    return items;
}

Обратите внимание, что приведенное выше полностью скрывает параметр удаления, но плагин также позволяет отображать элемент при отключении его поведения, добавляя _disabled: true к соответствующему элементу. В этом случае вместо оператора if можно использовать items.deleteItem._disabled = true.

Должно быть очевидным, но не забудьте инициализировать плагин с функцией customMenu вместо того, что вы имели ранее:

$("#tree").jstree({plugins: ["contextmenu"], contextmenu: {items: customMenu}});
//                                                                    ^
// ___________________________________________________________________|

Редактировать: Если вы не хотите, чтобы меню создавалось при каждом щелчке правой кнопкой мыши, вы можете поместить логику в обработчик действий для самого элемента меню удаления.

"label": "Delete",
"action": function (obj) {
    if ($(this._get_node(obj)).hasClass("folder") return; // cancel action
}

Отредактируйте снова: После просмотра исходного кода jsTree, похоже, что контекстное меню создается заново каждый раз, когда оно все равно отображается (см. Функции show() и parse()), поэтому Я не вижу проблемы с моим первым решением.

Однако мне нравятся обозначения, которые вы предлагаете, с функцией в качестве значения для _disabled. Потенциальный путь для изучения - обернуть их функцию parse() своей собственной, которая оценивает функцию в disabled: function () {...} и сохраняет результат в _disabled, перед вызовом оригинального parse().

Также не составит труда изменить их исходный код напрямую. Соответствует строка 2867 версии 1.0-rc1:

str += "<li class='" + (val._class || "") + (val._disabled ? " jstree-contextmenu-disabled " : "") + "'><ins ";

Вы можете просто добавить строку перед этой, которая проверяет $.isFunction(val._disabled), и если да, val._disabled = val._disabled(). Затем отправьте его создателям в виде патча:)

18 голосов
/ 27 февраля 2015

Реализовано с различными типами узлов:

$('#jstree').jstree({
    'contextmenu' : {
        'items' : customMenu
    },
    'plugins' : ['contextmenu', 'types'],
    'types' : {
        '#' : { /* options */ },
        'level_1' : { /* options */ },
        'level_2' : { /* options */ }
        // etc...
    }
});

И функция customMenu:

function customMenu(node)
{
    var items = {
        'item1' : {
            'label' : 'item1',
            'action' : function () { /* action */ }
        },
        'item2' : {
            'label' : 'item2',
            'action' : function () { /* action */ }
        }
    }

    if (node.type === 'level_1') {
        delete items.item2;
    } else if (node.type === 'level_2') {
        delete items.item1;
    }

    return items;
}

Работает красиво.

12 голосов
/ 11 мая 2011

Чтобы очистить все.

Вместо этого:

$("#xxx").jstree({
    'plugins' : 'contextmenu',
    'contextmenu' : {
        'items' : { ... bla bla bla ...}
    }
});

Используйте это:

$("#xxx").jstree({
    'plugins' : 'contextmenu',
    'contextmenu' : {
        'items' : customMenu
    }
});
5 голосов
/ 17 июня 2012

Я адаптировал предлагаемое решение для работы с типами немного по-другому, хотя, возможно, оно может помочь кому-то еще:

Где # {$ id_arr [$ k]} - ссылка на контейнер div ... в моем случае я использую много деревьев, поэтому весь этот код будет выводиться в браузер, но вы поймете, что идея. Мне нужны все опции контекстного меню, но только «Создать» и «Вставить» на диске. Очевидно, с правильными привязками к этим операциям позже:

<div id="$id_arr[$k]" class="jstree_container"></div>
</div>
</li>
<!-- JavaScript neccessary for this tree : {$value} -->
<script type="text/javascript" >
jQuery.noConflict();
jQuery(function ($) {
// This is for the context menu to bind with operations on the right clicked node
function customMenu(node) {
    // The default set of all items
    var control;
    var items = {
        createItem: {
            label: "Create",
            action: function (node) { return { createItem: this.create(node) }; }
        },
        renameItem: {
            label: "Rename",
            action: function (node) { return { renameItem: this.rename(node) }; }
        },
        deleteItem: {
            label: "Delete",
            action: function (node) { return { deleteItem: this.remove(node) }; },
            "separator_after": true
        },
        copyItem: {
            label: "Copy",
            action: function (node) { $(node).addClass("copy"); return { copyItem: this.copy(node) }; }
        },
        cutItem: {
            label: "Cut",
            action: function (node) { $(node).addClass("cut"); return { cutItem: this.cut(node) }; }
        },
        pasteItem: {
            label: "Paste",
            action: function (node) { $(node).addClass("paste"); return { pasteItem: this.paste(node) }; }
        }
    };

    // We go over all the selected items as the context menu only takes action on the one that is right clicked
    $.jstree._reference("#{$id_arr[$k]}").get_selected(false, true).each(function (index, element) {
        if ($(element).attr("id") != $(node).attr("id")) {
            // Let's deselect all nodes that are unrelated to the context menu -- selected but are not the one right clicked
            $("#{$id_arr[$k]}").jstree("deselect_node", '#' + $(element).attr("id"));
        }
    });

    //if any previous click has the class for copy or cut
    $("#{$id_arr[$k]}").find("li").each(function (index, element) {
        if ($(element) != $(node)) {
            if ($(element).hasClass("copy") || $(element).hasClass("cut")) control = 1;
        }
        else if ($(node).hasClass("cut") || $(node).hasClass("copy")) {
            control = 0;
        }
    });

    //only remove the class for cut or copy if the current operation is to paste
    if ($(node).hasClass("paste")) {
        control = 0;
        // Let's loop through all elements and try to find if the paste operation was done already
        $("#{$id_arr[$k]}").find("li").each(function (index, element) {
            if ($(element).hasClass("copy")) $(this).removeClass("copy");
            if ($(element).hasClass("cut")) $(this).removeClass("cut");
            if ($(element).hasClass("paste")) $(this).removeClass("paste");
        });
    }
    switch (control) {
        //Remove the paste item from the context menu
        case 0:
            switch ($(node).attr("rel")) {
                case "drive":
                    delete items.renameItem;
                    delete items.deleteItem;
                    delete items.cutItem;
                    delete items.copyItem;
                    delete items.pasteItem;
                    break;
                case "default":
                    delete items.pasteItem;
                    break;
            }
            break;
            //Remove the paste item from the context menu only on the node that has either copy or cut added class
        case 1:
            if ($(node).hasClass("cut") || $(node).hasClass("copy")) {
                switch ($(node).attr("rel")) {
                    case "drive":
                        delete items.renameItem;
                        delete items.deleteItem;
                        delete items.cutItem;
                        delete items.copyItem;
                        delete items.pasteItem;
                        break;
                    case "default":
                        delete items.pasteItem;
                        break;
                }
            }
            else //Re-enable it on the clicked node that does not have the cut or copy class
            {
                switch ($(node).attr("rel")) {
                    case "drive":
                        delete items.renameItem;
                        delete items.deleteItem;
                        delete items.cutItem;
                        delete items.copyItem;
                        break;
                }
            }
            break;

            //initial state don't show the paste option on any node
        default: switch ($(node).attr("rel")) {
            case "drive":
                delete items.renameItem;
                delete items.deleteItem;
                delete items.cutItem;
                delete items.copyItem;
                delete items.pasteItem;
                break;
            case "default":
                delete items.pasteItem;
                break;
        }
            break;
    }
    return items;
$("#{$id_arr[$k]}").jstree({
  // List of active plugins used
  "plugins" : [ "themes","json_data", "ui", "crrm" , "hotkeys" , "types" , "dnd", "contextmenu"],
  "contextmenu" : { "items" : customMenu  , "select_node": true},
2 голосов
/ 01 ноября 2018

Кстати: если вы просто хотите удалить опции из существующего контекстного меню - это работает для меня:

function customMenu(node)
{
    var items = $.jstree.defaults.contextmenu.items(node);

    if (node.type === 'root') {
        delete items.create;
        delete items.rename;
        delete items.remove;
        delete items.ccp;
    }

    return items;
}
1 голос
/ 13 февраля 2015

по состоянию на jsTree 3.0.9 Мне нужно было использовать что-то вроде

var currentNode = treeElem.jstree('get_node', node, true);
if (currentNode.hasClass("folder")) {
    // Delete the "delete" menu item
    delete items.deleteItem;
}

, поскольку предоставленный объект node не является объектом jQuery.

1 голос
/ 22 октября 2011

Вы можете изменить код @ Box9 в соответствии с требованиями динамического отключения контекстного меню следующим образом:

function customMenu(node) {

  ............
  ................
   // Disable  the "delete" menu item  
   // Original // delete items.deleteItem; 
   if ( node[0].attributes.yyz.value == 'notdelete'  ) {


       items.deleteItem._disabled = true;
    }   

}  

Вам необходимо добавить один атрибут "xyz" в данные XML или JSOn

...