2010-12-30 8 views
72

Ho visto un esempio da qualche parte online che mostra come personalizzare l'aspetto del menu di scelta rapida di jstree con il tasto destro del mouse (usando il plugin del menu di scelta rapida).Configurazione di jstree menu di scelta rapida con clic destro per diversi tipi di nodo

Ad esempio, consentire ai miei utenti di eliminare "documenti" ma non "cartelle" (nascondendo l'opzione "Elimina" dal menu di scelta rapida per le cartelle).

Ora non riesco a trovare quell'esempio. Qualcuno può indicarmi la giusta direzione? L'ufficiale documentation non è stato di grande aiuto.

Edit:

Dal momento che voglio il menu contestuale di default con solo uno o due piccole modifiche, preferirei non ricreare l'intero menu (anche se ovviamente lo farò se è l'unico modo). Quello che mi piacerebbe fare è qualcosa di simile:

"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'); 
      } 
     } 
    } 
} 

ma non funziona - la voce è solo creare sempre disabilitata (l'avviso non compare mai).

risposta

120

Il plug-in contextmenu ha già il supporto per questo. Dalla documentazione si è collegato al:

items: aspetta un una funzione, che dovrebbe restituire un oggetto oggetto o . Se viene utilizzata una funzione, viene attivata nel contesto dell'albero e riceve un argomento: il nodo su cui è stato fatto clic con il pulsante destro del mouse.

Quindi piuttosto che dare a contextmenu un oggetto con hard-coded con cui lavorare, è possibile fornire la seguente funzione. Esso controlla l'elemento che è stato cliccato per una classe denominata "cartella", e rimuove la voce di menu "delete" eliminandolo dall'oggetto:

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; 
} 

Si noti che quanto sopra si nasconde completamente l'opzione di eliminazione, ma il plugin consente inoltre di mostrare un elemento disabilitando il suo comportamento, aggiungendo _disabled: true all'elemento pertinente. In questo caso, è possibile utilizzare items.deleteItem._disabled = true all'interno dell'istruzione if.

dovrebbe essere ovvio, ma ricordatevi di inizializzare il plugin con la funzione, invece di quello che aveva in precedenza customMenu:

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

Edit: Se non si desidera che il menu per essere ricreato ogni volta che si fa clic con il tasto destro, è possibile inserire la logica nel gestore di azioni per la voce di menu Elimina stessa.

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

Edit di nuovo: Dopo aver guardato il codice sorgente jsTree, sembra che il menu contestuale che viene ricreata ogni volta che viene mostrato in ogni caso (vedi le show() e parse() funzioni), così ho non vedo un problema con la mia prima soluzione.

Tuttavia, mi piace la notazione che si sta suggerendo, con una funzione come valore per _disabled.Un potenziale percorso da esplorare è quello di avvolgere la propria funzione parse() con la propria che valuta la funzione allo disabled: function() {...} e memorizza il risultato in _disabled, prima di chiamare l'originale parse().

Non sarà difficile né modificare direttamente il codice sorgente. Linea 2867 della versione 1.0-rc1 è quella corrispondente:

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

si può semplicemente aggiungere una linea prima di questo che controlla $.isFunction(val._disabled), e in caso affermativo, val._disabled = val._disabled(). Quindi invialo ai creatori come patch :)

+0

Grazie. Ho pensato che una volta ho visto una soluzione che comportava solo la modifica di ciò che era necessario per passare dall'impostazione predefinita (piuttosto che ricreare l'intero menu da zero). Accetterò questa risposta se non ci sarà una soluzione migliore prima della scadenza della taglia. – MGOwen

+0

@ MGOwen, concettualmente * sto * modificando il "valore predefinito", ma sì hai ragione che l'oggetto viene ricreato ogni volta che viene chiamata la funzione. Tuttavia, l'impostazione predefinita deve essere prima clonata, altrimenti il ​​valore predefinito stesso viene modificato (e avrete bisogno di una logica più complessa per ripristinare lo stato originale). Un'alternativa che mi viene in mente è quella di spostare gli elementi '' 'var '' all'esterno della funzione in modo che siano creati una sola volta e restituire una selezione di elementi dalla funzione, ad es. 'return {renameItem: items.renameItem};' o 'return {renameItem: items.renameItem, deleteItem: items.deleteItem};' –

+0

Mi piace particolarmente quest'ultimo, in cui si modifica la sorgente di jstree. L'ho provato e funziona, la funzione assegnata a "_disabled" (nel mio esempio) viene eseguita.Ma non aiuta perché non riesco ad accedere al nodo (ho almeno bisogno del suo attributo rel per filtrare i nodi per tipo di nodo) all'interno dell'ambito della funzione. Ho provato a controllare le variabili che potevo passare dal codice sorgente di jstree ma non ho trovato il nodo. Qualche idea? – MGOwen

12

Per cancellare tutto.

Invece di questo:

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

Utilizzare questa:

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

È possibile modificare @ codice Box9 da soddisfare la vostra esigenza di disabilitazione dinamica del menu di contesto come:

function customMenu(node) { 

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


     items.deleteItem._disabled = true; 
    } 

} 

È necessario aggiungere un attributo "xyz" nei dati XML o JSOn

4

ho adattato la soluzione suggerita per lavorare con tipi un po 'diverso, però, forse può aiutare qualcun altro:

Dove # {$ id_arr [$ k]} è il riferimento al contenitore div ... in Nel mio caso uso molti alberi, quindi tutto questo codice sarà l'output del browser, ma tu hai l'idea .. Fondamentalmente voglio tutte le opzioni del menu contestuale ma solo "Crea" e "Incolla" sul nodo Drive. Ovviamente con le associazioni corrette per quelle operazioni più tardi:

<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}, 
0

come di jsTree 3.0.9 avevo bisogno di usare qualcosa come

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

perché l'oggetto node che viene fornito non è un oggetto jQuery.

16

implementato con diversi tipi di nodi:

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

E la funzione 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; 
} 

funziona a meraviglia.

+1

Preferisco questa risposta poiché si basa sull'attributo 'type' piuttosto che su una classe CSS ottenuta usando jQuery. –

+0

Quale codice stai inserendo in "action": function() {/ * action * /} 'nel secondo snippet? Che cosa succede se si desidera utilizzare la funzionalità "normale" e le voci di menu, ma è sufficiente rimuoverne uno (ad esempio rimuovere Elimina ma mantenere Rinomina e Crea)? A mio avviso, è proprio quello che l'OP chiedeva comunque. Sicuramente non hai bisogno di riscrivere le funzionalità per cose come Rinomina e Crea se rimuovi un altro elemento come Elimina? – Andy

+0

Non sono sicuro di aver capito la tua domanda. Stai definendo tutte le funzionalità per il menu di scelta rapida completo (ad es. Elimina, Rinomina e Crea) nell'elenco degli oggetti 'items', quindi specifichi quale di questi elementi rimuovere per un determinato' node.type' alla fine della funzione 'customMenu'. Quando l'utente fa clic su un nodo di un determinato 'tipo', il menu di scelta rapida elenca tutti gli elementi meno quelli rimossi nel condizionale alla fine della funzione' customMenu'. Non stai riscrivendo alcuna funzionalità (a meno che il jstree non sia cambiato da questa risposta tre anni fa, nel qual caso potrebbe non essere più rilevante). – stacked