Angular version 16; D3 version 7.9.0; D3--chart version 3.1.1... I created a chart of anization. But I would want to provide the anizational chart a zoom-in and zoom-out function.
I added the code for zoom-in and zoom-out feature but when zoom-in or out the chart disappeared.
I don't know how to set the zoom-in ,zoom-out limitations for anizational chart.
HTML code
<div id="chart" class="chart-container"></div>
SCSS code
#chart {
.node .label {
fill: green !important;
}
.node .title {
fill: red !important;
}
.node rect {
fill: lightblue !important;
}
svg {
background-color: rgb(213, 219, 220);
}
::ng-deep.node-button-div {
width: 134% !important;
}
// ::ng-deep svg.svg-chart-container {
// height: 200vh !important;
// }
::ng-deep svg.svg-chart-container {
height: auto !important;
width: auto !important;
}
}
TS code
import {
OnChanges,
Component,
OnInit,
Input,
ViewChild,
ElementRef,
} from '@angular/core';
import * as d3 from 'd3';
import { HierarchyNode } from 'd3';
import { OrgChart, State } from 'd3--chart';
@Component({
selector: 'app--chart',
templateUrl: './-chartponent.html',
styleUrls: ['./-chartponent.scss']
})
export class OrgChartComponent implements OnInit, OnChanges {
@ViewChild("chartContainer") chartContainer: ElementRef | undefined;
@Input() data: any[] | undefined;
chart: any;
datas: any[] = [];
constructor() { }
ngOnInit() {
const res = {
data: [
{
id: 1,
pos_name: 'KITTING PROCESS',
size: '',
parentId: 0,
member: 'Mani.N (027)',
imgname: null,
desig_id: 1,
desig_name: 'Managing Director',
file_path: null,
// status: 'inprogress',
imageUrl:
'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
},
{
id: 2,
pos_name: 'ASSEMBLY PROCESS',
size: '',
parentId: 0,
// member: 'Soumen Deb (142)',
imgname: null,
desig_id: 2,
// desig_name: 'Test27_desig',
file_path: null,
// status: 'pending',
imageUrl:
'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
},
{
id: 3,
pos_name: 'TESTING PROCESS',
size: '',
parentId: 0,
// member: 'Tanmoy Ghosh (341)',
imgname: null,
desig_id: 3,
// desig_name: 'Manager-Laser Customer Relations',
file_path: null,
// status: 'completed',
imageUrl:
'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
},
{
id: 4,
pos_name: 'Dry Process',
size: '',
parentId: 1,
// member: 'Tanmoy Ghosh (341)',
imgname: null,
desig_id: 4,
// desig_name: 'Manager-Laser Customer Relations',
file_path: null,
// status: 'completed',
imageUrl:
'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
},
{
id: 5,
pos_name: 'Wet Process',
size: '',
parentId: 1,
// member: 'Tanmoy Ghosh (341)',
imgname: null,
desig_id: 4,
// desig_name: 'Manager-Laser Customer Relations',
file_path: null,
// status: 'pending',
imageUrl:
'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
},
{
id: 6,
pos_name: 'Radix Lab to Fab Products',
size: '',
parentId: 1,
// member: 'Tanmoy Ghosh (341)',
imgname: null,
desig_id: 6,
// desig_name: 'Manager-Laser Customer Relations',
file_path: null,
// status: 'inprogress',
imageUrl:
'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
},
{
id: 7,
pos_name: "Strategic Technology Platforms",
size: "",
parentId: 1,
member: "Sanjay.G (476)",
imgname: null,
desig_id: 73,
desig_name: "Senior Officer - Costing",
file_path: null,
// status: 'completed',
imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
},
{
id: 8,
pos_name: 'Dry Process',
size: '',
parentId: 2,
// member: 'Tanmoy Ghosh (341)',
imgname: null,
desig_id: 4,
// desig_name: 'Manager-Laser Customer Relations',
file_path: null,
// status: 'completed',
imageUrl:
'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
},
{
id: 9,
pos_name: 'Wet Process',
size: '',
parentId: 2,
// member: 'Tanmoy Ghosh (341)',
imgname: null,
desig_id: 4,
// desig_name: 'Manager-Laser Customer Relations',
file_path: null,
// status: 'pending',
imageUrl:
'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
},
{
id: 10,
pos_name: 'Radix Lab to Fab Products',
size: '',
parentId: 2,
// member: 'Tanmoy Ghosh (341)',
imgname: null,
desig_id: 6,
// desig_name: 'Manager-Laser Customer Relations',
file_path: null,
// status: 'inprogress',
imageUrl:
'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
},
{
id: 11,
pos_name: "Strategic Technology Platforms",
size: "",
parentId: 2,
member: "Sanjay.G (476)",
imgname: null,
desig_id: 73,
desig_name: "Senior Officer - Costing",
file_path: null,
// status: 'completed',
imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
},
{
id: 0,
pos_name: "YES",
size: "",
parentId: "",
member: "Organizational Chart",
desig_id: 73,
desig_name: "",
}
]
}
console.log('Org Chart Data:', res.data);
console.log('ID type:', typeof res.data[0].id, 'ParentID type:', typeof res.data[0].parentId);
console.log('IDs:', res.data.map(item => item.id));
res.data.forEach(item => {
if (item.id === null || item.id === undefined || item.parentId === null || item.parentId === undefined) {
console.log('Null or undefined found:', item);
}
});
if (res) {
const chartContainer = d3.select('.chart-container');
chartContainer.append('defs')
.append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('width', '100%')
.attr('height', '100%');
this.chart = new OrgChart()
.container('.chart-container')
.layout('left')
.data(res.data)
.nodeWidth((d: any) => 300)
.initialZoom(0.8)
.nodeHeight((d: any) => 175)
.buttonContent(
({
node,
state,
}: {
node: HierarchyNode<any>;
state: State<any>;
}) => {
return `<div style="color:#fff; border-radius: 5px;
padding: 3px 9px 3px 8px;font-size:10px;margin:auto auto;background-color:#007672;border: 1px solid #007672">
<span style="font-size:14px">
${node.data._directSubordinates}
${node.children
? `<i class="fa fa-angle-up"></i>`
: `<i class="fa fa-angle-down"></i>`
}
</span>
</div>`;
}
)
.linkUpdate((d: any, i: any, arr: any) => {
const selection = d3.select('.chart-container');
const strokeColor = '#007672';
const strokeWidth = d.data._upToTheRootHighlighted ? 15 : 1;
selection.style('stroke', strokeColor);
selection.attr('stroke-width', strokeWidth);
if (d.data._upToTheRootHighlighted) {
selection.raise();
}
})
.childrenMargin((d: any) => 50)
pactMarginBetween((d: any) => 35)
pactMarginPair((d: any) => 90)
.nodeContent(function (
d: HierarchyNode<any>,
i: number,
arr: HierarchyNode<any>[],
state: State<any>
) {
const nodeWithWidth = d as HierarchyNode<any> & { width: number };
const imageUrl = d.data.imageUrl ? d.data.imageUrl : 'assets/masters/user.jpg';
let borderColor = 'lightgray'; // Default border color
if (d.data.status === 'pending') {
borderColor = 'orange';
} else if (d.data.status === 'inprogress') {
borderColor = 'blue';
} else if (d.data.status === 'completed') {
borderColor = 'green';
}
return `
<div style="padding-top:0px;background-color:none; border-radius: 20px; margin-left:1px;height:${d.height}px;border-radius:2px;overflow:visible">
<div style="height:inherit;padding-top:0px;background-color:white;border:5px solid ${borderColor}; border-radius: 20px;">
<div
class="col-xs-12 col-sm-12 col-md-12 col-lg-12 row m-0 p-0"
style="
background: #ffffff 0% 0% no-repeat padding-box;
box-shadow: 0px 3px 6px #00000029;
height: inherit;
width: inherit;
border-radius: 20px;
"
>
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 row m-0 p-3">
<div
class="col-xs-12 col-sm-12 col-md-12 col-lg-12 row m-0 d-flex align-items-center p-0"
>
<div class="col-xs-3 col-sm-3 col-md-3 col-lg-3 p-0">
<img src="assets/card/profile1.png" style="width: 70px; height: 70px;border-radius: 50%;" />
</div>
<div class="col-xs-9 col-sm-9 col-md-9 col-lg-9 p-0">
<div
class="col-xs-12 col-sm-12 col-md-12 col-lg-12 d-flex flex-column ms-4"
>
<div
class="col-xs-12 col-sm-12 col-md-12 col-lg-12 pt-2 d-flex justify-content-start fw-bolder"
>
${d.data.pos_name}
</div>
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 py-1 fw-light " style="color: #007672;">
${d.data.desig_name}
</div>
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 py-1 fw-light " style="color: #868585;">
${d.data.member}
</div>
</div>
</div>
</div>
</div>`;
})
const zoom: any = d3
.zoom<SVGSVGElement, any>()
.scaleExtent([1, 10])
.on('zoom', (event: any) => {
chartContainer.attr('transform', event.transform);
});
chartContainer.call(zoom);
console.log("Chart after expandAll:", this.chart);
console.log("Chart after render:", this.chart);
this.chart.expandAll();
this.chart.render();
}
}
ngAfterViewInit() {
if (!this.chart) {
}
this.updateChart();
}
ngOnChanges() {
this.updateChart();
}
updateChart() {
if (!this.data) {
return;
}
if (!this.chart) {
return;
}
this.chart
.container('.chart-container')
.data(this.data)
.svgWidth(500)
.onNodeClick((d: any) => console.log(d + ' node clicked'))
.render();
}
}