I have a previous question which has a solution for paging child nodes.
This works well, but has caused an issue with the Select2 list for finding nodes of d.type == 'learning_event'
.
The Select2 shows the nodes, but those that are paged in the tree do not show in the Select2.
Instead the name of the paging nodes, '...', displays instead of the names of the paged nodes.
See fiddle
Code in the fiddle:
//===============================================
function learningEventCollection(d) {
if (d.children)
d.children.forEach(learningEventCollection);
else if (d._children)
d._children.forEach(learningEventCollection);
if (!d.children && d.type == 'learning_event') learningEventData.push(d.name);
}
//===============================================
function searchTree(d) {
if (d.children)
d.children.forEach(searchTree);
else if (d._children)
d._children.forEach(searchTree);
var searchFieldValue = eval(searchField);
if (searchFieldValue && searchFieldValue.toLowerCase().match(searchText.toLowerCase())) {
// Walk parent chain
var ancestors = [];
var parent = d;
while (typeof(parent) !== "undefined") {
ancestors.push(parent);
//console.log(parent);
parent.class = "found";
parent = parent.parent;
}
//console.log(ancestors);
}
}
//===============================================
function clearAll(d) {
d.class = "";
if (d.children)
d.children.forEach(clearAll);
else if (d._children)
d._children.forEach(clearAll);
}
//===============================================
function collapse(d) {
if (d.children) {
d._children = d.children;
//set the parent object in all the children
d._children.forEach(function(d1) {
d1.parent = d;
collapse(d1);
});
d.children = null;
}
}
//===============================================
function collapseAllNotFound(d) {
if (d.children) {
if (d.class !== "found") {
d._children = d.children;
d._children.forEach(collapseAllNotFound);
d.children = null;
} else
d.children.forEach(collapseAllNotFound);
}
}
//===============================================
function expandAll(d) {
if (d._children) {
d.children = d._children;
d.children.forEach(expandAll);
d._children = null;
} else if (d.children)
d.children.forEach(expandAll);
}
//===============================================
// Toggle children on click.
function toggle(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
clearAll(root);
update(d);
$("#search").select2("val", "");
}
//===============================================
$("#search").on("select2-selecting", function(e) {
clearAll(root);
expandAll(root);
update(root);
searchField = "d.name";
searchText = e.object.text;
searchTree(root);
root.children.forEach(collapseAllNotFound);
update(root);
})
var colourScale = d3.scale.ordinal()
.domain(["Program", "ProgramGroup1", "ProgramGroup2"])
.range(["#abacab", "#b67a4e", "#5a6fbb"])
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = 2000 - margin.right - margin.left,
height = 500 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = treeData;
root.x0 = height / 2;
root.y0 = 0;
function pageNodes(d, options) {
if (d.children) {
d.children.forEach(c => pageNodes(c, options));
if (options.page && options.page(d) && d.children.length > options.maxNode) {
d.pages = {}
const count = options.maxNode - 2;
const l = Math.ceil(d.children.length / count);
for (let i = 0; i < l; i++) {
const startRange = i * count;
const endRange = i * count + count;
let pageNumber = i == 0 ? l - 1 : i - 1;
d.pages[i] = d.children.slice(startRange, endRange);
d.pages[i].unshift({
...d.pages[i][0],
data: {
name: options.getLabel ? options.getLabel(pageNumber) : "..."
},
pageNumber,
name: "..."
})
// console.log(i, d.pages[i]);
pageNumber = i != (l - 1) ? i + 1 : 0;
d.pages[i].push({
...d.pages[i][0],
data: {
name: options.getLabel ? options.getLabel(pageNumber) : "..."
},
pageNumber,
name: "..."
});
}
d.children = d.pages[0];
console.log(d.pages)
}
}
}
root.children.forEach(c => pageNodes(c, {
maxNode: 8,
page: function(d) {
return d.type == "unit_group";
}
}));
learningEventData = [];
learningEventCollection(root);
learningEventDataObject = [];
learningEventData.sort(function(a, b) {
if (a > b) return 1; // sort
if (a < b) return -1;
return 0;
})
.filter(function(item, i, ar) {
return ar.indexOf(item) === i;
}) // remove duplicate items
.filter(function(item, i, ar) {
learningEventDataObject.push({
"id": i,
"text": item
});
});
$("#search").select2({
placeholder: "Select a Learning Event...",
data: learningEventDataObject,
containerCssClass: "search"
});
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
//svg.style("height", "500px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 180;
});
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.attr('stroke', function(d) {
return d.color ? d.color : 'blue';
})
.style("fill", function(d) {
return d._children ? "#ccc" : "#fff";
});
nodeEnter.append("text")
.attr("x", function(d) {
return d.children || d._children ? -13 : 13;
})
.attr("dy", ".35em")
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
return d.name;
})
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeUpdate.select("circle")
.attr("r", 6.5)
.attr("fill-opacity", "0.7")
.attr("stroke-opacity", "1")
.style("fill", function(d) {
if (d.class !== "found") {
if(d.type == "learning_event") return "#6eb7e3";
if(d.type == "assessment") return "#4ecc44";
}
if (d.class === "found") {
return "#ff4136"; //red
} else {
return (typeof d._children !== 'undefined') ? (colourScale(findParent(d))) : '#FFF';
}
})
.style("stroke", function(d) {
if (d.class !== "found") {
if(d.type == "learning_event") return "#6eb7e3";
if(d.type == "assessment") return "#4ecc44";
if(d.name.match(/Learning Events/)) return "#6eb7e3";
if(d.name.match(/Assessments/)) return "#4ecc44";
}
if (d.class === "found") {
return "#ff4136"; //red
} else {
return colourScale(findParent(d));
}
});
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {
x: source.x,
y: source.y
};
return diagonal({
source: o,
target: o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
function findParent(datum) {
if (datum.depth < 2) {
return datum.name
} else {
return findParent(datum.parent)
}
}
function findParentLinks(datum) {
if (datum.target.depth < 2) {
return datum.target.name
} else {
return findParent(datum.target.parent)
}
}
// Toggle children on click.
function click(d) {
if (d.hasOwnProperty('pageNumber')) {
d.parent.children = d.parent.pages[d.pageNumber];
} else if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
type
was being assigned learning_event
as other nodes in the same level so I changed the type
to page-toggle-node
.Select2
I have modified it in option-group for all the nodes and adjusted the search functionality accordinglymatch
to compare text which would fail for the case when LE1
is compared to LE15
it would return true as LE15
contains LE1
.var treeData = {"name":"Program","column_to_sort_by":null,"type":"program","children":[{"name":"ProgramGroup1","column_to_sort_by":null,"type":"program_group","children":[{"name":"POGroup1","column_to_sort_by":null,"type":"1program_outcome_group","children":[{"name":"PO1","column_to_sort_by":null,"type":"program_outcome","children":[{"name":"Unit1","column_to_sort_by":"Unit1","children":[{"name":"UG1-LE","column_to_sort_by":null,"type":"unit_group","children":[{"name":"LE1","column_to_sort_by":"LE1","type":"learning_event"},{"name":"LE10","column_to_sort_by":"LE10","type":"learning_event"},{"name":"LE11","column_to_sort_by":"LE11","type":"learning_event"},{"name":"LE12","column_to_sort_by":"LE12","type":"learning_event"},{"name":"LE13","column_to_sort_by":"LE13","type":"learning_event"},{"name":"LE14","column_to_sort_by":"LE14","type":"learning_event"},{"name":"LE15","column_to_sort_by":"LE15","type":"learning_event"},{"name":"LE2","column_to_sort_by":"LE2","type":"learning_event"},{"name":"LE4","column_to_sort_by":"LE4","type":"learning_event"},{"name":"LE5","column_to_sort_by":"LE5","type":"learning_event"},{"name":"LE6","column_to_sort_by":"LE6","type":"learning_event"},{"name":"LE7","column_to_sort_by":"LE7","type":"learning_event"},{"name":"LE8","column_to_sort_by":"LE8","type":"learning_event"},{"name":"LE9","column_to_sort_by":"LE9","type":"learning_event"}]},{"name":"UG1-Assessments","column_to_sort_by":null,"type":"unit_group","children":[{"name":"ASST1","column_to_sort_by":"ASST1","type":"assessment"},{"name":"ASST10","column_to_sort_by":"ASST10","type":"assessment"},{"name":"ASST11","column_to_sort_by":"ASST11","type":"assessment"},{"name":"ASST13","column_to_sort_by":"ASST13","type":"assessment"},{"name":"ASST14","column_to_sort_by":"ASST14","type":"assessment"},{"name":"ASST15","column_to_sort_by":"ASST15","type":"assessment"},{"name":"ASST2","column_to_sort_by":"ASST2","type":"assessment"},{"name":"ASST3","column_to_sort_by":"ASST3","type":"assessment"},{"name":"ASST4","column_to_sort_by":"ASST4","type":"assessment"},{"name":"ASST5","column_to_sort_by":"ASST5","type":"assessment"},{"name":"ASST6","column_to_sort_by":"ASST6","type":"assessment"},{"name":"ASST7","column_to_sort_by":"ASST7","type":"assessment"},{"name":"ASST8","column_to_sort_by":"ASST8","type":"assessment"},{"name":"ASST9","column_to_sort_by":"ASST9","type":"assessment"}]}],"type":"unit"}]},{"name":"PO2","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO3","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO4","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO5","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO6","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO7","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO8","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO9","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO10","column_to_sort_by":null,"type":"program_outcome"},{"name":"PO11","column_to_sort_by":null,"type":"program_outcome"}]},{"name":"POGroup2","column_to_sort_by":null,"type":"1program_outcome_group"}]},{"name":"ProgramGroup2","column_to_sort_by":null,"type":"program_group"}]};
//===============================================
function searchTree(d) {
if (d.pages) {
const pageKeys = Object.keys(d.pages)
Object.keys(d.pages).forEach(page => {
let currentNodes = d.pages[page];
currentNodes.forEach((node, idx) => {
if (node.name.toLowerCase() == searchText.toLowerCase()) {
currentNodes[idx]['parent'] = d.children[0].parent;
d.children = currentNodes;
}
})
});
}
if (d.children)
d.children.forEach(searchTree);
else if (d._children)
d._children.forEach(searchTree);
var searchFieldValue = eval(searchField);
if (searchFieldValue && searchFieldValue.toLowerCase().match(searchText.toLowerCase())) {
// Walk parent chain
var ancestors = [];
var parent = d;
while (typeof(parent) !== "undefined") {
ancestors.push(parent);
//console.https://jsfiddle.net/qyn5fd0t/#runlog(parent);
parent.class = "found";
parent = parent.parent;
}
// console.log(ancestors);
}
}
//===============================================
function clearAll(d) {
d.class = "";
if (d.children)
d.children.forEach(clearAll);
else if (d._children)
d._children.forEach(clearAll);
}
//===============================================
function collapse(d) {
if (d.children) {
d._children = d.children;
//set the parent object in all the children
d._children.forEach(function(d1) {
d1.parent = d;
collapse(d1);
});
d.children = null;
}
}
//===============================================
function collapseAllNotFound(d) {
if (d.children) {
if (d.class !== "found") {
d._children = d.children;
d._children.forEach(collapseAllNotFound);
d.children = null;
} else
d.children.forEach(collapseAllNotFound);
}
}
//===============================================
function expandAll(d) {
if (d._children) {
d.children = d._children;
d.children.forEach(expandAll);
d._children = null;
} else if (d.children)
d.children.forEach(expandAll);
}
//===============================================
// Toggle children on click.
function toggle(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
clearAll(root);
update(d);
$("#search").select2("val", "");
}
//===============================================
$("#search").on("select2-selecting", function(e) {
clearAll(root);
expandAll(root);
update(root);
searchField = "d.name";
searchText = e.object.text;
searchTree(root);
root.children.forEach(collapseAllNotFound);
update(root);
})
var colourScale = d3.scale.ordinal()
.domain(["Program", "ProgramGroup1", "ProgramGroup2"])
.range(["#abacab", "#b67a4e", "#5a6fbb"])
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = 2000 - margin.right - margin.left,
height = 500 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = treeData;
root.x0 = height / 2;
root.y0 = 0;
function pageNodes(d, options) {
if (d.children) {
d.children.forEach(c => pageNodes(c, options));
if (options.isPageable && options.isPageable(d) && d.children.length > options.maxNode) {
d.pages = {}
const count = options.maxNode - 2;
const l = Math.ceil(d.children.length / count);
for (let i = 0; i < l; i++) {
const startRange = i * count;
const endRange = i * count + count;
let pageNumber = i == 0 ? l - 1 : i - 1;
d.pages[i] = d.children.slice(startRange, endRange);
options.page && options.page(d.pages[i]);
d.pages[i].unshift({
...d.pages[i][0],
data: {
name: options.getLabel ? options.getLabel(pageNumber) : "..."
},
pageNumber,
name: "...",
type: "page-toggle-node"
})
// console.log(i, d.pages[i]);
pageNumber = i != (l - 1) ? i + 1 : 0;
d.pages[i].push({
...d.pages[i][0],
data: {
name: options.getLabel ? options.getLabel(pageNumber) : "..."
},
pageNumber,
name: "...",
type: "page-toggle-node"
});
}
d.children = d.pages[0];
}
}
}
//===============================================
const groupLearningOptions = [];
let groupLearningOptionsCounter = 0;
root.children.forEach(c => pageNodes(c, {
maxNode: 8,
isPageable: function(d) {
return d.type == "unit_group";
},
page: function(nodes) {
if (nodes[0].type == "learning_event") {
groupLearningOptions.push({
text: `Group ${groupLearningOptions.length + 1}`,
children: nodes.map(node => ({
id: groupLearningOptionsCounter++,
text: node.name
}))
.sort((a, b) => (a.text > b.text) ? 1 : -1)
})
}
}
}));
// console.log(groupLearningOptions);
$("#search").select2({
placeholder: "Select a Learning Event...",
data: {
results: groupLearningOptions
},
containerCssClass: "search"
});
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
//svg.style("height", "500px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 180;
});
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.attr('stroke', function(d) {
return d.color ? d.color : 'blue';
})
.style("fill", function(d) {
return d._children ? "#ccc" : "#fff";
});
nodeEnter.append("text")
.attr("x", function(d) {
return d.children || d._children ? -13 : 13;
})
.attr("dy", ".35em")
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
return d.name;
})
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeUpdate.select("circle")
.attr("r", 6.5)
.attr("fill-opacity", "0.7")
.attr("stroke-opacity", "1")
.style("fill", function(d) {
if (d.class !== "found") {
if (d.type == "learning_event") return "#6eb7e3";
if (d.type == "assessment") return "#4ecc44";
}
if (d.class === "found") {
return "#ff4136"; //red
} else {
return (typeof d._children !== 'undefined') ? (colourScale(findParent(d))) : '#FFF';
}
})
.style("stroke", function(d) {
if (d.class !== "found") {
if (d.type == "learning_event") return "#6eb7e3";
if (d.type == "assessment") return "#4ecc44";
if (d.name.match(/Learning Events/)) return "#6eb7e3";
if (d.name.match(/Assessments/)) return "#4ecc44";
}
if (d.class === "found") {
return "#ff4136"; //red
} else {
return colourScale(findParent(d));
}
});
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {
x: source.x,
y: source.y
};
return diagonal({
source: o,
target: o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
function findParent(datum) {
if (datum.depth < 2) {
return datum.name
} else {
return findParent(datum.parent)
}
}
function findParentLinks(datum) {
if (datum.target.depth < 2) {
return datum.target.name
} else {
return findParent(datum.target.parent)
}
}
// Toggle children on click.
function click(d) {
if (d.hasOwnProperty('pageNumber')) {
d.parent.children = d.parent.pages[d.pageNumber];
} else if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
Here working example
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments