Skip to content

Instantly share code, notes, and snippets.

@Kashif1Naqvi
Created November 7, 2019 11:36
Show Gist options
  • Save Kashif1Naqvi/ac32a1524baf786a7f781b711512afd8 to your computer and use it in GitHub Desktop.
Save Kashif1Naqvi/ac32a1524baf786a7f781b711512afd8 to your computer and use it in GitHub Desktop.
Donut Chart(Budget Planner)
license: mit
const dim = { width:500 , height: 340, radius:150 }
const cent = {x:dim.width/2 + 5 , y:dim.height/2 + 5 }
const svg = d3.select(".canvas").append("svg").attr("height",dim.height+150).attr("width",dim.width+150)
const graph = svg.append("g").attr("transform",`translate(${cent.x},${cent.y})`)
const color = d3.scaleOrdinal(d3['schemeSet3'])
const pie = d3.pie().sort(null).value(d=>d.cost)
const arcPath = d3.arc().outerRadius(dim.radius).innerRadius(dim.radius/2)
const legendGroup = svg.append("g").attr("transform",`translate(${cent.x + 190},${cent.y-101})`)
const legend = d3.legendColor()
.shape("circle")
.shapePadding(10)
.scale(color);
const tip = d3.tip()
.attr("class","tip card")
.html(d=>{
let content = `<div class="name" >${d.data.name}</div>`
content += `<div class="cost">${d.data.cost}</div>`
content += `<div class="text-info">Click slice to delete</div>`
return content;
})
graph.call(tip)
var data = []
const update = (data) => {
color.domain(data.map(d => d.name))
legendGroup.call(legend).style("fill","#fff").style("font-weight","bold")
const path = graph.selectAll("path").data(pie(data))
path.exit().transition().duration(750).attrTween("d",arcTweenExit)
.remove()
path.attr("d",arcPath)
.enter()
.append("path")
.attr("stroke","#fff")
.attr("stroke-width",1)
.attr("fill", d => color(d.data.name))
graph.selectAll("path")
.on("mouseover",(d,i,n)=>{
tip.show(d,n[i])
handleMouseOver(d,i,n)
})
.on("mouseout",(d,i,n)=>{
tip.hide()
handleMouseOut(d,i,n)
})
.on("click",handleClick)
.transition()
.duration(750)
.attrTween("d",arcTweenEnter)
}
db.collection("expenses").onSnapshot(res=>{
console.log(res);
res.docChanges().map(change=>{
let doc = {...change.doc.data(), id: change.doc.id}
switch (change.type) {
case 'added':
data.push(doc)
break;
case 'modified':
let index = data.findIndex(item => item.id == doc.id );
data[index] = doc;
break;
case "removed":
data = data.filter(item => item.id !== doc.id)
break;
default:
break;
}
})
update(data)
})
const arcTweenEnter = (d) => {
let i = d3.interpolate(d.endAngle , d.startAngle);
return function(t){
d.startAngle = i(t)
return arcPath(d)
}
}
const arcTweenExit = (d) => {
let i = d3.interpolate(d.startAngle , d.endAngle);
return function(t){
d.startAngle = i(t)
return arcPath(d)
}
}
const handleMouseOver = (d,i,n)=>{
d3.select(n[i]).transition("changeSliceFill").duration(400).attr("fill","white")
}
const handleMouseOut = (d,i,n)=>{
d3.select(n[i]).transition("changeSliceFill").duration(400).attr("fill",color(d.data.name))
}
const handleClick = (d) =>{
console.log(d.data.id);
let id = d.data.id
db.collection("expenses").doc(id).delete()
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<title>Dont charset</title>
</head>
<body class="bg-dark" >
<style>
.text-line {
background-color: transparent;
color: #000;
outline: none;
outline-style: none;
border-top: none;
border-left: none;
border-right: none;
border-bottom: solid #eeeddd 2px;
padding: 13px 13px;
}
.tip {
pointer-events:none;
position:absolute;
margin:1px;
padding-bottom: : 5px;
opacity: .9;
padding:0px;
background: none repeat scroll 0 white;
border:solid none;
box-shadow: 0px 3px 15px #888888;
color:#8f8e8c;
font: 14px sans-serif;
width:100px;
height:inherit;
text-align: center;
font-weight: normal;
border-radius: 3px;
/* font-family: 'Staatliches', cursive; */
font-family: 'Liu Jian Mao Cao', cursive;
font-family: 'Courgette', cursive;
}
.tip ::after {
content: "";
position: absolute;
top: 100%;
left: 80%;
margin-left:-35px;
border-width:10px;
border-style: solid;
border-color: white transparent transparent transparent;
}
</style>
<div class="container" >
<header class="bg-light" >
<h2 class="text-center p-4" >Tracker</h2>
<p class="text-center p-4" >Monthly Money Tracker</p>
</header>
<div class="row">
<div class="col-12 col-sm-6 col-lg-6 col-md-6 col-xl-6">
<form class="card p-3 m-4" >
<div class="card-body" >
<div class="form-group" >
<label for="name"></label>
<input type="text" class="text-line" placeholder="Name" id="name">
</div>
<div class="form-group">
<label for="cost"></label>
<input type="text" id="cost" class="text-line" placeholder="Cost" >
</div>
<p id="error" class="alert-danger mt-3"></p>
<input type="submit" value="Add item" class="btn btn-danger float-right " style="border-style:dashed;" >
</div>
</form>
</div>
<div class="col-12 col-sm-6 col-lg-6 col-md-6 col-xl-6">
<div class="canvas" ></div>
</div>
</div>
</div>
<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-firestore.js"></script>
<!-- TODO: Add SDKs for Firebase products that you want to use
https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-analytics.js"></script>
<script>
// Your web app's Firebase configuration
var firebaseConfig = {
apiKey: "AIzaSyCrJwDOd-Fu8R9i2NFTuk3adl49_5bdMME",
authDomain: "d3-firebase-1c3d6.firebaseapp.com",
databaseURL: "https://d3-firebase-1c3d6.firebaseio.com",
projectId: "d3-firebase-1c3d6",
storageBucket: "d3-firebase-1c3d6.appspot.com",
messagingSenderId: "776696333178",
appId: "1:776696333178:web:04004dafee1c4dace4ccc3",
measurementId: "G-9X0E1CZWNF"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.analytics();
let db = firebase.firestore()
</script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.25.6/d3-legend.min.js" ></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.9.1/d3-tip.min.js"></script>
<script src="index.js" ></script>
<script src="graph.js" ></script>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>
let form = document.querySelector("form")
let name = document.querySelector("#name")
let cost = document.querySelector("#cost")
let error = document.querySelector("#error")
form.addEventListener("submit",(e)=>{
e.preventDefault();
if(name.value && cost.value ){
let item = {
name : name.value,
cost: parseInt(cost.value)
}
db.collection('expenses').add(item).then(res=>{
name.value = ""
cost.value = ""
error.textContent = ""
}
)
}else{
error.textContent = "Please enter values before submitting"
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment