ludo / jquery-treetable

jQuery plugin to show a tree structure in a table
http://ludo.cubicphuse.nl/jquery-treetable
GNU General Public License v2.0
739 stars 278 forks source link

Feature req: Trees with tristate checkboxes #195

Open NoseyNick opened 7 years ago

NoseyNick commented 7 years ago

I've combined treetable with https://css-tricks.com/indeterminate-checkboxes/ for one of those trees where you can select/unselect nodes or entire tree branches.

However I did it "on the outside" of jquery-treetable. Would love to see it merged into jquery-treetable itself, with a setting to enable/disable. Will definitely need tidying (EG my remove/wrap/prepend could almost certainly be "done right" if it was inside the real _initialize, and I probably violate all naming conventions and indent styles etc)

Here's my PoC:

$("#tab").treetable({
  expandable: true,
  onNodeInitialized: function () {
    // Create a checkbox per tree node too:
    this.nn_cb = $('<input type="checkbox">').click(this, function(e) {
      $(this).prop('indeterminate', false); // we have set/unset, so no longer indeterminate
      tt_set_kids(e.data, $(this).is(':checked'));
      tt_check_parent(e.data.tree[e.data.parentId]);
    }).attr('id', this.id);

    // if I have a parent, which was checked, I should be checked too!
    if (this.parentId && this.tree[this.parentId]) {
      this.nn_cb.attr('checked', this.tree[this.parentId].nn_cb.is(':checked'));
    }

    // remove the indenter, wrap the label, add back indenter AND CHECKBOX
    this.treeCell
      .remove('span.indenter')
      .wrapInner($('<label>').attr('for', this.id))
      .prepend(this.indenter, this.nn_cb);
  }
});

function tt_set_kids(tt, checked) {
  // User has just set/unset a parent. All kids should be updated to match:
  if (!tt) { return }
  tt.children.forEach(function (kid) {
    if (! kid.nn_cb) { return } // has no checkbox, prob not init'd yet
    kid.nn_cb.prop('checked', checked);
    tt_set_kids(kid, checked);
  });
}

function tt_check_parent(tt) {
  // I have just changed a child. See if this makes parent (and 
  // grandparents etc) set/unset/indeterminate:
  if (! tt) { return }
  var ch = false, un = false;
  tt.children.forEach(function(kid) {
    if      (kid.nn_cb.is(':indeterminate')) { ch = un = true }
    else if (kid.nn_cb.is(':checked'))       { ch      = true }
    else                                     {      un = true }
  });
  // We should be checked if there are NO unchecked (all checked) kids
  // We should be indeterminate if there are checked+unchecked kids:
  tt.nn_cb.prop('checked', !un).prop('indeterminate', ch && un);
  tt_check_parent(tt.tree[tt.parentId]);
}
Tinycocha commented 7 years ago

it‘s good, thanks,