JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
<div class="org-chart-container">
<div id="chart_div"></div>
</div>
<div class="task-bar">
<button id="update-chart">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="18"
height="18"
stroke="currentColor"
fill="none"
>
<path
d="M10.5 22H6.59087C5.04549 22 3.81631 21.248 2.71266 20.1966C0.453365 18.0441 4.1628 16.324 5.57757 15.4816C8.12805 13.9629 11.2057 13.6118 14 14.4281"
stroke-width="1.5"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M16.5 6.5C16.5 8.98528 14.4853 11 12 11C9.51472 11 7.5 8.98528 7.5 6.5C7.5 4.01472 9.51472 2 12 2C14.4853 2 16.5 4.01472 16.5 6.5Z"
stroke="currentColor"
stroke-width="1.5"
/>
<path
d="M18.4332 13.8485C18.7685 13.4851 18.9362 13.3035 19.1143 13.1975C19.5442 12.9418 20.0736 12.9339 20.5107 13.1765C20.6918 13.2771 20.8646 13.4537 21.2103 13.8067C21.5559 14.1598 21.7287 14.3364 21.8272 14.5214C22.0647 14.9679 22.0569 15.5087 21.8066 15.9478C21.7029 16.1298 21.5251 16.3011 21.1694 16.6437L16.9378 20.7194C16.2638 21.3686 15.9268 21.6932 15.5056 21.8577C15.0845 22.0222 14.6214 22.0101 13.6954 21.9859L13.5694 21.9826C13.2875 21.9752 13.1466 21.9715 13.0646 21.8785C12.9827 21.7855 12.9939 21.6419 13.0162 21.3548L13.0284 21.1988C13.0914 20.3906 13.1228 19.9865 13.2807 19.6232C13.4385 19.2599 13.7107 18.965 14.2552 18.375L18.4332 13.8485Z"
stroke-width="1.5"
stroke="currentColor"
stroke-linejoin="round"
/>
</svg>
</button>
<button id="edit-code">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="18"
height="18"
color="currentColor"
fill="none"
>
<path
d="M21.3175 7.14139L20.8239 6.28479C20.4506 5.63696 20.264 5.31305 19.9464 5.18388C19.6288...
CSS
#chart_div {
width: 100%;
height: calc(100vh - 100px);
font-family: Barlow Condensed, sans-serif;
font-size: 16px;
margin: 0 auto;
position: relative;
overflow: hidden;
}
.padded-text {
padding: 20px;
display: inline-block;
}
/* General text styles for nodes */
.node text {
font-family: Barlow Condensed, sans-serif;
text-anchor: middle; /* Center the text horizontally */
fill: #292929; /* Default black fill */
}
.node image {
height: 3.25em;
}
/* Position text styles */
.position-text {
font-size: .4em;
text-transform: uppercase;
letter-spacing: -.15px;
font-weight: 500;
}
/* Name text styles */
.name-text {
font-size: .45em;
letter-spacing: -.15px;
font-weight: normal;
}
/* Years of service text styles */
.years-text {
font-size: .3em;
font-style: italic;
}
/* Style for links between nodes */
.link {
stroke-width: 0.5px;
stroke-linecap: round;
}
/* Styling for site code */
.site-code {
font-family: Poppins, sans-serif;
font-weight: 300;
text-align: left;
letter-spacing: 0.03em;/* Use fill instead of color for SVG text */
}
/* Styling for service icon */
.service-icon {
font-weight: bold;/* Use fill for SVG text */
}
_____________________
.task-bar { /* Empty but necessary */
}
/* Button styling */
body .task-bar {
position: fixed;
bottom: 0px;
left: 50%;
transform: translateX(-50%);
width: 33vw;
min-width: 35.0em;
background-color: #011E41;
border-radius: 8px 8px 0px 0px;
display: flex;
align-items: center;
justify-content: space-around;
padding: 10px;
z-index: 1000;
color: #D4E2FF;
}
.task-bar button {
background-color: transparent;
border: none;
border-radius: 8px;
color: #D4E2FF;
font-size: 12px;
padding-top: 5px;
padding-bottom: 5px;
padding-left: 10px;
padding-right: 10px;
cursor: pointer;
font-family: 'Lexend Exa', sans-serif;
text-transform: uppercase;
transition: background-color 0.3s;
}
/* Hover effect for buttons...
JavaScript
// Define node width and tree layout size
const width = 1080
const height = 1080
const svg = d3
.select("#chart_div")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.append("g")
.attr("transform", "translate(50,50)")
// Set up zoom behavior
const zoom = d3
.zoom()
.scaleExtent([0.1, 8])
.on("zoom", function (event) {
svg.attr("transform", event.transform)
})
d3.select("svg").call(zoom)
// Fetching Data
const sheetId = "1AyZ_K4keTbkNlTpjzxP7Ac3WB8Hok_IZS0fkNgWmYo4"
const apiKey = "AIzaSyCFFxB-EOrPNodRO8xnCHSIxbbTBkCaadE"
const range = "Sheet1!A:I"
const url = `https://sheets.googleapis.com/v4/spreadsheets/${sheetId}/values/${range}?key=${apiKey}`
fetch(url)
.then((response) => response.json())
.then((data) => {
const rows = data.values
const orgData = rows.slice(1).map((row, index) => ({
id: `node_${index + 2}`,
position: row[0] || "Vacant",
name: row[1] || "Vacant",
manager: row[2] || null,
tier: row[3] || 7,
photo: row[4] || "https://gadshillcenter.org/files/galleries/blank.jpg",
yearsOfService: row[5] || "",
serviceIcon: row[6] || "",
siteCode: row[7] || "",
department: row[8] || "CEO",
}))
const root = d3
.stratify()
.id((d) => d.position)
.parentId((d) => d.manager)(orgData)
const treeLayout = d3.tree().size([width, height])
treeLayout(root)
// Node size logic
const nodesByTier = d3.group(root.descendants(), (d) => d.depth)
const nodeWidth = width * 0.05
const nodeHeight = (nodeWidth / 3) * 5.5
const imageHeight = 3.25 * 16; // Convert em units to px (assuming 16px base font size)
const imageAspectRatio = 2.16 / 3.25; // Aspect ratio based on node dimensions
const imageWidth = imageHeight * imageAspectRatio;
// Define the clipPath for rounded images inside this block
svg.append("defs")
.append("clipPath")
.attr("id", "rounded-image") // Unique ID for this clip path
...