JSFiddle - React, Tailwind, and code Playground

by Dogbert

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.6/d3.min.js"></script>
<div id="menu-container">
  <div id="menu">
  </div>
  <svg viewBox="0 0 20 100" preserveAspectRatio="none"></svg>
</div>

SCSS

$closed-width: 50px;
$opened-width: 200px;
$curve-width: 35px;
$curve-padding-right: 50px;
* {
  margin: 0;
  padding: 0;
}

body {
  background: white;
}

#menu-container {
  background: #eee;
  float: left;
  position: relative;
  height: 100vh;
  #menu {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    background: #eee;
    padding-right: $curve-width;
  }
  &,
  #menu {
    width: $closed-width;
    transition: width .5s ease;
  }
  svg {
    position: absolute;
    left: $closed-width;
    height: 100%;
    width: $curve-width;
    padding-right: $curve-padding-right;
    path {
      fill: #eee;
    }
  }
  &.open {
    &,
    #menu {
      width: $opened-width;
    }
    svg {
      width: 0;
    }
  }
}

JavaScript

// ratio of height of curve to window height, in %
var heightRatio = 200;
var duration = 500;

var svg = d3.select('#menu-container svg');

var line = svg.append('path').attr('d', d);

var d;

svg.on('mouseenter', function() {
  go(true);
}).on('mouseleave', function() {
  line.transition().duration(duration).attr('d', d);
}).on('mousemove', function() {
  go(false)
});

var container = d3.select('#menu-container');

d3.select("#menu-container #menu").on('mouseenter', function() {
  container.classed('open', true);
});

container.on('mouseleave', function() {
  container.classed('open', false);
});

var suppress = false;

function go(didEnter) {
  var y = (d3.event.offsetY - container.node().offsetTop) / container.node().offsetHeight * 100;
  d = 'M 0 ' + (y - heightRatio / 2) + ' Q 0 ' + y + ' 0 ' + (y + heightRatio / 2);
  var d2 = 'M 0 ' + (y - heightRatio / 2) + ' Q 15 ' + y + ' 0 ' + (y + heightRatio / 2);
  if (didEnter) {
    suppress = true;
    line.attr('d', d);
    line.transition()
      .ease(d3.easeCubic)
      .duration(duration)
      .attr('d', d2)
      .on('end', function() {
        suppress = false;
      })
  } else if (!suppress) {
    line.attr('d', d2)
  }
}