Public
Edited
Oct 17, 2023
1 fork
Insert cell
Insert cell
employeePerSegment = 20
Insert cell
ySpacing = radius * 30
Insert cell
tValues = d3.range(0, 1, 0.05)
Insert cell
radius = width * 0.019
Insert cell
chart = {
let employees;
let employeeNav;
const h = 700;
let faggruppe;
const fgIds = [40, 31]; // Ought to be dynamic range
const fills = ["blue", "red"];

const svg = d3
.create("svg")
.attr("width", w)
.attr("height", h)
.style("background", "#f4f4f5");

const t = svg.transition().duration(1000);

// Faggruppe filter
const alleAnsatte = svg.append("rect");
const design = svg.append("rect");
const teknologer = svg.append("rect");
const faggruppePadding = width * 0.08;

alleAnsatte
.style("fill", "black") // rect's fill color
.attr("height", 20) // rect's height (in pixels)
.attr("width", 20) // rect's width (in pixels)
.attr("x", width - faggruppePadding) // x position of the top-left corner
.attr("y", 10) // y position of the top-left corner
.on("click", showAllEmployees);

design
.data([{ fgId: fgIds[0] }])
.style("fill", "blue") // rect's fill color
.attr("height", 20) // rect's height (in pixels)
.attr("width", 20) // rect's width (in pixels)
.attr("x", width - faggruppePadding) // x position of the top-left corner
.attr("y", 40) // y position of the top-left corner
.on("click", filterFaggruppe);

teknologer
.data([{ fgId: fgIds[1] }])
.style("fill", "red") // rect's fill color
.attr("height", 20) // rect's height (in pixels)
.attr("width", 20) // rect's width (in pixels)
.attr("x", width - faggruppePadding) // x position of the top-left corner
.attr("y", 70) // y position of the top-left corner
.on("click", filterFaggruppe);

showAllEmployees();

return svg.node();

// ALL
function showAllEmployees() {
// const steps = 3;
// console.log(
// d3.range(steps).map(function (num) {
// return (num / steps) * (2 * Math.PI);
// })
// );
const t = svg.transition().duration(1000);
employees = svg
.selectAll("g")
.data(flatData, (d) => d.empId) //
.join(
(enter) =>
enter
.append("g")
.on("click", showSelected)
.attr("class", "circle-group")
.style("opacity", 0)
.style("fill", "none")
.attr("transform", (d) => `translate(${d.t.x},${d.t.y})`)

// Circle
// .append("circle")
// .attr("cx", 0)
// .attr("cy", 0)
// .attr("r", radius * 1.2)
// .attr("stroke-width", 2)
// .attr("stroke", (d) => (d.fgId === 40 ? "blue" : "red"))

.call((g) => drawProfilePic(g))

.call((g) => g.transition(t).style("opacity", 1)),

(update) =>
update

// .call((g) => g.select("circle").transition(t).attr("r", radius))
//.call((g) => g.transition(t).attr("r", radius))
.call((g) =>
g
.transition(t)
// .style("fill", "black")
.attr("transform", (d) => `translate(${d.t.x},${d.t.y})`)
),
(exit) => exit.remove()
)
.on("click", showSelected);
return employees;
}

// FAGGRUPPE SELECTION
function filterFaggruppe(d, e) {
const t = svg.transition().duration(700);
const faggruppe = e.fgId;

const filteredData = flatData.filter((emp) => emp.fgId === faggruppe);
console.log("filtered data", filteredData);
employees = svg.selectAll("g");

employees
.data(filteredData, (d) => d.empId)
.join(
(enter) =>
enter
.append("g")
.attr("class", "circle-group")
.style("opacity", 0)
// .style("fill", fills[fgIds.indexOf(faggruppe)])
// Re-append circle to G
// .call((g) =>g.append("circle").attr("cx", 0).attr("cy", 0).attr("r", radius))
.call((g) => drawProfilePic(g))
// //Add transition to G
.call((g) => g.transition(t).style("opacity", 1))
.attr(
"transform",
(d, i) =>
`translate(${radius * 1.5 + radius * 2.2 * (i % 10)},${
radius * 1.5 + Math.floor(i / 10) * (radius * 2.2)
})`
)
.on("click", showSelected),
(update) =>
update
.call((g) =>
g
.transition(t)
.style("opacity", 1)
// .style("fill", fills[fgIds.indexOf(faggruppe)])
.attr(
"transform",
(d, i) =>
`translate(${radius * 1.5 + radius * 2.2 * (i % 10)},${
radius * 1.5 + Math.floor(i / 10) * (radius * 2.2)
})`
)
) // Update circe
.call((g) => g.selectAll("circle").transition(t).attr("r", radius)),
(exit) => exit.remove()
);
}

// SELECTED
function showSelected(d, e) {
const filteredData = flatData.filter(
(employee) => employee.empId === e.empId
);

const t = svg.transition().duration(1000);

employees = svg
.selectAll("g")
.data(filteredData, (d) => d.empId)
.join(
(enter) => enter,
(update) =>
update
// .call((g) =>

// .attr("cy", 0)
// .attr("r", radius)
.transition(t)
//.attr("transform", (d) => `scale(2)`)
.attr("transform", (d) => `translate(${w / 2},${h / 2}) scale(4)`),

//.call(
//(g) => g.transition(t).attr("transform", (d) => `scale(4)`)
// (g) =>
// g
// .transition(t)
// .attr("transform", (d) => `translate(${w / 2},${h / 2})`)
//)
//.style("fill", "green")
// .call((g) => g.select("circle").transition(t).attr("r", 70)),
// .call((g) => g.transition(t).attr("r", 70))
//.on("click", faggruppe ? filterFaggruppe : showAllEmployees),
(exit) => exit.transition().duration(400).style("opacity", 0).remove()
);
}

// SELECTED
function drawProfilePic(g) {
// g.each((d) =>
// g
// .append("circle")
// .attr("cx", 0)
// .attr("cy", 0)
// .attr("r", radius * 1.2)
// .attr("stroke", (d) => (d.fgId === 40 ? "blue" : "red"))
// .attr("stroke-width", 2)
// );
g.append("defs")
.append("clipPath")
.attr("id", "employee_pfp")
.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", radius);

g.append("image")
.attr("x", (d) => (-1 * (radius * 2)) / Math.pow(2, 1))
.attr("y", (d) => (-1 * (radius * 2)) / Math.pow(2, 1))
.attr("height", radius * 2)
.attr("width", radius * 2)
.attr("clip-path", "url(#employee_pfp)")
.attr("href", `${kavianPfp}`);

g.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", radius)
.attr("stroke-width", 1.5)
.attr("fill", "none")
.attr("stroke", (d) => (d.fgId === 40 ? "blue" : "red"));
}

// EMPLOYEE NAV (Identical to filters, actually)
// Select clicked employeePfp. No need to append anything new right?
// (Maybe new group)
// Scale, Translate and Transition circle to center, fade all.
// Create flyCircle data
// Append flyCircle/Button to Employee circle.
// + and X don't need data. But they have different onFunctions..

//EmployeeNavButtons
}
Insert cell
Insert cell
Insert cell
w = width
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
flatData = splines.flatMap((spline) => spline.employees)
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require("d3@6")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more