// ---------------------------------------------------------------------

interface TestObject {
    name: string;
    val: number;
}

let testObjectMaybe: TestObject | undefined;
let testObjArray: TestObject[];
let testObjKVArray: Array<{ key: string, value: TestObject }>;

// Create Map ========================================================

let basicMap: d3Collection.Map<string>;
let anyMap: d3Collection.Map<any>;
anyMap = d3Collection.map(); // empty map
basicMap = d3Collection.map<string>(); // empty map

// from array with accessor without accessor
basicMap = d3Collection.map(['foo', 'bar']); // map with key-value pairs { '0': 'foo' } and { '1': 'bar'}

// from array with accessor
let testObjMap: d3Collection.Map<TestObject>;
testObjMap = d3Collection.map<TestObject>([{ name: 'foo', val: 10 }, { name: 'bar', val: 42 }], (value, i, array) => {
    return value.name;
});

// from existing map
basicMap = d3Collection.map(basicMap);
// basicMap = d3Collection.map(testObjMap); // fails, as maps have different value type
Beispiel #2
0
export default function(nodes) {
  var simulation,
      alpha = 1,
      alphaMin = 0.001,
      alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),
      alphaTarget = 0,
      drag = 0.6,
      forces = map(),
      fixes = {},
      stepper = timer(step),
      event = dispatch("tick", "end");

  if (nodes == null) nodes = [];

  function step() {
    tick();
    event.call("tick", simulation);
    if (alpha < alphaMin) {
      stepper.stop();
      event.call("end", simulation);
    }
  }

  function tick() {
    var i, n = nodes.length, node, fix;

    alpha += (alphaTarget - alpha) * alphaDecay;

    forces.each(function(force) {
      force(alpha);
    });

    for (i = 0; i < n; ++i) {
      node = nodes[i];
      node.x += node.vx *= drag;
      node.y += node.vy *= drag;
    }

    for (i in fixes) {
      fix = fixes[i], node = nodes[i];
      node.x = fix.x;
      node.y = fix.y;
      node.vx =
      node.vy = 0;
    }
  }

  function initializeNodes() {
    for (var i = 0, n = nodes.length, node; i < n; ++i) {
      node = nodes[i], node.index = i;
      if (isNaN(node.x) || isNaN(node.y)) {
        var radius = initialRadius * Math.sqrt(i), angle = i * initialAngle;
        node.x = radius * Math.cos(angle);
        node.y = radius * Math.sin(angle);
      }
      if (isNaN(node.vx) || isNaN(node.vy)) {
        node.vx = node.vy = 0;
      }
    }
  }

  function initializeForce(force) {
    if (force.initialize) force.initialize(nodes);
    return force;
  }

  initializeNodes();

  return simulation = {
    tick: tick,

    restart: function() {
      return stepper.restart(step), simulation;
    },

    stop: function() {
      return stepper.stop(), simulation;
    },

    nodes: function(_) {
      return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes;
    },

    alpha: function(_) {
      return arguments.length ? (alpha = +_, simulation) : alpha;
    },

    alphaMin: function(_) {
      return arguments.length ? (alphaMin = +_, simulation) : alphaMin;
    },

    alphaDecay: function(_) {
      return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay;
    },

    alphaTarget: function(_) {
      return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget;
    },

    drag: function(_) {
      return arguments.length ? (drag = 1 - _, simulation) : 1 - drag;
    },

    force: function(name, _) {
      return arguments.length > 1 ? ((_ == null ? forces.remove(name) : forces.set(name, initializeForce(_))), simulation) : forces.get(name);
    },

    fix: function(node, x, y) {
      return fixes[node.index] = {x: x == null ? node.x : +x, y: y == null ? node.y : +y}, simulation;
    },

    unfix: function(node) {
      return delete fixes[node.index], simulation;
    },

    find: function(x, y, radius) {
      var i = 0,
          n = nodes.length,
          dx,
          dy,
          d2,
          node,
          closest;

      if (radius == null) radius = Infinity;
      else radius *= radius;

      for (i = 0; i < n; ++i) {
        node = nodes[i];
        dx = x - node.x;
        dy = y - node.y;
        d2 = dx * dx + dy * dy;
        if (d2 < radius) closest = node, radius = d2;
      }

      return closest;
    },

    on: function(name, _) {
      return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);
    }
  };
}
Beispiel #3
0
export function tip() {
    let direction = d3TipDirection;
    let offset = d3TipOffset;
    let html = d3TipHTML;
    let rootElement = functor(document.body);
    let node = initNode();
    let svg = null;
    let point = null;
    let target = null;

    const tip: any = function (vis) {
        svg = getSVGNode(vis);
        if (!svg) return;
        point = svg.createSVGPoint();
        const re = rootElement();
        if (!re) return;
        if (!node) return;
        re.appendChild(node);
    };

    // Public - show the tooltip on the screen
    //
    // Returns a tip
    tip.show = function (d, idx, arr) {
        target = arr[idx];
        const args = Array.prototype.slice.call(arguments);
        const content = html.apply(this, args);
        if (content === null) {
            return tip;
        }
        const poffset = offset.apply(this, args);
        const nodel = getNodeEl();
        let i = directions.length;
        let coords;
        const root_bbox = rootElement().getBoundingClientRect();
        nodel.html(content)
            .style("opacity", 1).style("pointer-events", "all");

        while (i--) nodel.classed(directions[i], false);
        let placement_success = false;
        const placement_overflow = {};
        let least_overflow_direction = directions[0];
        for (let i = 0; i < directions.length; i++) {
            placement_success = _placement_attempt(directions[i]);
            if (placement_success) break;
        }
        if (!placement_success) {
            nodel.classed("notick", true);
            const top_offset = _vertical_adjustment(placement_overflow[least_overflow_direction]);
            const left_offset = _horizontal_adjustment(placement_overflow[least_overflow_direction]);
            _placement_attempt(least_overflow_direction, top_offset, left_offset);
        } else {
            nodel.classed("notick", false);
        }
        return tip;

        function _horizontal_adjustment(overflow_obj) {
            if (overflow_obj.left > overflow_obj.right) {
                return overflow_obj.left > 0 ? -overflow_obj.left : 0;
            } else {
                return overflow_obj.right > 0 ? overflow_obj.right : 0;
            }
        }
        function _vertical_adjustment(overflow_obj) {
            if (overflow_obj.top > overflow_obj.bottom) {
                return overflow_obj.top > 0 ? -overflow_obj.top : 0;
            } else {
                return overflow_obj.bottom;
            }
        }

        function _placement_attempt(_dir, _top_offset?, _left_offset?) {
            _top_offset = _top_offset ? _top_offset : 0;
            _left_offset = _left_offset ? _left_offset : 0;
            nodel.style("white-space", "nowrap");
            coords = directionCallbacks.get(_dir).apply(this);
            nodel.classed(_dir, true)
                .style("top", (coords.top + poffset[0] - _top_offset) + "px")
                .style("left", (coords.left + poffset[1] - _left_offset) + "px");
            const nodel_bbox = nodel.node().getBoundingClientRect();
            const ret = nodel_bbox.top > root_bbox.top
                && nodel_bbox.left > root_bbox.left
                && nodel_bbox.bottom < root_bbox.bottom
                && nodel_bbox.right < root_bbox.right
                ;
            placement_overflow[_dir] = {
                top: root_bbox.top - nodel_bbox.top,
                right: nodel_bbox.right - root_bbox.right,
                bottom: nodel_bbox.bottom - root_bbox.bottom,
                left: root_bbox.left - nodel_bbox.left
            };
            nodel.style("white-space", "normal");
            placement_overflow[_dir].total_overflow = Object.keys(placement_overflow[_dir])
                .filter(side => placement_overflow[_dir][side] > 0)
                .reduce((sum, side) => {
                    const side_overflow = placement_overflow[_dir][side];
                    return sum + side_overflow;
                }, 0);
            if (placement_overflow[least_overflow_direction].total_overflow > placement_overflow[_dir].total_overflow) {
                least_overflow_direction = _dir;
            }
            if (!ret) {
                nodel.classed(_dir, false);
            }
            return ret;
        }
    };

    // Public - hide the tooltip
    //
    // Returns a tip
    tip.hide = function () {
        const nodel = getNodeEl();
        nodel.style("opacity", 0).style("pointer-events", "none");
        return tip;
    };

    // Public: Proxy attr calls to the d3 tip container.
    // Sets or gets attribute value.
    //
    // n - name of the attribute
    // v - value of the attribute
    //
    // Returns tip or attribute value
    // eslint-disable-next-line no-unused-vars
    tip.attr = function (n, v) {
        if (arguments.length < 2 && typeof n === "string") {
            return getNodeEl().attr(n);
        }

        const args = Array.prototype.slice.call(arguments);
        selection.prototype.attr.apply(getNodeEl(), args);
        return tip;
    };

    // Public: Proxy style calls to the d3 tip container.
    // Sets or gets a style value.
    //
    // n - name of the property
    // v - value of the property
    //
    // Returns tip or style property value
    // eslint-disable-next-line no-unused-vars
    tip.style = function (n, v) {
        if (arguments.length < 2 && typeof n === "string") {
            return getNodeEl().style(n);
        }

        const args = Array.prototype.slice.call(arguments);
        selection.prototype.style.apply(getNodeEl(), args);
        return tip;
    };

    // Public: Set or get the direction of the tooltip
    //
    // v - One of n(north), s(south), e(east), or w(west), nw(northwest),
    //     sw(southwest), ne(northeast) or se(southeast)
    //
    // Returns tip or direction
    tip.direction = function (v) {
        if (!arguments.length) return direction;
        direction = v == null ? v : functor(v);

        return tip;
    };

    // Public: Sets or gets the offset of the tip
    //
    // v - Array of [x, y] offset
    //
    // Returns offset or
    tip.offset = function (v) {
        if (!arguments.length) return offset;
        offset = v == null ? v : functor(v);

        return tip;
    };

    // Public: sets or gets the html value of the tooltip
    //
    // v - String value of the tip
    //
    // Returns html value or tip
    tip.html = function (v) {
        if (!arguments.length) return html;
        html = v == null ? v : functor(v);

        return tip;
    };

    // Public: sets or gets the root element anchor of the tooltip
    //
    // v - root element of the tooltip
    //
    // Returns root node of tip
    tip.rootElement = function (v) {
        if (!arguments.length) return rootElement;
        rootElement = functor(v);

        return tip;
    };

    // Public: destroys the tooltip and removes it from the DOM
    //
    // Returns a tip
    tip.destroy = function () {
        if (node) {
            getNodeEl().remove();
            node = null;
        }
        return tip;
    };

    function d3TipDirection() { return "n"; }
    function d3TipOffset() { return [0, 0]; }
    function d3TipHTML() { return " "; }

    const directionCallbacks = map({
        n: directionNorth,
        s: directionSouth,
        e: directionEast,
        w: directionWest,
        nw: directionNorthWest,
        ne: directionNorthEast,
        sw: directionSouthWest,
        se: directionSouthEast
    });
    const directions = directionCallbacks.keys();

    function directionNorth() {
        const bbox = getScreenBBox(window);
        return {
            top: bbox.n.y - node.offsetHeight,
            left: bbox.n.x - node.offsetWidth / 2
        };
    }

    function directionSouth() {
        const bbox = getScreenBBox(window);
        return {
            top: bbox.s.y + 8,
            left: bbox.s.x - node.offsetWidth / 2
        };
    }

    function directionEast() {
        const bbox = getScreenBBox(window);
        return {
            top: bbox.e.y - node.offsetHeight / 2,
            left: bbox.e.x + 8
        };
    }

    function directionWest() {
        const bbox = getScreenBBox(window);
        return {
            top: bbox.w.y - node.offsetHeight / 2,
            left: bbox.w.x - node.offsetWidth - 8
        };
    }

    function directionNorthWest() {
        const bbox = getScreenBBox(window);
        return {
            top: bbox.nw.y - node.offsetHeight,
            left: bbox.nw.x - node.offsetWidth
        };
    }

    function directionNorthEast() {
        const bbox = getScreenBBox(window);
        return {
            top: bbox.ne.y - node.offsetHeight,
            left: bbox.ne.x
        };
    }

    function directionSouthWest() {
        const bbox = getScreenBBox(window);
        return {
            top: bbox.sw.y,
            left: bbox.sw.x - node.offsetWidth
        };
    }

    function directionSouthEast() {
        const bbox = getScreenBBox(window);
        return {
            top: bbox.se.y,
            left: bbox.se.x
        };
    }

    function initNode() {
        const div = select(document.createElement("div"));
        div
            .attr("class", "d3-tip")
            .style("position", "absolute")
            .style("top", 0)
            .style("opacity", 0)
            .style("pointer-events", "none")
            .style("box-sizing", "border-box");

        return div.node();
    }

    function getSVGNode(element) {
        const svgNode = element.node();
        if (!svgNode) return null;
        if (svgNode.tagName.toLowerCase() === "svg") return svgNode;
        return svgNode.ownerSVGElement;
    }

    function getNodeEl() {
        if (node == null) {
            node = initNode();
            // re-add node to DOM
            rootElement().appendChild(node);
        }
        return select(node);
    }

    // Private - gets the screen coordinates of a shape
    //
    // Given a shape on the screen, will return an SVGPoint for the directions
    // n(north), s(south), e(east), w(west), ne(northeast), se(southeast),
    // nw(northwest), sw(southwest).
    //
    //    +-+-+
    //    |   |
    //    +   +
    //    |   |
    //    +-+-+
    //
    // Returns an Object {n, s, e, w, nw, sw, ne, se}
    function getScreenBBox(targetShape) {
        let targetel = target || targetShape;

        while (targetel.getCTM == null && targetel.parentNode != null) {
            targetel = targetel.parentNode;
        }

        const bbox: any = {};
        const matrix = targetel.getCTM();
        const tbbox = targetel.getBBox();
        const width = tbbox.width;
        const height = tbbox.height;
        const x = tbbox.x;
        const y = tbbox.y;

        point.x = x;
        point.y = y;
        bbox.nw = point.matrixTransform(matrix);
        point.x += width;
        bbox.ne = point.matrixTransform(matrix);
        point.y += height;
        bbox.se = point.matrixTransform(matrix);
        point.x -= width;
        bbox.sw = point.matrixTransform(matrix);
        point.y -= height / 2;
        bbox.w = point.matrixTransform(matrix);
        point.x += width;
        bbox.e = point.matrixTransform(matrix);
        point.x -= width / 2;
        point.y -= height / 2;
        bbox.n = point.matrixTransform(matrix);
        point.y += height;
        bbox.s = point.matrixTransform(matrix);

        return bbox;
    }

    // Private - replace D3JS 3.X d3.functor() function
    function functor(v) {
        return typeof v === "function" ? v : function () {
            return v;
        };
    }

    return tip;
}