block by jwilber 854038587107130baf74e6eebe65bb21

Sorting the Grid

Full Screen

Grid layout in combination with multi-dimensional sorting using d3.comparator.

forked from mayblue9‘s block: Sorting the Grid

forked from FrissAnalytics‘s block: Sorting the Grid

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
  font-family: Helvetica, sans-serif;
}
svg {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
}
</style>
<body>
Sort by
<a href="#" class="sort-btn" data-sort="id">id</a> /
<a href="#" class="sort-btn" data-sort="size">size</a> /
<a href="#" class="sort-btn" data-sort="color">color</a>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="d3-grid.js"></script>
<script src="d3-comparator.js"></script>
<script>

var width = 960,
    height = 500;

var grid = d3.layout.grid()
  .size([400, 400]);

var color = d3.scale.linear()
  .interpolate(d3.interpolateHcl)
  .domain([0, 9])
  .range(["rgb(33,49,62)", "rgb(239,238,105)"]);

var size = d3.scale.sqrt()
  .domain([0, 9])
  .range([0, 20]);

var sortBy = {
  id: d3.comparator()
    .order(d3.ascending, function(d) { return d.id; }),
  color: d3.comparator()
    .order(d3.ascending, function(d) { return d.color; })
    .order(d3.descending, function(d) { return d.size; })
    .order(d3.ascending, function(d) { return d.id; }),
  size: d3.comparator()
    .order(d3.descending, function(d) { return d.size; })
    .order(d3.ascending, function(d) { return d.color; })
    .order(d3.ascending, function(d) { return d.id; })
};

var data = d3.range(64).map(function(d) { 
  return {
    id: d,
    size: 1 + Math.floor(Math.random() * 9),
    color: Math.floor(Math.random() * 10)
  }; 
});

var svg = d3.select("body").append("svg")
  .attr({
    width: width,
    height: height
  })
.append("g")
  .attr("transform", "translate(280,50)");

d3.selectAll(".sort-btn")
  .on("click", function(d) {
    d3.event.preventDefault();
    data.sort(sortBy[this.dataset.sort]);
    update();
  });

update();

function update() {
  var node = svg.selectAll(".node")
    .data(grid(data), function(d) { return d.id; });
  node.enter().append("circle")
    .attr("class", "node")
    .attr("r", 1e-9)
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
    .style("fill", function(d) { return color(d.color); });
  node.transition().duration(1000).delay(function(d, i) { return i * 20; })
    .attr("r", function(d) { return size(d.size); })
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
  node.exit().transition()
    .attr("r", 1e-9)
    .remove();
}
 
</script>

d3-comparator.js

(function() {
  d3.comparator = function() {
    var cmps = [], accessors = [];

    var comparator = function(a, b) {
      var i = -1, 
          n = cmps.length, 
          result;
      while (++i < n) {
        result = cmps[i](accessors[i](a), accessors[i](b));
        if (result !== 0) return result;
      }
      return 0;
    }

    comparator.order = function(cmp, accessor) {
      cmps.push(cmp);
      accessors.push(accessor || identity);
      return comparator;
    }

    return comparator;
  }
  
  function identity(d) { return d; }
})();

d3-grid.js

(function() {
  d3.layout.grid = function() {
    var mode = "equal",
        layout = _distributeEqually,
        x = d3.scale.ordinal(),
        y = d3.scale.ordinal(),
        size = [1, 1],
        actualSize = [0, 0],
        nodeSize = false,
        bands = false,
        padding = [0, 0],
        cols, rows;

    function grid(nodes) {
      return layout(nodes);
    }

    function _distributeEqually(nodes) {
      var i = -1, 
          n = nodes.length,
          _cols = cols ? cols : 0,
          _rows = rows ? rows : 0,
          col, row;

      if (_rows && !_cols) {
        _cols = Math.ceil(n / _rows)
      } else {
        _cols || (_cols = Math.ceil(Math.sqrt(n)));
        _rows || (_rows = Math.ceil(n / _cols));
      }

      if (nodeSize) {
        x.domain(d3.range(_cols)).range(d3.range(0, (size[0] + padding[0]) * _cols, size[0] + padding[0]));
        y.domain(d3.range(_rows)).range(d3.range(0, (size[1] + padding[1]) * _rows, size[1] + padding[1]));
        actualSize[0] = bands ? x(_cols - 1) + size[0] : x(_cols - 1);
        actualSize[1] = bands ? y(_rows - 1) + size[1] : y(_rows - 1);
      } else if (bands) {
        x.domain(d3.range(_cols)).rangeBands([0, size[0]], padding[0], 0);
        y.domain(d3.range(_rows)).rangeBands([0, size[1]], padding[1], 0);
        actualSize[0] = x.rangeBand();
        actualSize[1] = y.rangeBand();
      } else {
        x.domain(d3.range(_cols)).rangePoints([0, size[0]]);
        y.domain(d3.range(_rows)).rangePoints([0, size[1]]);
        actualSize[0] = x(1);
        actualSize[1] = y(1);
      }

      while (++i < n) {
        col = i % _cols;
        row = Math.floor(i / _cols);

        nodes[i].x = x(col);
        nodes[i].y = y(row);
      }

      return nodes;
    }

    grid.size = function(value) {
      if (!arguments.length) return nodeSize ? actualSize : size;
      actualSize = [0, 0];
      nodeSize = (size = value) == null;
      return grid;
    }

    grid.nodeSize = function(value) {
      if (!arguments.length) return nodeSize ? size : actualSize;
      actualSize = [0, 0];
      nodeSize = (size = value) != null;
      return grid;
    }

    grid.rows = function(value) {
      if (!arguments.length) return rows;
      rows = value;
      return grid;
    }

    grid.cols = function(value) {
      if (!arguments.length) return cols;
      cols = value;
      return grid;
    }

    grid.bands = function() {
      bands = true;
      return grid;
    }

    grid.points = function() {
      bands = false;
      return grid;
    }

    grid.padding = function(value) {
      if (!arguments.length) return padding;
      padding = value;
      return grid;
    }

    return grid;
  };
})();