Can't this be achieved by adding a handler to the nodeChecking event of Treeview? It does not visually disable the checkboxes, but it allows you to prevent a checkbox from being checked by canceling the event.
I use this myself to prevent the user to select more than a maximum number/less than a minimum number of nodes by canceling the event when the constraint is violated, though you have to iterate child nodes if a container node is checked: ( checked : "columnVisible", hasChildren : "field" )
Where I use a counter as constraint you could modify it to use a different one (for example, the presence of a css class on the node)
function nodeChecking(args) {
let count = this.treeData.filter(n => !n.hasChildren && this.checkedNodes.includes(n.id.toString())).length;
const nodeId = args.data[0].id;
const colInfo = this.fields.dataSource.find(c => c.id.toString() === nodeId);
switch (args.action) {
case "check":
{
const toCheck = [];
if (!colInfo.field) {
const containers = [nodeId];
do { // use a post-check loop so we can check if there were more to check than allowed or it was just enough
const current = containers[containers.length - 1];
for (const item of this.fields.dataSource) {
//for (const item of this.getTreeData()) {
if (item.parentId != current) continue; // need coercion: current is string. parentId can be Number, null or undefined (so can't call .toString() on it)
if (item.field) {
if (item.columnVisible) continue;
count++;
if (count > maxVisible)
break;
if (!this.getNode(current).expanded)
this.expandAll([current], 1); // nodes need to be rendered for the nodeChecked event to fire
toCheck.push(item.id.toString());
} else
containers.push(item.id);
}
containers.pop();
} while (containers.length && count <= maxVisible) // if count > max we know not all desired nodes will checked
this.checkAll(toCheck);
} else {
if (this.checkedNodes.includes(nodeId))
return; // for some reason the event gets fired twice for one of the nodes that are checked ???
if (++count <= maxVisible) {
toCheck.push(nodeId);
}
}
if (count >= maxVisible) {
// if maximum has been reached, disable all unchecked nodes, except the ones included in toCheck
this.disableNodes(this.getTreeData().filter(c => c.columnVisible === false && !toCheck.includes(c.id))
.map(n => n.id));
if (count > maxVisible) {
// attempt was made to select more nodes than allowed => cancel event (in case of a container select, we check the max amount of child nodes allowed ourselves)
args.cancel = true;
}
}
break;
}
case "uncheck":
// we need to determine how many checkboxes will remain checked after the operation manually to ensure at least the minimum amount is preserved.
if (!colInfo.field) {
const toUncheck = [];
const containers = [nodeId];
const items = this.fields.dataSource;
do {
const current = containers[containers.length - 1];
// by iterating backwards, an attempt to clear the selected fields will preserve the topmost minimum items in the list.
for (let i = items.length - 1; i >= 0; i--) {
const item = items[i];
if (item.parentId != current) continue; // need coercion: current is string. parentId can be Number, null or undefined (so can't call .toString() on it)
if (item.field) {
if (!item.columnVisible) continue;
count--;
if (count < minVisible)
break;
toUncheck.push(item.id.toString());
} else
containers.push(item.id);
}
containers.pop();
} while (containers.length && count >= minVisible)
if (count < minVisible)
this.uncheckAll(toUncheck); // this container was the only one with any checked boxes in it. Uncheck all except the topmost minimum
} else {
if (this.checkedNodes.includes(nodeId)) // exclude duplicate events
count--;
}
if (count < minVisible) {
args.cancel = true; // cancel if an attempt was made to uncheck more than allowed
}
}
}
I hope this helps as a workaround, and if not, I guess it didn't harm sharing an idea.