J'ai vu un exemple quelque part en ligne montrant comment personnaliser l'apparence du menu contextuel de jstree (en utilisant le plugin contextmenu).
Par exemple, autoriser mes utilisateurs à supprimer des "documents" mais pas des "dossiers" (en masquant l'option "supprimer" du menu contextuel des dossiers).
Maintenant, je ne trouve pas cet exemple. Est-ce que quelqu'un peut-il me montrer la bonne direction? Le fonctionnaire documentation n'a pas vraiment aidé.
Modifier:
Puisque je veux le menu contextuel par défaut avec seulement un ou deux changements mineurs, je préférerais ne pas recréer tout le menu (bien sûr, je le ferai si c'est le seul moyen). Ce que j'aimerais faire est quelque chose comme ceci:
"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');
}
}
}
}
mais cela ne fonctionne pas - l'élément de création est toujours désactivé (l'alerte n'apparaît jamais).
Le plugin contextmenu
a déjà un support pour cela. À partir de la documentation que vous avez liée à:
items
: Attend un objet ou une fonction, qui devrait renvoyer un objet . Si une fonction est utilisée, elle est déclenchée dans le contexte de l'arborescence et reçoit un argument - le nœud sur lequel un clic droit a été effectué.
Ainsi, plutôt que de donner à contextmenu
un objet codé en dur avec lequel travailler, vous pouvez fournir la fonction suivante. Il vérifie l'élément sur lequel l'utilisateur a cliqué pour une classe nommée "dossier" et supprime l'élément de menu "supprimer" en le supprimant de l'objet:
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;
}
Notez que ce qui précède masque complètement l'option de suppression, mais le plug-in vous permet également d'afficher un élément tout en désactivant son comportement, en ajoutant _disabled: true
à l'élément pertinent. Dans ce cas, vous pouvez utiliser items.deleteItem._disabled = true
dans l'instruction if
.
Cela devrait être évident, mais n'oubliez pas d'initialiser le plugin avec la fonction customMenu
à la place de ce que vous aviez auparavant:
$("#tree").jstree({plugins: ["contextmenu"], contextmenu: {items: customMenu}});
// ^
// ___________________________________________________________________|
Éditer: Si vous ne voulez pas que le menu soit recréé à chaque clic droit, vous pouvez placer la logique dans le gestionnaire d'actions pour l'élément de menu de suppression lui-même.
"label": "Delete",
"action": function (obj) {
if ($(this._get_node(obj)).hasClass("folder") return; // cancel action
}
Edit again: Après avoir examiné le code source de jsTree, il semble que le menu contextuel soit recréé chaque fois qu'il est affiché (voir les fonctions show()
et parse()
), je ne vois donc aucun problème avec mon première solution.
Cependant, j'aime bien la notation que vous suggérez, avec une fonction comme valeur pour _disabled
. Un chemin potentiel à explorer consiste à envelopper leur fonction parse()
avec votre propre fonction qui évalue la fonction à disabled: function () {...}
et stocke le résultat dans _disabled
, avant d'appeler la parse()
d'origine.
Il ne sera pas difficile non plus de modifier directement leur code source. La ligne 2867 de la version 1.0-rc1 est la suivante:
str += "<li class='" + (val._class || "") + (val._disabled ? " jstree-contextmenu-disabled " : "") + "'><ins ";
Vous pouvez simplement ajouter une ligne avant celle-ci qui vérifie $.isFunction(val._disabled)
, et si oui, val._disabled = val._disabled()
. Puis soumettez-le aux créateurs sous forme de patch :)
Implémenté avec différents types de nœuds:
$('#jstree').jstree({
'contextmenu' : {
'items' : customMenu
},
'plugins' : ['contextmenu', 'types'],
'types' : {
'#' : { /* options */ },
'level_1' : { /* options */ },
'level_2' : { /* options */ }
// etc...
}
});
Et la fonction 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;
}
Fonctionne à merveille.
Pour tout effacer.
Au lieu de cela:
$("#xxx").jstree({
'plugins' : 'contextmenu',
'contextmenu' : {
'items' : { ... bla bla bla ...}
}
});
Utilisez ceci:
$("#xxx").jstree({
'plugins' : 'contextmenu',
'contextmenu' : {
'items' : customMenu
}
});
J'ai adapté la solution suggérée pour travailler avec des types un peu différemment, mais peut-être peut-elle aider quelqu'un d'autre:
Où # {$ id_arr [$ k]} est la référence au conteneur div ... Dans mon cas, j’utilise beaucoup d’arbres pour que tout ce code soit envoyé au navigateur, mais vous en avez l’idée. les options du menu contextuel mais uniquement "Créer" et "Coller" sur le nœud Lecteur. Évidemment avec les liaisons correctes à ces opérations plus tard:
<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},
Btw: Si vous voulez juste supprimer des options du menu contextuel existant - cela a fonctionné pour moi:
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;
}
Vous pouvez modifier le code @ Box9 en fonction de vos besoins en matière de désactivation dynamique du menu contextuel, par exemple:
function customMenu(node) {
............
................
// Disable the "delete" menu item
// Original // delete items.deleteItem;
if ( node[0].attributes.yyz.value == 'notdelete' ) {
items.deleteItem._disabled = true;
}
}
Vous devez ajouter un attribut "xyz" dans vos données XML ou JSOn.
à partir de jsTree 3.0.9 j'avais besoin d'utiliser quelque chose comme
var currentNode = treeElem.jstree('get_node', node, true);
if (currentNode.hasClass("folder")) {
// Delete the "delete" menu item
delete items.deleteItem;
}
parce que l'objet node
fourni n'est pas un objet jQuery.