Published
Edited
Feb 7, 2020
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
workViz = {
const svg = d3.create('svg')
.attr('viewBox', [0, 0, width, height]);
// Create wrapper for upper day bar
const dayBar = svg.append('g')
.attr('class', 'dayBar')
.attr('transform', `translate(${margin.left},0)`)
.attr('width', innerWidth);
// Add month
svg.append('text')
.text(monthNames[new Date(firstDay).getMonth()])
.attr('x', width * 0.01)
.attr('y', height * 0.05)
.style('fill', '#70757a')
.style('font-family', 'Roboto');
// Add weekdays
dayBar.append('g')
.call(addWeekdays);
// Create container for work rectangles
const wrapper = svg.append('g')
.attr('transform', `translate(${margin.left},${0.2 * height})`);
// Add grid
wrapper.append('g')
.call(grid);
// Add y-axis
wrapper.append('g')
.call(yAxis);
// Add rectangles
wrapper.append('g')
.call(workRectangles);
return svg.node()
}
Insert cell
Insert cell
workRectangles = g => g
.selectAll('rect')
.data(filteredData)
.join(
enter => enter.append("rect")
.attr("fill", "#3490DC")
.attr('fill-opacity', 0.8)
.attr('data-start', d => d.beginning_datetime)
.attr('data-end', d => d.end_datetime)
.attr('y', d => {
let hour = parseInt(new Date(d.beginning_datetime).getUTCHours());
let minute = parseInt(new Date(d.beginning_datetime).getUTCMinutes());
if (hour == 0) {
yScale(23 * 60 + minute);
}
return yScale(hour * 60 + minute);
})
.attr('x', d => {
return xScale(moment(d.beginning_datetime).format('dd'));
})
.attr('height', d => {
let hourStart = parseInt(new Date(d.beginning_datetime).getUTCHours());
let minuteStart = parseInt(new Date(d.beginning_datetime).getUTCMinutes());
let hourEnd = parseInt(new Date(d.end_datetime).getUTCHours());
let minuteEnd = parseInt(new Date(d.end_datetime).getUTCMinutes());

return (yScale(hourEnd * 60 + minuteEnd)) -
(yScale(hourStart * 60 + minuteStart));
})
.attr('width', xScale.bandwidth() - 5)
.call(enter => enter.transition(t)
.attr('fill-opacity', 0.8)),
exit => exit
.call(exit => exit.transition(t)
.attr('fill-opacity', 0)
.remove())
);
Insert cell
Insert cell
addWeekdays = g => g
.selectAll('g')
.data(orderedWeekdays)
.join('g')
.attr('transform', d => `translate(${xScale(d)},0)`)
.attr('width', xScale.bandwidth())
.call(g => g
.append('text')
.attr('x', xScale.bandwidth() / 2)
.attr('y', 0.1 * innerHeight)
.style('fill', '#70757a')
.text(d => d.toUpperCase())
.style('font-family', 'Roboto')
.style('font-size', '0.6rem')
.attr('text-anchor', 'middle'))
.call(g => g
.append('text')
.data(weekDaysCurrent)
.join('text')
.attr('x', xScale.bandwidth() / 2)
.attr('y', 0.19 * innerHeight)
.style('color', '#3D4852')
.text(d => d)
.style('font-family', 'Roboto')
.style('font-size', '1rem')
.attr('text-anchor', 'middle'));
Insert cell
Insert cell
grid = g => g
.attr('stroke', 'grey')
.attr('stroke-opacity', 0.8)
.call(g => g.append('g')
.selectAll('line')
.data(d3.range(420, 1440, 60))
.join('line')
.attr('x1', 0)
.attr('x2', innerWidth)
.attr('y1', d => yScale(d) + 0.5)
.attr('y2', d => yScale(d) + 0.5)
.style('stroke', '#DAE1E7')
)
.call(g => g.append('g')
.selectAll('line')
.data(orderedWeekdays)
.join('line')
.attr('x1', d => xScale(d))
.attr('x2', d => xScale(d))
.attr('y1', d => yScale(420) - 10)
.attr('y2', d => yScale(1440))
.style('stroke', '#DAE1E7'));
Insert cell
Insert cell
xScale = d3.scaleBand()
.domain(orderedWeekdays)
.range([0, innerWidth])
.paddingInner(0);
Insert cell
yScale = d3.scaleLinear()
.domain([420, totalMinutesPerDay])
.range([0, 0.8 * height]);
Insert cell
yAxis = g => g
.attr('transform', `0,0)`)
.call(d3.axisLeft(yScale)
.tickValues(d3.range(420, 1440, 60))
.tickFormat(d => zeroFill(Math.floor(d / 60), 2) + ":" +
zeroFill((d % 60).toFixed(), 2)))
.call(g => g.select('.domain').remove())
.call(g => g.selectAll('.tick line')
.attr('stroke', '#DAE1E7'))
.call(g => g.selectAll('.tick text')
.style('font-size', '0.6rem')
.attr('font-family', 'Roboto'));
Insert cell
Insert cell
t = d3.transition()
.duration(750);
Insert cell
monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
Insert cell
orderedWeekdays = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]
Insert cell
firstDay = getDateOfISOWeek(week, year)
Insert cell
totalMinutesPerDay = 1430
Insert cell
weekDaysCurrent = [0, 1, 2, 3, 4, 5, 6].map(d => {
return new Date(firstDay).addDays(d).getDate()
})
Insert cell
Insert cell
innerWidth = width - margin.left - margin.right;
Insert cell
innerHeight = height - margin.top - margin.bottom;
Insert cell
margin = ({top: 100, left: 50, right: 5, bottom: 30});
Insert cell
height = 500;
Insert cell
Insert cell
zeroFill = function( number, width ){
width -= number.toString().length;
if ( width > 0 )
{
return new Array( width + (/\./.test( number ) ? 2 : 1) ).join( '0' ) + number;
}
return number + ""; // always return a string
}
Insert cell
Insert cell
Date.prototype.addDays = function(days) {
var date = new Date(this.valueOf());
date.setDate(date.getDate() + days);
return date;
}
Insert cell
Insert cell
function getDateOfISOWeek(w, y) {
var simple = new Date(y, 0, 1 + (w - 1) * 7);
var dow = simple.getDay();
var ISOweekStart = simple;
if (dow <= 4)
ISOweekStart.setDate(simple.getDate() - simple.getDay() + 1);
else
ISOweekStart.setDate(simple.getDate() + 8 - simple.getDay());
return ISOweekStart;
}
Insert cell
Insert cell
filteredData = work.filter(d => {
return d.year == year & moment(d.beginning_datetime).isoWeek() == week;
});
Insert cell
Insert cell
d3 = require('d3@5');
Insert cell
moment = require('moment@2.24')
Insert cell
import {slider, select} from "@jashkenas/inputs"
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more