Published
Edited
Oct 21, 2021
Fork of D3 Template
2 forks
2 stars
Insert cell
Insert cell
Insert cell
chart = new Chart()
.container(chartContainer)
.svgWidth(1000)
.data({message:'Hello, I am chart, seriously...'})
.render()
Insert cell
class Chart {
constructor() {
const attrs = {
id: "ID" + Math.floor(Math.random() * 1000000),
svgWidth: 400,
svgHeight: 400,
marginTop: 5,
marginBottom: 5,
marginRight: 5,
marginLeft: 5,
container: "body",
defaultTextFill: "#2C3E50",
defaultFont: "Helvetica",
data: null,
chartWidth: null,
chartHeight: null
};
this.getState = () => attrs;
this.setState = (d) => Object.assign(attrs, d);
Object.keys(attrs).forEach((key) => {
//@ts-ignore
this[key] = function (_) {
var string = `attrs['${key}'] = _`;
if (!arguments.length) {
return eval(`attrs['${key}'];`);
}
eval(string);
return this;
};
});
this.initializeEnterExitUpdatePattern();
}

initializeEnterExitUpdatePattern() {
d3.selection.prototype.patternify = function (params) {
var container = this;
var selector = params.selector;
var elementTag = params.tag;
var data = params.data || [selector];
var exitTransition = params.exitTransition || null;
var enterTransition = params.enterTransition || null;
// Pattern in action
var selection = container.selectAll("." + selector).data(data, (d, i) => {
if (typeof d === "object") {
if (d.id) {
return d.id;
}
}
return i;
});
if (exitTransition) {
exitTransition(selection);
} else {
selection.exit().remove();
}

const enterSelection = selection.enter().append(elementTag);
if (enterTransition) {
enterTransition(enterSelection);
}
selection = enterSelection.merge(selection);
selection.attr("class", selector);
return selection;
};
}

// ================== RENDERING ===================
render() {
this.setDynamicContainer();
this.calculateProperties();
this.drawSvgAndWrappers();
}

setDynamicContainer() {
const attrs = this.getState();

//Drawing containers
var container = d3.select(attrs.container);
var containerRect = container.node().getBoundingClientRect();
if (containerRect.width > 0) attrs.svgWidth = containerRect.width;
this.setState({ container });
}

drawSvgAndWrappers() {
const {
container,
svgWidth,
svgHeight,
defaultFont,
calc
} = this.getState();

// Draw SVG
const svg = container
.patternify({
tag: "svg",
selector: "svg-chart-container"
})
.attr("width", svgWidth)
.attr("height", svgHeight)
.attr("font-family", defaultFont);

//Add container g element
var chart = svg
.patternify({
tag: "g",
selector: "chart"
})
.attr(
"transform",
"translate(" + calc.chartLeftMargin + "," + calc.chartTopMargin + ")"
);

// REMOVE THIS SNIPPET AFTER YOU START THE DEVELOPMENT
chart
.patternify({
tag: "text",
selector: "example-text",
data: ["test"]
})
.text((d) => d)
.attr("x", 10)
.attr("y", 20);
}

calculateProperties() {
const attrs = this.getState();

//Calculated properties
var calc = {
id: null,
chartTopMargin: null,
chartLeftMargin: null,
chartWidth: null,
chartHeight: null
};
calc.id = "ID" + Math.floor(Math.random() * 1000000); // id for event handlings
calc.chartLeftMargin = attrs.marginLeft;
calc.chartTopMargin = attrs.marginTop;
const chartWidth =
attrs.svgWidth - attrs.marginRight - calc.chartLeftMargin;
const chartHeight =
attrs.svgHeight - attrs.marginBottom - calc.chartTopMargin;

this.setState({ calc, chartWidth, chartHeight });
}

updateData(data) {
const attrs = this.getChartState();
console.log("smoothly updating data");
return this;
}
}
Insert cell
Insert cell
Insert cell
Insert cell
function typescriptify(str){
return str.replace(/\t/g,' ')
.replace(/attrs\s?=/g,'attrs:any = ')
.replace(/updateData;/g,'updateData:any;')
.replace(/setExpanded;/g,'setExpanded:any;')
.replace(/setZoomFactor;/g,'setZoomFactor:any;')
.replace(/removeNode;/g,'removeNode:any;')
.replace(/addNode;/g,'addNode:any;')
.replace(/calc\s?=/g,'calc:any = ')
.replace(/\(d\)\s*\{/g,'(d:any){')
.replace(/d=>/g,'(d:any)=>')
.replace(/d =>/g,'(d:any)=>')
.replace(/ch =>/g,'(ch:any)=>')
.replace(/\(d, i\)/g,'(d:any,i:number)')
.replace(/\(s, t\)/g,'(s:any,t:any)')
.replace(/\(params\)/g,'(params:any)')
.replace(/function\(obj\)/g,'function(obj:any)')
.replace(/function\(nodeId\)/g,'function(nodeId:any)')
.replace(/function\(zoomLevel\)/g,'function(zoomLevel:number)')
.replace(/\(id, expandedFlag\)/g,'(id:any,expandedFlag:boolean)')
.replace(/\(node, flag\)/g,'(node:any,flag:boolean)')
.replace(/\(node, nodeIdsStore\)/g,'(node:any,nodeIdsStore:any[])')
.replace(/layouts\s?=/g,'layouts:any = ')
.replace(/behaviors\s?=/g,'behaviors:any = ')
.replace(/\(source\)/g,'(source:any)')
.replace(/\(\s?d\s?=>/g,'((d:any)=>')
return str;
}
Insert cell
d3 = require('d3')
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