block by bollwyvl 5599335

Cards

Full Screen

Cards

Shows use of:

Limitations, ways forward

index.html

<html>
  <head>
    <title>Card Moving/Editing Proof of Concept</title>
    <style>
      html, body{
        margin: 0;
        padding: 0;
        overflow: hidden;
        background-color: lightblue;
        font-family: 'Gill Sans MT', 'Gill Sans';
      }
      #root{
        width: 100%;
        height: 100%;
        
      }
      .card{
        height: 150px;
        width: 150px;
        position: absolute;
        background-color: lightyellow;
        overflow-y: auto;
        text-align: center;
        border-radius: 2px;
        padding: 5px;
        box-shadow: -1px 2px 5px 2px rgba(0, 0, 0, .25);
      }
      .card .text{
        height: 100%;
        width: 100%;
      }
      #doc{
        position: fixed;
        right: 10px;
        bottom: 10px;
        text-align: right;
        opacity: .25;
      }
    </style>
  </head>
  <body>
    <div id="doc">
      <p>Double-click the background to make a card</p>
      <p>Double-click a card to edit it</p>
      <p>Zoom with mouse wheel</p>
    </div>
    <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script src="cards.js" charset="utf-8"></script>
  </body>
</html>

cards.js

;(function(d3){
  var ENTER = 13;
  
  var Z = 1;
  
  var ZOOM = 1;
  
  var card_collection = window.card_collection = [];
  
  function Card(){
    var my = {
      x: 0,
      y: 0,
      text: null,
      id: +(new Date()) + Math.random()
    };
    
    function wrap(attr){
      return function(val){
        if(val === void 0){ return my[attr]; }
        my[attr] = val;
        return api;
      };
    }
    
    var api = {
      x: wrap("x"),
      y: wrap("y"),
      text: wrap("text"),
      id: wrap("id")
    };
    
    return api;
  }
  
  Card.create = function(){
    var card = Card().x(d3.event.x).y(d3.event.y);
    card._editable = 1;
    card_collection.push(card);
    render();
    
    var div = d3.selectAll(".card").filter(function(d){
      return d === card;
    }).node();

    Card.click.call(div, card);
  };
  
  Card.click = function(d, i){
    d._editable = true;
    render();
    d3.select(this).select(".text").node().focus();
    d3.event.stopPropagation();
  };
  
  Card.keyup = function(d, i){
    if(d3.event.shiftKey && d3.event.keyCode === ENTER){
      Card.save(d, i);
    }
  };
  
  Card.save = function(d, i){
    if(d._editable){ return; }
    console.log("saved!");
    this.blur();
    var text = d3.select(this);
    d.text(this.innerHTML);
    d._editable = false;
    render();
  };
  
  Card.zoom = function(){
    var regex = {
      scale: /scale\(([\d\.]*)\)/,
      mtx: /matrix\(([\d\.]*), /,
    };
    var old_zoom = (
      root.style("transform") || 
      root.style("-webkit-transform") ||
      "");
    
    d3.entries(regex).map(function(regex, key){
      var match = old_zoom.match(regex.value);
      if(match !== null){
        old_zoom = +match[1];
      }
    });
    
    if(!old_zoom === null){
      old_zoom = 1;
    }
    ZOOM = Math.max(0.01, old_zoom + d3.event.wheelDelta / 1000);
    root.call(xb_scale(ZOOM));
  };
  
  Card.drag = d3.behavior.drag()
    .origin(function(){
      var that = d3.select(this);
      return {
        x: parseInt(that.style("left")),
        y: parseInt(that.style("top"))
      }
    })
    .on("drag", function(d, i){
      if(editable(d)){return;}
      
      d.x(d3.event.x)
        .y(d3.event.y);
        
      d3.select(this)
        .style("left", d3.event.x + "px")
        .style("top", d3.event.y + "px")
        .style("z-index", Z++);
    });
  
  function attr(att){
    return function(d){
      return d[att];
    };
  }
  
  attr.px = function(att){
    var func = attr(att);
    return function(d, i){
      return func(d, i) + "px";
    };
  };
  
  function xb_scale(amount){
    var val = "scale(" + amount + ")";
    return function(selection){
      selection
        .transition()
        .style("transform", val)
        .style("-webkit-transform", val)
    };
  }
  
  // initialize the frame
  var root = d3.select("body")
    .on("mousewheel", Card.zoom)
  .selectAll("#root")
    .data([1]);
  
  root.enter().append("div")
    .attr("id", "root")
    .on("dblclick", Card.create)
    .call(xb_scale(ZOOM));
  
  function editable(d, i){
    return d._editable;
  }
  
  function render(){
    var card = root.selectAll(".card")
        .data(card_collection),
      card_init = card.enter()
        .append("div")
        .attr("class", "card")
        .call(Card.drag)
        .style("left", function(d){ return d.x()+"px"; })
        .style("top", function(d){ return d.y()+"px"; })
        .on("dblclick", Card.click);
      
    card_init.append("div")
      .classed("text", 1)
      .on("keydown", Card.keyup)
      .on("blur", function(d, i){
        d._editable = false;
        Card.save.call(this, d,i);
      });
    
    card.select(".text")
      .attr("contenteditable", editable)
      .each(function(d, i){
        this.innerHTML = d.text();
      });
        
    card
      .style("left", attr.px("x"))
      .style("top", attr.px("y"));
  }
  
  render();
}).call(this, d3);