block by shimizu 2660d136ba9156de119ac5ccb8130d33

Responsive Calendar

Full Screen

点線をドラッグしてチャートのサイズを変更することができます。

index.html

<!DOCTYPE html>
<html lang="jp">
<head>
<style>
html, body {
    padding: 0px;
    width:100%;
    height:100%;
}
#chart {
  width: 90%;
  border: 8px dashed gray;
  border-left: none;
  border-top:none;
  border-bottom:none;
  cursor:all-scroll;
}
    
    
    
.calendar {
    width: 100%;
}
    
</style>
<script src="//unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
<script src="//unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
<script src="//unpkg.com/d3@4.12.2/build/d3.min.js"></script>    
</head>
<body>
<div id="chart"></div>    
  
<script type="text/babel">
  
const WeeksPerYear = 53;  
  
const parent = d3.select("#chart");

const formatPercent = d3.format(".1%");

render()

function render(){
  d3.range(2013, 2018).forEach(function(year){
      const calendarWrpper = parent.selectAll(`.calendar.Y${year}`).data([null]);
      const wrapper = calendarWrpper.merge(calendarWrpper.enter().append("div").classed(`calendar Y${year}`, true));
      renderCalendar(wrapper, year)
  })
}

function renderCalendar(wrapper, years){

  const w = wrapper.node().clientWidth;
  const h = wrapper.style(w / WeeksPerYear * 7);

  const marginGuid = w / WeeksPerYear;

  const margin = {top:marginGuid*2, left:marginGuid*2, bottom:0, right:marginGuid*2};
  

  const pw = w - (margin.left + margin.right);
  const ph = h - (margin.top + margin.bottom);

  const cellSize = pw / WeeksPerYear;

  const updateSVG = wrapper.selectAll("svg").data([years]);
  const svg = updateSVG.merge(updateSVG.enter().append("svg"))
    .attr("width", w)
    .attr("height", marginGuid*7 + (margin.top + margin.bottom))
      
  const updateCalendar = svg.selectAll("g").data(d => [d])  
  const enterCalendar = updateCalendar.enter().append("g");
  enterCalendar.append("g").classed("cellLayer", true);
  enterCalendar.append("g").classed("monthLayer", true);
  enterCalendar.append("g").classed("labelLayer", true);
  const calendar = updateCalendar.merge(enterCalendar).attr("transform", `translate(${margin.left}, ${margin.top}) `);
  
  const cellLayer = calendar.select(".cellLayer");
  const monthLayer = calendar.select(".monthLayer"); 
  const labelLayer = calendar.select(".labelLayer");
  
  const title = renderCalendarTitle(labelLayer, cellSize, margin);
  const cell = renderCalendarCell(cellLayer, cellSize);
  const manth = renderMonthGuideline(monthLayer, cellSize)
}

function renderMonthGuideline(layer, cellSize){
  const  pathMonth = function(t0) {
    const t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
        d0 = t0.getDay(), w0 = d3.timeWeek.count(d3.timeYear(t0), t0),
        d1 = t1.getDay(), w1 = d3.timeWeek.count(d3.timeYear(t1), t1);
    return "M" + (w0 + 1) * cellSize + "," + d0 * cellSize
        + "H" + w0 * cellSize + "V" + 7 * cellSize
        + "H" + w1 * cellSize + "V" + (d1 + 1) * cellSize
        + "H" + (w1 + 1) * cellSize + "V" + 0
        + "H" + (w0 + 1) * cellSize + "Z";
  }

  const updateLine = layer.selectAll("path")
    .data(function(d) { return d3.timeMonths(new Date(d, 0, 1), new Date(d + 1, 0, 1)); })
  const line = updateLine.merge(updateLine.enter().append("path"))
    .attr("fill", "none")
    .attr("stroke", "#000")
    .attr("d", pathMonth);
  
  return pathMonth;
}

function renderCalendarTitle(layer, cellSize, margin){
  
  const updateYearLabel = layer.selectAll("text").data(function(d){ return [d] })
  const yearLabel = updateYearLabel.merge(updateYearLabel.enter().append("text"));
  
  yearLabel.attr("transform", `translate(${-cellSize},${cellSize * 3.5}) rotate(90)`)
    .attr("font-size", cellSize)
    .attr("font-family", "sans-serif")
    .attr("text-anchor", "middle")
    .attr("y", "0.5em")
    .attr("x", "-0.5em")
    .text(function(d) { return d; });
      
    return yearLabel;
}

function renderCalendarCell(layer, cellSize){    
  const updateCell = layer.selectAll("rect")
    .data(function(d) { return d3.timeDays(new Date(d, 0, 1), new Date(d + 1, 0, 1)); })
  const cell = updateCell.merge(updateCell.enter().append("rect"))
    .attr("width", cellSize)
    .attr("height", cellSize)
    .attr("fill", "none")
    .attr("stroke", "#ccc")
    .attr("x", function(d) { return d3.timeWeek.count(d3.timeYear(d), d) * cellSize; })
    .attr("y", function(d) { return d.getDay() * cellSize; })
    .datum(d3.timeFormat("%Y-%m-%d"));
    
    return cell
  
}


  
  
//divエレメントをドラッグでリサイズできるようにする。
const dispatch = d3.dispatch("resize");    
dispatch.on("resize", render);
setResizeControler();

function setResizeControler(){
    const drag = d3.drag()
        .on("drag", resized)
    
    d3.select("#chart")    
        .call(drag);
    
    function resized(e){
        const s = d3.event.sourceEvent;
        const w = (s.pageX < 400) ? 400 : s.pageX;
        
        d3.select(this)
            .style("width", `${w}px`)
        
        dispatch.call("resize");
        
    }
    
}
    
</script>