Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

d3plus-text

Package Overview
Dependencies
Maintainers
1
Versions
89
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

d3plus-text - npm Package Compare versions

Comparing version 0.2.1 to 0.3.0

.rollup.js

561

build/d3plus-text.js
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-selection')) :
typeof define === 'function' && define.amd ? define('d3plus-text', ['exports', 'd3-selection'], factory) :
(factory((global.d3plus_text = {}),global.d3_selection));
}(this, function (exports,d3Selection) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3')) :
typeof define === 'function' && define.amd ? define('d3plus-text', ['exports', 'd3'], factory) :
(factory((global.d3plus_text = {}),global.d3));
}(this, function (exports,d3) { 'use strict';
var version = "0.2.0";
d3 = 'default' in d3 ? d3['default'] : d3;
var version = "0.3.0";
/**

@@ -20,13 +22,49 @@ Wraps non-function variables in a simple return function.

/**
The default ellipsis function.
@private
@function width
@desc Given a text string, returns the predicted pixel width of the string when placed into DOM.
@param {String|Array} text Can be either a single string or an array of strings to analyze.
@param {Object} [style] An object of CSS font styles to apply. Accepts any of the valid [CSS font property](http://www.w3schools.com/cssref/pr_font_font.asp) values.
*/
function boxEllipsis(_) {
if (_.includes(" ")) {
var a = _.split(/\s+/);
return _.replace(" " + a[a.length - 1], "...");
function measure (text) {
var style = arguments.length <= 1 || arguments[1] === undefined ? { "font-size": 10, "font-family": "sans-serif" } : arguments[1];
var canvas = d3.select("body").selectAll("canvas#d3plus-text-size").data([0]);
canvas.enter().append("canvas").attr("id", "d3plus-text-size").style("position", "absolute").style("left", "-9999px").style("top", "-9999px").style("visibility", "hidden").style("display", "block");
var context = canvas.node().getContext("2d");
var font = [];
if ("font-style" in style) font.push(style["font-style"]);
if ("font-variant" in style) font.push(style["font-variant"]);
if ("font-weight" in style) font.push(style["font-weight"]);
if ("font-size" in style) {
var s = style["font-size"] + "px";
if ("line-height" in style) s += "/" + style["line-height"] + "px";
font.push(s);
}
return "...";
if ("font-family" in style) font.push(style["font-family"]);
context.font = font.join(" ");
if (text instanceof Array) return text.map(function (t) {
return context.measureText(t).width;
});
return context.measureText(text).width;
}
/**
The default height accessor function.
@private
*/
function boxHeight(d) {
return d.height || 200;
}
/**
The default id accessor function.
@private
*/
function boxId(d, i) {
return d.id || "" + i;
}
var splitChars = ["-", "/", ";", ":", "&"];

@@ -42,92 +80,300 @@ var splitRegex = new RegExp("[^\\s\\" + splitChars.join("\\") + "]+\\" + splitChars.join("?\\") + "?", "g");

/**
The default text accessor function.
@private
*/
function boxText(d) {
return d.text;
}
/**
The default width accessor function.
@private
*/
function boxWidth(d) {
return d.width || 200;
}
/**
The default x accessor function.
@private
*/
function boxX(d) {
return d.x || 0;
}
/**
The default y accessor function.
@private
*/
function boxY(d) {
return d.y || 0;
}
/**
@function box
@desc Creates a wrapped text box based on an array of data. If *data* is specified, immediately wraps the text based on the specified array and returns this box generator. If *data* is not specified on instantiation, it can be passed/updated after instantiation using the [data](#box.data) method.
@param {Array} [data = []]
@example <caption>a sample row of data</caption>
var data = {"text": "Hello D3plus, please wrap this sentence for me."};
@example <caption>passed to the generator</caption>
box([data]);
@example <caption>creates the following</caption>
<text class="d3plus-text-box" id="d3plus-text-box-0" text-anchor="start" font-family="Helvetica Neue" font-size="16px" transform="translate(0,-3.6)">
<tspan dominant-baseline="alphabetic" opacity="1" x="0px" dx="0px" dy="18px" style="baseline-shift: 0%;">
Hello D3plus, please wrap
</tspan>
<tspan dominant-baseline="alphabetic" opacity="1" x="0px" dx="0px" dy="18px" style="baseline-shift: 0%;">
this sentence for me.
</tspan>
</text>
*/
function box () {
var data = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];
var ellipsis = boxEllipsis,
/**
The default ellipsis function.
@private
*/
function boxEllipsis(_) {
return _ + "...";
}
var delay = 0,
duration = 0,
ellipsis = boxEllipsis,
fontColor = undefined,
fontFamily = undefined,
fontMax = constant(50),
fontMin = constant(8),
fontResize = constant(false),
fontSize = undefined,
height = constant(200),
height = boxHeight,
id = boxId,
lineHeight = undefined,
select = undefined,
split = boxSplit,
text = undefined,
width = constant(200),
x = constant(0),
y = constant(0);
text = boxText,
textAnchor = constant("start"),
verticalAlign = constant("top"),
width = boxWidth,
x = boxX,
y = boxY;
/**
The inner return object and draw function that gets assigned the public methods.
@private
*/
function box() {
var fS = fontSize(),
h = height(select),
lH = lineHeight(),
t = text(select),
w = width(select),
xP = x(select);
if (select === void 0) box.select(d3.select("body").append("svg").style("width", window.innerWidth + "px").style("height", window.innerHeight + "px").node());
var l = 1,
p = "";
var boxes = select.selectAll(".d3plus-text-box").data(data, id);
select.attr("y", y(select) + "px").attr("fill", fontColor()).attr("font-family", fontFamily()).attr("font-size", fS + "px").size("font-size", fS + "px");
boxes.enter().append("text").attr("class", "d3plus-text-box").attr("id", function (d, i) {
return "d3plus-text-box-" + id(d, i);
});
function tspanStyle(tspan) {
tspan.attr("x", xP + "px").attr("dx", "0px").attr("dy", lH + "px").attr("dominant-baseline", "alphabetic").style("baseline-shift", "0%");
}
boxes.attr("y", function (d, i) {
return y(d, i) + "px";
}).attr("fill", function (d, i) {
return fontColor(d, i);
}).attr("text-anchor", function (d, i) {
return textAnchor(d, i);
}).attr("font-family", function (d, i) {
return fontFamily(d, i);
}).each(function (d, i) {
var tspan = select.html("").append("tspan").call(tspanStyle);
function addWord(word) {
var curr = tspan.html(),
temp = p + word;
var join = t.charAt(temp.length);
tspan.html(curr + word);
var resize = fontResize(d, i);
if (select.node().getBBox().height > h) {
tspan.remove();
tspan = d3Selection.select(select.node().lastChild);
if (tspan.size()) {
var tl = tspan.html();
var e = ellipsis(tl);
tspan.html(e ? e : tl);
var fS = resize ? fontMax(d, i) : fontSize(d, i),
lH = resize ? fS * 1.1 : lineHeight(d, i),
line = 1,
lineData = [""],
sizes = undefined;
var fMax = fontMax(d, i),
fMin = fontMin(d, i),
h = height(d, i),
space = measure(" ", style),
t = text(d, i),
tA = textAnchor(d, i),
vA = verticalAlign(d, i),
w = width(d, i),
words = split(t, i);
var dx = tA === "start" ? 0 : tA === "end" ? w : w / 2;
var style = {
"font-family": fontFamily(d, i),
"font-size": fS,
"line-height": lH
};
/**
Figures out the lineData to be used for wrapping.
@private
*/
function checkSize() {
line = 1;
lineData = [""];
if (fS < fMin) {
lineData = [];
return;
} else if (fS > fMax) fS = fMax;
var textProg = "",
widthProg = 0;
if (resize) {
lH = fS * 1.1;
style["font-size"] = fS;
style["line-height"] = lH;
}
return false;
} else if (tspan.node().getComputedTextLength() > w) {
tspan.html(curr.trimRight());
if (l === 1 && curr === "") return false;
tspan = select.append("tspan").call(tspanStyle);
l++;
return addWord(word);
} else {
var char = join === " " ? " " : "";
p = temp + char;
tspan.html(curr + word + char);
return true;
sizes = measure(words, style);
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = words[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var word = _step.value;
var wordWidth = sizes[words.indexOf(word)];
if (wordWidth > w) break;
var nextChar = t.charAt(textProg.length + word.length);
if (nextChar === " ") word += nextChar;
if (widthProg + wordWidth > w - fS) {
line++;
if (lH * line > h) {
if (resize) {
fS--;
if (fS < fMin) {
lineData = [];
break;
}
checkSize();
} else lineData[line - 2] = ellipsis(lineData[line - 2].trimRight());
break;
}
widthProg = 0;
lineData.push(word);
} else lineData[line - 1] += word;
textProg += word;
widthProg += wordWidth;
if (nextChar === " ") widthProg += space;
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
}
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
if (h > lH || resize) {
try {
for (var _iterator = split(t)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var word = _step.value;
if (resize) {
var r = addWord(word);
if (!r) break;
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
sizes = measure(words, style);
var areaMod = 1.165 + w / h * 0.1,
boxArea = w * h,
maxWidth = d3.max(sizes),
textArea = d3.sum(sizes, function (d) {
return d * lH;
}) * areaMod;
if (maxWidth > w || textArea > boxArea) {
var areaRatio = Math.sqrt(boxArea / textArea),
widthRatio = w / maxWidth;
var sizeRatio = d3.min([areaRatio, widthRatio]);
fS = Math.floor(fS * sizeRatio);
}
var heightMax = Math.floor(h * 0.8);
if (fS > heightMax) fS = heightMax;
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
checkSize();
d3.select(this).attr("font-size", fS + "px").style("font-size", fS + "px");
}
}
var tH = line * lH;
var y = vA === "top" ? 0 : vA === "middle" ? h / 2 - tH / 2 : h - tH;
y -= lH * 0.2;
d3.select(this).transition().duration(duration).attr("transform", "translate(0," + y + ")");
/**
Styles to apply to each <tspan> element.
@private
*/
function tspanStyle(tspan) {
tspan.text(function (d) {
return d.trimRight();
}).attr("x", x(d, i) + "px").attr("dx", dx + "px").attr("dy", lH + "px");
}
var tspans = d3.select(this).selectAll("tspan").data(lineData);
tspans.exit().transition().duration(duration).attr("opacity", 0).remove();
tspans.transition().duration(duration).call(tspanStyle);
tspans.enter().append("tspan").attr("dominant-baseline", "alphabetic").style("baseline-shift", "0%").attr("opacity", 0).call(tspanStyle).transition().duration(duration).delay(delay).attr("opacity", 1);
});
return box;
}
/**
@memberof box
@desc If *data* is specified, sets the data array to the specified array and returns this box generator. If *data* is not specified, returns the current data array. A text box will be drawn for each object in the array.
@param {Array} [*data* = []]
*/
box.data = function (_) {
return arguments.length ? (data = _, box) : data;
};
/**
@memberof box
@desc If *value* is specified, sets the animation delay to the specified number and returns this box generator. If *value* is not specified, returns the current animation delay.
@param {Number} [*value* = 0]
*/
box.delay = function (_) {
return arguments.length ? (delay = _, box) : delay;
};
/**
@memberof box
@desc If *value* is specified, sets the animation duration to the specified number and returns this box generator. If *value* is not specified, returns the current animation duration.
@param {Number} [*value* = 0]
*/
box.duration = function (_) {
return arguments.length ? (duration = _, box) : duration;
};
/**
@memberof box
@desc If *value* is specified, sets the ellipsis method to the specified function or string and returns this box generator. If *value* is not specified, returns the current ellipsis method, which simply adds an ellipsis to the string by default.
@param {Function|String} [*value*]
@example
function(d) {
return d + "...";
}
*/
box.ellipsis = function (_) {

@@ -137,2 +383,7 @@ return arguments.length ? (ellipsis = typeof _ === "function" ? _ : constant(_), box) : ellipsis;

/**
@memberof box
@desc If *value* is specified, sets the font color accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current font color accessor, which is inferred from the [container element](#box.select) by default.
@param {Function|String} [*value*]
*/
box.fontColor = function (_) {

@@ -142,2 +393,7 @@ return arguments.length ? (fontColor = typeof _ === "function" ? _ : constant(_), box) : fontColor;

/**
@memberof box
@desc If *value* is specified, sets the font family accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current font family accessor, which is inferred from the [container element](#box.select) by default.
@param {Function|String} [*value*]
*/
box.fontFamily = function (_) {

@@ -147,6 +403,52 @@ return arguments.length ? (fontFamily = typeof _ === "function" ? _ : constant(_), box) : fontFamily;

/**
@memberof box
@desc If *value* is specified, sets the maximum font size accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current maximum font size accessor. The maximum font size is used when [resizing fonts](#box.fontResize) dynamically.
@param {Function|Number} [*value* = 50]
*/
box.fontMax = function (_) {
return arguments.length ? (fontMax = typeof _ === "function" ? _ : constant(_), box) : fontMax;
};
/**
@memberof box
@desc If *value* is specified, sets the minimum font size accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current minimum font size accessor. The minimum font size is used when [resizing fonts](#box.fontResize) dynamically.
@param {Function|Number} [*value* = 8]
*/
box.fontMin = function (_) {
return arguments.length ? (fontMin = typeof _ === "function" ? _ : constant(_), box) : fontMin;
};
/**
@memberof box
@desc If *value* is specified, sets the font resizing accessor to the specified function or boolean and returns this box generator. If *value* is not specified, returns the current font resizing accessor.
@param {Function|Boolean} [*value* = false]
*/
box.fontResize = function (_) {
return arguments.length ? (fontResize = typeof _ === "function" ? _ : constant(_), box) : fontResize;
};
/**
@memberof box
@desc If *value* is specified, sets the font size accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current font size accessor, which is inferred from the [container element](#box.select) by default.
@param {Function|Number} [*value*]
*/
box.fontSize = function (_) {
return arguments.length ? (fontSize = typeof _ === "function" ? _ : constant(_), box) : fontSize;
if (arguments.length) {
fontSize = typeof _ === "function" ? _ : constant(_);
if (lineHeight === void 0) lineHeight = constant(Math.ceil(fontSize() * 1.1));
return box;
}
return fontSize;
};
/**
@memberof box
@desc If *value* is specified, sets the height accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current height accessor.
@param {Function|Number} [*value*]
@example
function(d) {
return d.height || 200;
}
*/
box.height = function (_) {

@@ -156,14 +458,35 @@ return arguments.length ? (height = typeof _ === "function" ? _ : constant(_), box) : height;

/**
@memberof box
@desc If *value* is specified, sets the id accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current id accessor.
@param {Function|Number} [*value*]
@example
function(d, i) {
return d.id || i + "";
}
*/
box.id = function (_) {
return arguments.length ? (id = typeof _ === "function" ? _ : constant(_), box) : id;
};
/**
@memberof box
@desc If *value* is specified, sets the line height accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current line height accessor, which is 1.1 times the [font size](#box.fontSize) by default.
@param {Function|Number} [*value*]
*/
box.lineHeight = function (_) {
return arguments.length ? (lineHeight = typeof _ === "function" ? _ : constant(_), box) : lineHeight;
};
/**
@memberof box
@desc If *selector* is specified, sets the SVG container element to the specified d3 selector or DOM element and returns this box generator. If *selector* is not specified, returns the current SVG container element, which adds an SVG element to the page by default.
@param {String|HTMLElement} [*selector*]
*/
box.select = function (_) {
if (arguments.length) {
select = d3Selection.select(_);
if (text === void 0) {
text = constant(select.text());
if (fontColor === void 0) fontColor = constant(select.style("font-color"));
if (fontFamily === void 0) fontFamily = constant(select.style("font-family"));
if (fontSize === void 0) {
fontSize = constant(parseFloat(select.style("font-size"), 10));
lineHeight = constant(Math.ceil(fontSize() * 1.1));
}
}
select = d3.select(_);
if (fontColor === void 0) box.fontColor(select.style("font-color"));
if (fontFamily === void 0) box.fontFamily(select.style("font-family"));
if (fontSize === void 0) box.fontSize(parseFloat(select.style("font-size"), 10));
return box;

@@ -174,2 +497,7 @@ }

/**
@memberof box
@desc If *value* is specified, sets the word split function to the specified function and returns this box generator. If *value* is not specified, returns the current word split function.
@param {Function} [*value*] A function that, when passed a string, is expected to return that string split into an array of words to wrap. The default split function splits strings on the following characters: `-`, `/`, `;`, `:`, `&`
*/
box.split = function (_) {

@@ -179,2 +507,11 @@ return arguments.length ? (split = _, box) : split;

/**
@memberof box
@desc If *value* is specified, sets the text accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current text accessor.
@param {Function|String} [*value*]
@example
function(d) {
return d.text;
}
*/
box.text = function (_) {

@@ -184,2 +521,29 @@ return arguments.length ? (text = typeof _ === "function" ? _ : constant(_), box) : text;

/**
@memberof box
@desc If *value* is specified, sets the horizontal text anchor accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current horizontal text anchor accessor.
@param {Function|String} [*value* = "start"] Analagous to the SVG [text-anchor](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/text-anchor) property.
*/
box.textAnchor = function (_) {
return arguments.length ? (textAnchor = typeof _ === "function" ? _ : constant(_), box) : textAnchor;
};
/**
@memberof box
@desc If *value* is specified, sets the vertical alignment accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current vertical alignment accessor.
@param {Function|String} [*value* = "top"] Accepts `"top"`, `"middle"`, and `"bottom"`.
*/
box.verticalAlign = function (_) {
return arguments.length ? (verticalAlign = typeof _ === "function" ? _ : constant(_), box) : verticalAlign;
};
/**
@memberof box
@desc If *value* is specified, sets the width accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current width accessor.
@param {Function|Number} [*value*]
@example
function(d) {
return d.width || 200;
}
*/
box.width = function (_) {

@@ -189,2 +553,11 @@ return arguments.length ? (width = typeof _ === "function" ? _ : constant(_), box) : width;

/**
@memberof box
@desc If *value* is specified, sets the x accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current x accessor. The number returned should correspond to the left position of the box.
@param {Function|Number} [*value*]
@example
function(d) {
return d.x || 0;
}
*/
box.x = function (_) {

@@ -194,2 +567,11 @@ return arguments.length ? (x = typeof _ === "function" ? _ : constant(_), box) : x;

/**
@memberof box
@desc If *value* is specified, sets the y accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current y accessor. The number returned should correspond to the top position of the box.
@param {Function|Number} [*value*]
@example
function(d) {
return d.y || 0;
}
*/
box.y = function (_) {

@@ -199,3 +581,3 @@ return arguments.length ? (y = typeof _ === "function" ? _ : constant(_), box) : y;

return box;
return data.length ? box() : box;
}

@@ -205,3 +587,4 @@

exports.box = box;
exports.width = measure;
}));

2

build/d3plus-text.min.js

@@ -1,1 +0,1 @@

!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports,require("d3-selection")):"function"==typeof define&&define.amd?define("d3plus-text",["exports","d3-selection"],n):n(t.d3plus_text={},t.d3_selection)}(this,function(t,n){"use strict";function e(t){return function(){return t}}function r(t){if(t.includes(" ")){var n=t.split(/\s+/);return t.replace(" "+n[n.length-1],"...")}return"..."}function o(t){return t.match(l)}function i(){function t(){function t(t){t.attr("x",b+"px").attr("dx","0px").attr("dy",y+"px").attr("dominant-baseline","alphabetic").style("baseline-shift","0%")}function e(r){var u=w.html(),f=j+r,l=v.charAt(f.length);if(w.html(u+r),s.node().getBBox().height>o){if(w.remove(),w=n.select(s.node().lastChild),w.size()){var a=w.html(),c=i(a);w.html(c?c:a)}return!1}if(w.node().getComputedTextLength()>x)return w.html(u.trimRight()),1===z&&""===u?!1:(w=s.append("tspan").call(t),z++,e(r));var p=" "===l?" ":"";return j=f+p,w.html(u+r+p),!0}var r=l(),o=a(s),y=c(),v=d(s),x=h(s),b=g(s),z=1,j="";s.attr("y",m(s)+"px").attr("fill",u()).attr("font-family",f()).attr("font-size",r+"px").size("font-size",r+"px");var w=s.html("").append("tspan").call(t),C=!0,B=!1,F=void 0;try{for(var R,S=p(v)[Symbol.iterator]();!(C=(R=S.next()).done);C=!0){var _=R.value,k=e(_);if(!k)break}}catch(q){B=!0,F=q}finally{try{!C&&S["return"]&&S["return"]()}finally{if(B)throw F}}}var i=r,u=void 0,f=void 0,l=void 0,a=e(200),c=void 0,s=void 0,p=o,d=void 0,h=e(200),g=e(0),m=e(0);return t.ellipsis=function(n){return arguments.length?(i="function"==typeof n?n:e(n),t):i},t.fontColor=function(n){return arguments.length?(u="function"==typeof n?n:e(n),t):u},t.fontFamily=function(n){return arguments.length?(f="function"==typeof n?n:e(n),t):f},t.fontSize=function(n){return arguments.length?(l="function"==typeof n?n:e(n),t):l},t.height=function(n){return arguments.length?(a="function"==typeof n?n:e(n),t):a},t.select=function(r){return arguments.length?(s=n.select(r),void 0===d&&(d=e(s.text()),void 0===u&&(u=e(s.style("font-color"))),void 0===f&&(f=e(s.style("font-family"))),void 0===l&&(l=e(parseFloat(s.style("font-size"),10)),c=e(Math.ceil(1.1*l())))),t):s},t.split=function(n){return arguments.length?(p=n,t):p},t.text=function(n){return arguments.length?(d="function"==typeof n?n:e(n),t):d},t.width=function(n){return arguments.length?(h="function"==typeof n?n:e(n),t):h},t.x=function(n){return arguments.length?(g="function"==typeof n?n:e(n),t):g},t.y=function(n){return arguments.length?(m="function"==typeof n?n:e(n),t):m},t}var u="0.2.0",f=["-","/",";",":","&"],l=new RegExp("[^\\s\\"+f.join("\\")+"]+\\"+f.join("?\\")+"?","g");t.version=u,t.box=i});
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports,require("d3")):"function"==typeof define&&define.amd?define("d3plus-text",["exports","d3"],n):n(t.d3plus_text={},t.d3)}(this,function(t,n){"use strict";function e(t){return function(){return t}}function i(t){var e=arguments.length<=1||void 0===arguments[1]?{"font-size":10,"font-family":"sans-serif"}:arguments[1],i=n.select("body").selectAll("canvas#d3plus-text-size").data([0]);i.enter().append("canvas").attr("id","d3plus-text-size").style("position","absolute").style("left","-9999px").style("top","-9999px").style("visibility","hidden").style("display","block");var r=i.node().getContext("2d"),o=[];if("font-style"in e&&o.push(e["font-style"]),"font-variant"in e&&o.push(e["font-variant"]),"font-weight"in e&&o.push(e["font-weight"]),"font-size"in e){var u=e["font-size"]+"px";"line-height"in e&&(u+="/"+e["line-height"]+"px"),o.push(u)}return"font-family"in e&&o.push(e["font-family"]),r.font=o.join(" "),t instanceof Array?t.map(function(t){return r.measureText(t).width}):r.measureText(t).width}function r(t){return t.height||200}function o(t,n){return t.id||""+n}function u(t){return t.match(g)}function a(t){return t.text}function f(t){return t.width||200}function s(t){return t.x||0}function l(t){return t.y||0}function c(){function t(t){return t+"..."}function c(){void 0===j&&c.select(n.select("body").append("svg").style("width",window.innerWidth+"px").style("height",window.innerHeight+"px").node());var t=j.selectAll(".d3plus-text-box").data(d,A);return t.enter().append("text").attr("class","d3plus-text-box").attr("id",function(t,n){return"d3plus-text-box-"+A(t,n)}),t.attr("y",function(t,n){return H(t,n)+"px"}).attr("fill",function(t,n){return y(t,n)}).attr("text-anchor",function(t,n){return C(t,n)}).attr("font-family",function(t,n){return m(t,n)}).each(function(t,e){function r(){if(s=1,l=[""],y>a)return void(l=[]);a>d&&(a=d);var t="",n=0;u&&(f=1.1*a,B["font-size"]=a,B["line-height"]=f),c=i(W,B);var e=!0,o=!1,h=void 0;try{for(var g,m=W[Symbol.iterator]();!(e=(g=m.next()).done);e=!0){var x=g.value,v=c[W.indexOf(x)];if(v>O)break;var b=H.charAt(t.length+x.length);if(" "===b&&(x+=b),n+v>O-a){if(s++,f*s>A){if(u){if(a--,y>a){l=[];break}r()}else l[s-2]=p(l[s-2].trimRight());break}n=0,l.push(x)}else l[s-1]+=x;t+=x,n+=v," "===b&&(n+=j)}}catch(w){o=!0,h=w}finally{try{!e&&m["return"]&&m["return"]()}finally{if(o)throw h}}}function o(n){n.text(function(t){return t.trimRight()}).attr("x",q(t,e)+"px").attr("dx",_+"px").attr("dy",f+"px")}var u=b(t,e),a=u?x(t,e):w(t,e),f=u?1.1*a:M(t,e),s=1,l=[""],c=void 0,d=x(t,e),y=v(t,e),A=z(t,e),j=i(" ",B),H=R(t,e),T=C(t,e),E=F(t,e),O=S(t,e),W=k(H,e),_="start"===T?0:"end"===T?O:O/2,B={"font-family":m(t,e),"font-size":a,"line-height":f};if(A>f||u){if(u){c=i(W,B);var D=1.165+O/A*.1,G=O*A,I=n.max(c),J=n.sum(c,function(t){return t*f})*D;if(I>O||J>G){var K=Math.sqrt(G/J),L=O/I,N=n.min([K,L]);a=Math.floor(a*N)}var P=Math.floor(.8*A);a>P&&(a=P)}r(),n.select(this).attr("font-size",a+"px").style("font-size",a+"px")}var Q=s*f,U="top"===E?0:"middle"===E?A/2-Q/2:A-Q;U-=.2*f,n.select(this).transition().duration(g).attr("transform","translate(0,"+U+")");var V=n.select(this).selectAll("tspan").data(l);V.exit().transition().duration(g).attr("opacity",0).remove(),V.transition().duration(g).call(o),V.enter().append("tspan").attr("dominant-baseline","alphabetic").style("baseline-shift","0%").attr("opacity",0).call(o).transition().duration(g).delay(h).attr("opacity",1)}),c}var d=arguments.length<=0||void 0===arguments[0]?[]:arguments[0],h=0,g=0,p=t,y=void 0,m=void 0,x=e(50),v=e(8),b=e(!1),w=void 0,z=r,A=o,M=void 0,j=void 0,k=u,R=a,C=e("start"),F=e("top"),S=f,q=s,H=l;return c.data=function(t){return arguments.length?(d=t,c):d},c.delay=function(t){return arguments.length?(h=t,c):h},c.duration=function(t){return arguments.length?(g=t,c):g},c.ellipsis=function(t){return arguments.length?(p="function"==typeof t?t:e(t),c):p},c.fontColor=function(t){return arguments.length?(y="function"==typeof t?t:e(t),c):y},c.fontFamily=function(t){return arguments.length?(m="function"==typeof t?t:e(t),c):m},c.fontMax=function(t){return arguments.length?(x="function"==typeof t?t:e(t),c):x},c.fontMin=function(t){return arguments.length?(v="function"==typeof t?t:e(t),c):v},c.fontResize=function(t){return arguments.length?(b="function"==typeof t?t:e(t),c):b},c.fontSize=function(t){return arguments.length?(w="function"==typeof t?t:e(t),void 0===M&&(M=e(Math.ceil(1.1*w()))),c):w},c.height=function(t){return arguments.length?(z="function"==typeof t?t:e(t),c):z},c.id=function(t){return arguments.length?(A="function"==typeof t?t:e(t),c):A},c.lineHeight=function(t){return arguments.length?(M="function"==typeof t?t:e(t),c):M},c.select=function(t){return arguments.length?(j=n.select(t),void 0===y&&c.fontColor(j.style("font-color")),void 0===m&&c.fontFamily(j.style("font-family")),void 0===w&&c.fontSize(parseFloat(j.style("font-size"),10)),c):j},c.split=function(t){return arguments.length?(k=t,c):k},c.text=function(t){return arguments.length?(R="function"==typeof t?t:e(t),c):R},c.textAnchor=function(t){return arguments.length?(C="function"==typeof t?t:e(t),c):C},c.verticalAlign=function(t){return arguments.length?(F="function"==typeof t?t:e(t),c):F},c.width=function(t){return arguments.length?(S="function"==typeof t?t:e(t),c):S},c.x=function(t){return arguments.length?(q="function"==typeof t?t:e(t),c):q},c.y=function(t){return arguments.length?(H="function"==typeof t?t:e(t),c):H},d.length?c():c}n="default"in n?n["default"]:n;var d="0.3.0",h=["-","/",";",":","&"],g=new RegExp("[^\\s\\"+h.join("\\")+"]+\\"+h.join("?\\")+"?","g");t.version=d,t.box=c,t.width=i});
export {version} from "./package.json";
export {default as box} from "./src/box";
export {default as width} from "./src/width";
{
"name": "d3plus-text",
"version": "0.2.1",
"description": "Smart SVG text box with line wrapping and automatic font scaling.",
"version": "0.3.0",
"description": "SVG text functions, including line wrapping with automatic font scaling.",
"main": "build/d3plus-text.js",

@@ -26,3 +26,3 @@ "jsnext:main": "index",

"dependencies": {
"d3-selection": "^0.5.1"
"d3": "^3.5.13"
},

@@ -33,7 +33,7 @@ "devDependencies": {

"eslint": "^1.10.3",
"jsdoc-to-markdown": "^1.1.1",
"jsdoc-to-markdown": "^1.3.3",
"node-static": "^0.7.7",
"nodemon": "^1.8.1",
"phantomjs": "^1.9.18",
"rollup": "^0.24.0",
"phantomjs": "^1.9.19",
"rollup": "^0.25.1",
"rollup-plugin-babel": "^2.3.8",

@@ -44,9 +44,11 @@ "rollup-plugin-json": "^2.0.0",

"scripts": {
"pretest": "mkdir -p build && rollup -c",
"pretest": "mkdir -p build && rollup -c .rollup.js",
"dev": "npm run serve & nodemon --watch src/ -x \"npm run pretest\"",
"docs": "jsdoc2md 'src/**/*.js' -t README.hbs > README.md",
"prepublish": "npm run docs && uglifyjs build/d3plus-text.js -c -m -o build/d3plus-text.min.js && rm -f build/d3plus-text.zip && zip -j build/d3plus-text.zip -- LICENSE README.md build/d3plus-text.js build/d3plus-text.min.js",
"min": "uglifyjs build/d3plus-text.js -c -m -o build/d3plus-text.min.js",
"prepublish": "npm test && npm run docs && npm run min && npm run zip",
"serve": "static -p 4000",
"test": "npm run serve & eslint src/* test/browser/*.js && casperjs test ./test/browser/*.js && killall node"
"test": "npm run serve & eslint index.js src/* && casperjs test ./test/browser/*.js && killall node",
"zip": "rm -f build/d3plus-text.zip && zip -j build/d3plus-text.zip -- LICENSE README.md build/d3plus-text.js build/d3plus-text.min.js"
}
}

@@ -8,4 +8,370 @@ # d3plus-text

A javascript library that contains various text functions, most notably SVG text wrapping with automatic font size scaling.
## Installation Options
* [NPM](#install.npm)
* [Browser](#install.browser)
* [AMD and CommonJS](#install.amd)
* [Custom Builds](#install.custom)
<a name="install.npm"></a>
### NPM
```sh
npm install d3plus-text
```
<a name="install.browser"></a>
### Browser
In a vanilla environment, a `d3plus_text` global is exported. To use a compiled version hosted on [d3js.org](https://d3js.org) and [d3plus.org](https://d3plus.org):
```html
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3plus.org/js/d3plus-text.v0.3.min.js"></script>
```
This library was written in ES6 and compiled using Babel for use in modern browsers. If you'd like to use it in older browsers that may not support some features, you will need to include the Babel polyfill. We're hosting the latest version on [d3plus.org](https://d3plus.org), so just include this line before loading the library:
```html
<script src="http://d3plus.org/js/polyfill.min.js"></script>
```
Otherwise, [click here](https://github.com/d3plus/d3plus-text/releases/latest) to download the latest release.
<a name="install.amd"></a>
### AMD and CommonJS
The released bundle natively supports both AMD and CommonJS, and vanilla environments.
<a name="install.custom"></a>
### Custom Builds
The source code is written using standard `import` and `export` statements. Create a custom build using [Rollup](https://github.com/rollup/rollup) or your preferred bundler. Take a look at the [index.js](https://github.com/d3plus/d3plus-text/blob/master/index.js) file to see the modules exported.
---
# API Reference
## Functions
<dl>
<dt><a href="#box">box([data])</a></dt>
<dd><p>Creates a wrapped text box based on an array of data. If <em>data</em> is specified, immediately wraps the text based on the specified array and returns this box generator. If <em>data</em> is not specified on instantiation, it can be passed/updated after instantiation using the <a href="#box.data">data</a> method.</p>
</dd>
<dt><a href="#width">width(text, [style])</a></dt>
<dd><p>Given a text string, returns the predicted pixel width of the string when placed into DOM.</p>
</dd>
</dl>
<a name="box"></a>
## box([data])
Creates a wrapped text box based on an array of data. If *data* is specified, immediately wraps the text based on the specified array and returns this box generator. If *data* is not specified on instantiation, it can be passed/updated after instantiation using the [data](#box.data) method.
**Kind**: global function
| Param | Type | Default |
| --- | --- | --- |
| [data] | <code>Array</code> | <code>[]</code> |
**Example** *(a sample row of data)*
```js
var data = {"text": "Hello D3plus, please wrap this sentence for me."};
```
**Example** *(passed to the generator)*
```js
box([data]);
```
**Example** *(creates the following)*
```js
<text class="d3plus-text-box" id="d3plus-text-box-0" text-anchor="start" font-family="Helvetica Neue" font-size="16px" transform="translate(0,-3.6)">
<tspan dominant-baseline="alphabetic" opacity="1" x="0px" dx="0px" dy="18px" style="baseline-shift: 0%;">
Hello D3plus, please wrap
</tspan>
<tspan dominant-baseline="alphabetic" opacity="1" x="0px" dx="0px" dy="18px" style="baseline-shift: 0%;">
this sentence for me.
</tspan>
</text>
```
* [box([data])](#box)
* [.data([*data*])](#box.data)
* [.delay([*value*])](#box.delay)
* [.duration([*value*])](#box.duration)
* [.ellipsis([*value*])](#box.ellipsis)
* [.fontColor([*value*])](#box.fontColor)
* [.fontFamily([*value*])](#box.fontFamily)
* [.fontMax([*value*])](#box.fontMax)
* [.fontMin([*value*])](#box.fontMin)
* [.fontResize([*value*])](#box.fontResize)
* [.fontSize([*value*])](#box.fontSize)
* [.height([*value*])](#box.height)
* [.id([*value*])](#box.id)
* [.lineHeight([*value*])](#box.lineHeight)
* [.select([*selector*])](#box.select)
* [.split([*value*])](#box.split)
* [.text([*value*])](#box.text)
* [.textAnchor([*value*])](#box.textAnchor)
* [.verticalAlign([*value*])](#box.verticalAlign)
* [.width([*value*])](#box.width)
* [.x([*value*])](#box.x)
* [.y([*value*])](#box.y)
<a name="box.data"></a>
### box.data([*data*])
If *data* is specified, sets the data array to the specified array and returns this box generator. If *data* is not specified, returns the current data array. A text box will be drawn for each object in the array.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type | Default |
| --- | --- | --- |
| [*data*] | <code>Array</code> | <code>[]</code> |
<a name="box.delay"></a>
### box.delay([*value*])
If *value* is specified, sets the animation delay to the specified number and returns this box generator. If *value* is not specified, returns the current animation delay.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type | Default |
| --- | --- | --- |
| [*value*] | <code>Number</code> | <code>0</code> |
<a name="box.duration"></a>
### box.duration([*value*])
If *value* is specified, sets the animation duration to the specified number and returns this box generator. If *value* is not specified, returns the current animation duration.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type | Default |
| --- | --- | --- |
| [*value*] | <code>Number</code> | <code>0</code> |
<a name="box.ellipsis"></a>
### box.ellipsis([*value*])
If *value* is specified, sets the ellipsis method to the specified function or string and returns this box generator. If *value* is not specified, returns the current ellipsis method, which simply adds an ellipsis to the string by default.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type |
| --- | --- |
| [*value*] | <code>function</code> &#124; <code>String</code> |
**Example**
```js
function(d) {
return d + "...";
}
```
<a name="box.fontColor"></a>
### box.fontColor([*value*])
If *value* is specified, sets the font color accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current font color accessor, which is inferred from the [container element](#box.select) by default.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type |
| --- | --- |
| [*value*] | <code>function</code> &#124; <code>String</code> |
<a name="box.fontFamily"></a>
### box.fontFamily([*value*])
If *value* is specified, sets the font family accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current font family accessor, which is inferred from the [container element](#box.select) by default.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type |
| --- | --- |
| [*value*] | <code>function</code> &#124; <code>String</code> |
<a name="box.fontMax"></a>
### box.fontMax([*value*])
If *value* is specified, sets the maximum font size accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current maximum font size accessor. The maximum font size is used when [resizing fonts](#box.fontResize) dynamically.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type | Default |
| --- | --- | --- |
| [*value*] | <code>function</code> &#124; <code>Number</code> | <code>50</code> |
<a name="box.fontMin"></a>
### box.fontMin([*value*])
If *value* is specified, sets the minimum font size accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current minimum font size accessor. The minimum font size is used when [resizing fonts](#box.fontResize) dynamically.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type | Default |
| --- | --- | --- |
| [*value*] | <code>function</code> &#124; <code>Number</code> | <code>8</code> |
<a name="box.fontResize"></a>
### box.fontResize([*value*])
If *value* is specified, sets the font resizing accessor to the specified function or boolean and returns this box generator. If *value* is not specified, returns the current font resizing accessor.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type | Default |
| --- | --- | --- |
| [*value*] | <code>function</code> &#124; <code>Boolean</code> | <code>false</code> |
<a name="box.fontSize"></a>
### box.fontSize([*value*])
If *value* is specified, sets the font size accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current font size accessor, which is inferred from the [container element](#box.select) by default.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type |
| --- | --- |
| [*value*] | <code>function</code> &#124; <code>Number</code> |
<a name="box.height"></a>
### box.height([*value*])
If *value* is specified, sets the height accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current height accessor.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type |
| --- | --- |
| [*value*] | <code>function</code> &#124; <code>Number</code> |
**Example**
```js
function(d) {
return d.height || 200;
}
```
<a name="box.id"></a>
### box.id([*value*])
If *value* is specified, sets the id accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current id accessor.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type |
| --- | --- |
| [*value*] | <code>function</code> &#124; <code>Number</code> |
**Example**
```js
function(d, i) {
return d.id || i + "";
}
```
<a name="box.lineHeight"></a>
### box.lineHeight([*value*])
If *value* is specified, sets the line height accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current line height accessor, which is 1.1 times the [font size](#box.fontSize) by default.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type |
| --- | --- |
| [*value*] | <code>function</code> &#124; <code>Number</code> |
<a name="box.select"></a>
### box.select([*selector*])
If *selector* is specified, sets the SVG container element to the specified d3 selector or DOM element and returns this box generator. If *selector* is not specified, returns the current SVG container element, which adds an SVG element to the page by default.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type |
| --- | --- |
| [*selector*] | <code>String</code> &#124; <code>HTMLElement</code> |
<a name="box.split"></a>
### box.split([*value*])
If *value* is specified, sets the word split function to the specified function and returns this box generator. If *value* is not specified, returns the current word split function.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type | Description |
| --- | --- | --- |
| [*value*] | <code>function</code> | A function that, when passed a string, is expected to return that string split into an array of words to wrap. The default split function splits strings on the following characters: `-`, `/`, `;`, `:`, `&` |
<a name="box.text"></a>
### box.text([*value*])
If *value* is specified, sets the text accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current text accessor.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type |
| --- | --- |
| [*value*] | <code>function</code> &#124; <code>String</code> |
**Example**
```js
function(d) {
return d.text;
}
```
<a name="box.textAnchor"></a>
### box.textAnchor([*value*])
If *value* is specified, sets the horizontal text anchor accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current horizontal text anchor accessor.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [*value*] | <code>function</code> &#124; <code>String</code> | <code>&quot;start&quot;</code> | Analagous to the SVG [text-anchor](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/text-anchor) property. |
<a name="box.verticalAlign"></a>
### box.verticalAlign([*value*])
If *value* is specified, sets the vertical alignment accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current vertical alignment accessor.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [*value*] | <code>function</code> &#124; <code>String</code> | <code>&quot;top&quot;</code> | Accepts `"top"`, `"middle"`, and `"bottom"`. |
<a name="box.width"></a>
### box.width([*value*])
If *value* is specified, sets the width accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current width accessor.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type |
| --- | --- |
| [*value*] | <code>function</code> &#124; <code>Number</code> |
**Example**
```js
function(d) {
return d.width || 200;
}
```
<a name="box.x"></a>
### box.x([*value*])
If *value* is specified, sets the x accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current x accessor. The number returned should correspond to the left position of the box.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type |
| --- | --- |
| [*value*] | <code>function</code> &#124; <code>Number</code> |
**Example**
```js
function(d) {
return d.x || 0;
}
```
<a name="box.y"></a>
### box.y([*value*])
If *value* is specified, sets the y accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current y accessor. The number returned should correspond to the top position of the box.
**Kind**: static method of <code>[box](#box)</code>
| Param | Type |
| --- | --- |
| [*value*] | <code>function</code> &#124; <code>Number</code> |
**Example**
```js
function(d) {
return d.y || 0;
}
```
<a name="width"></a>
## width(text, [style])
Given a text string, returns the predicted pixel width of the string when placed into DOM.
**Kind**: global function
| Param | Type | Description |
| --- | --- | --- |
| text | <code>String</code> &#124; <code>Array</code> | Can be either a single string or an array of strings to analyze. |
| [style] | <code>Object</code> | An object of CSS font styles to apply. Accepts any of the valid [CSS font property](http://www.w3schools.com/cssref/pr_font_font.asp) values. |

@@ -1,16 +0,21 @@

import {select as d3} from "d3-selection";
import {default as d3} from "d3";
import {default as constant} from "./constant";
import {default as measure} from "./width";
/**
The default ellipsis function.
The default height accessor function.
@private
*/
function boxEllipsis(_) {
if (_.includes(" ")) {
const a = _.split(/\s+/);
return _.replace(` ${a[a.length - 1]}`, "...");
}
return "...";
function boxHeight(d) {
return d.height || 200;
}
/**
The default id accessor function.
@private
*/
function boxId(d, i) {
return d.id || `${i}`;
}
const splitChars = ["-", "/", ";", ":", "&"],

@@ -27,84 +32,294 @@ splitRegex = new RegExp(`[^\\s\\${splitChars.join("\\")}]+\\${splitChars.join("?\\")}?`, "g");

export default function() {
/**
The default text accessor function.
@private
*/
function boxText(d) {
return d.text;
}
let ellipsis = boxEllipsis,
/**
The default width accessor function.
@private
*/
function boxWidth(d) {
return d.width || 200;
}
/**
The default x accessor function.
@private
*/
function boxX(d) {
return d.x || 0;
}
/**
The default y accessor function.
@private
*/
function boxY(d) {
return d.y || 0;
}
/**
@function box
@desc Creates a wrapped text box based on an array of data. If *data* is specified, immediately wraps the text based on the specified array and returns this box generator. If *data* is not specified on instantiation, it can be passed/updated after instantiation using the [data](#box.data) method.
@param {Array} [data = []]
@example <caption>a sample row of data</caption>
var data = {"text": "Hello D3plus, please wrap this sentence for me."};
@example <caption>passed to the generator</caption>
box([data]);
@example <caption>creates the following</caption>
<text class="d3plus-text-box" id="d3plus-text-box-0" text-anchor="start" font-family="Helvetica Neue" font-size="16px" transform="translate(0,-3.6)">
<tspan dominant-baseline="alphabetic" opacity="1" x="0px" dx="0px" dy="18px" style="baseline-shift: 0%;">
Hello D3plus, please wrap
</tspan>
<tspan dominant-baseline="alphabetic" opacity="1" x="0px" dx="0px" dy="18px" style="baseline-shift: 0%;">
this sentence for me.
</tspan>
</text>
*/
export default function(data = []) {
/**
The default ellipsis function.
@private
*/
function boxEllipsis(_) {
return `${_}...`;
}
let delay = 0,
duration = 0,
ellipsis = boxEllipsis,
fontColor,
fontFamily,
fontMax = constant(50),
fontMin = constant(8),
fontResize = constant(false),
fontSize,
height = constant(200),
height = boxHeight,
id = boxId,
lineHeight,
select,
split = boxSplit,
text,
width = constant(200),
x = constant(0),
y = constant(0);
text = boxText,
textAnchor = constant("start"),
verticalAlign = constant("top"),
width = boxWidth,
x = boxX,
y = boxY;
/**
The inner return object and draw function that gets assigned the public methods.
@private
*/
function box() {
const fS = fontSize(),
h = height(select),
lH = lineHeight(),
t = text(select),
w = width(select),
xP = x(select);
if (select === void 0) box.select(d3.select("body").append("svg").style("width", `${window.innerWidth}px`).style("height", `${window.innerHeight}px`).node());
let l = 1,
p = "";
const boxes = select.selectAll(".d3plus-text-box").data(data, id);
select
.attr("y", `${y(select)}px`)
.attr("fill", fontColor())
.attr("font-family", fontFamily())
.attr("font-size", `${fS}px`)
.size("font-size", `${fS}px`);
boxes.enter().append("text")
.attr("class", "d3plus-text-box")
.attr("id", (d, i) => `d3plus-text-box-${id(d, i)}`);
function tspanStyle(tspan) {
tspan
.attr("x", `${xP}px`)
.attr("dx", "0px")
.attr("dy", `${lH}px`)
.attr("dominant-baseline", "alphabetic")
.style("baseline-shift", "0%");
}
boxes
.attr("y", (d, i) => `${y(d, i)}px`)
.attr("fill", (d, i) => fontColor(d, i))
.attr("text-anchor", (d, i) => textAnchor(d, i))
.attr("font-family", (d, i) => fontFamily(d, i))
.each(function(d, i) {
let tspan = select.html("").append("tspan").call(tspanStyle);
function addWord(word) {
const curr = tspan.html(),
temp = p + word;
const join = t.charAt(temp.length);
tspan.html(curr + word);
const resize = fontResize(d, i);
if (select.node().getBBox().height > h) {
tspan.remove();
tspan = d3(select.node().lastChild);
if (tspan.size()) {
const tl = tspan.html();
const e = ellipsis(tl);
tspan.html(e ? e : tl);
let fS = resize ? fontMax(d, i) : fontSize(d, i),
lH = resize ? fS * 1.1 : lineHeight(d, i),
line = 1,
lineData = [""],
sizes;
const fMax = fontMax(d, i),
fMin = fontMin(d, i),
h = height(d, i),
space = measure(" ", style),
t = text(d, i),
tA = textAnchor(d, i),
vA = verticalAlign(d, i),
w = width(d, i),
words = split(t, i);
const dx = tA === "start" ? 0 : tA === "end" ? w : w / 2;
const style = {
"font-family": fontFamily(d, i),
"font-size": fS,
"line-height": lH
};
/**
Figures out the lineData to be used for wrapping.
@private
*/
function checkSize() {
line = 1;
lineData = [""];
if (fS < fMin) {
lineData = [];
return;
}
else if (fS > fMax) fS = fMax;
let textProg = "",
widthProg = 0;
if (resize) {
lH = fS * 1.1;
style["font-size"] = fS;
style["line-height"] = lH;
}
sizes = measure(words, style);
for (let word of words) {
const wordWidth = sizes[words.indexOf(word)];
if (wordWidth > w) break;
const nextChar = t.charAt(textProg.length + word.length);
if (nextChar === " ") word += nextChar;
if (widthProg + wordWidth > w - fS) {
line++;
if (lH * line > h) {
if (resize) {
fS--;
if (fS < fMin) {
lineData = [];
break;
}
checkSize();
}
else lineData[line - 2] = ellipsis(lineData[line - 2].trimRight());
break;
}
widthProg = 0;
lineData.push(word);
}
else lineData[line - 1] += word;
textProg += word;
widthProg += wordWidth;
if (nextChar === " ") widthProg += space;
}
}
return false;
}
else if (tspan.node().getComputedTextLength() > w) {
tspan.html(curr.trimRight());
if (l === 1 && curr === "") return false;
tspan = select.append("tspan").call(tspanStyle);
l++;
return addWord(word);
}
else {
const char = join === " " ? " " : "";
p = temp + char;
tspan.html(curr + word + char);
return true;
}
}
for (const word of split(t)) {
const r = addWord(word);
if (!r) break;
}
if (h > lH || resize) {
if (resize) {
sizes = measure(words, style);
const areaMod = 1.165 + w / h * 0.1,
boxArea = w * h,
maxWidth = d3.max(sizes),
textArea = d3.sum(sizes, (d) => d * lH) * areaMod;
if (maxWidth > w || textArea > boxArea) {
const areaRatio = Math.sqrt(boxArea / textArea),
widthRatio = w / maxWidth;
const sizeRatio = d3.min([areaRatio, widthRatio]);
fS = Math.floor(fS * sizeRatio);
}
const heightMax = Math.floor(h * 0.8);
if (fS > heightMax) fS = heightMax;
}
checkSize();
d3.select(this)
.attr("font-size", `${fS}px`)
.style("font-size", `${fS}px`);
}
const tH = line * lH;
let y = vA === "top" ? 0 : vA === "middle" ? h / 2 - tH / 2 : h - tH;
y -= lH * 0.2;
d3.select(this).transition().duration(duration)
.attr("transform", `translate(0,${y})`);
/**
Styles to apply to each <tspan> element.
@private
*/
function tspanStyle(tspan) {
tspan
.text((d) => d.trimRight())
.attr("x", `${x(d, i)}px`)
.attr("dx", `${dx}px`)
.attr("dy", `${lH}px`);
}
const tspans = d3.select(this).selectAll("tspan").data(lineData);
tspans.exit().transition().duration(duration)
.attr("opacity", 0).remove();
tspans.transition().duration(duration).call(tspanStyle);
tspans.enter().append("tspan")
.attr("dominant-baseline", "alphabetic")
.style("baseline-shift", "0%")
.attr("opacity", 0)
.call(tspanStyle)
.transition().duration(duration).delay(delay)
.attr("opacity", 1);
});
return box;
}
/**
@memberof box
@desc If *data* is specified, sets the data array to the specified array and returns this box generator. If *data* is not specified, returns the current data array. A text box will be drawn for each object in the array.
@param {Array} [*data* = []]
*/
box.data = function(_) {
return arguments.length ? (data = _, box) : data;
};
/**
@memberof box
@desc If *value* is specified, sets the animation delay to the specified number and returns this box generator. If *value* is not specified, returns the current animation delay.
@param {Number} [*value* = 0]
*/
box.delay = function(_) {
return arguments.length ? (delay = _, box) : delay;
};
/**
@memberof box
@desc If *value* is specified, sets the animation duration to the specified number and returns this box generator. If *value* is not specified, returns the current animation duration.
@param {Number} [*value* = 0]
*/
box.duration = function(_) {
return arguments.length ? (duration = _, box) : duration;
};
/**
@memberof box
@desc If *value* is specified, sets the ellipsis method to the specified function or string and returns this box generator. If *value* is not specified, returns the current ellipsis method, which simply adds an ellipsis to the string by default.
@param {Function|String} [*value*]
@example
function(d) {
return d + "...";
}
*/
box.ellipsis = function(_) {

@@ -114,2 +329,7 @@ return arguments.length ? (ellipsis = typeof _ === "function" ? _ : constant(_), box) : ellipsis;

/**
@memberof box
@desc If *value* is specified, sets the font color accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current font color accessor, which is inferred from the [container element](#box.select) by default.
@param {Function|String} [*value*]
*/
box.fontColor = function(_) {

@@ -119,2 +339,7 @@ return arguments.length ? (fontColor = typeof _ === "function" ? _ : constant(_), box) : fontColor;

/**
@memberof box
@desc If *value* is specified, sets the font family accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current font family accessor, which is inferred from the [container element](#box.select) by default.
@param {Function|String} [*value*]
*/
box.fontFamily = function(_) {

@@ -124,6 +349,52 @@ return arguments.length ? (fontFamily = typeof _ === "function" ? _ : constant(_), box) : fontFamily;

/**
@memberof box
@desc If *value* is specified, sets the maximum font size accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current maximum font size accessor. The maximum font size is used when [resizing fonts](#box.fontResize) dynamically.
@param {Function|Number} [*value* = 50]
*/
box.fontMax = function(_) {
return arguments.length ? (fontMax = typeof _ === "function" ? _ : constant(_), box) : fontMax;
};
/**
@memberof box
@desc If *value* is specified, sets the minimum font size accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current minimum font size accessor. The minimum font size is used when [resizing fonts](#box.fontResize) dynamically.
@param {Function|Number} [*value* = 8]
*/
box.fontMin = function(_) {
return arguments.length ? (fontMin = typeof _ === "function" ? _ : constant(_), box) : fontMin;
};
/**
@memberof box
@desc If *value* is specified, sets the font resizing accessor to the specified function or boolean and returns this box generator. If *value* is not specified, returns the current font resizing accessor.
@param {Function|Boolean} [*value* = false]
*/
box.fontResize = function(_) {
return arguments.length ? (fontResize = typeof _ === "function" ? _ : constant(_), box) : fontResize;
};
/**
@memberof box
@desc If *value* is specified, sets the font size accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current font size accessor, which is inferred from the [container element](#box.select) by default.
@param {Function|Number} [*value*]
*/
box.fontSize = function(_) {
return arguments.length ? (fontSize = typeof _ === "function" ? _ : constant(_), box) : fontSize;
if (arguments.length) {
fontSize = typeof _ === "function" ? _ : constant(_);
if (lineHeight === void 0) lineHeight = constant(Math.ceil(fontSize() * 1.1));
return box;
}
return fontSize;
};
/**
@memberof box
@desc If *value* is specified, sets the height accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current height accessor.
@param {Function|Number} [*value*]
@example
function(d) {
return d.height || 200;
}
*/
box.height = function(_) {

@@ -133,14 +404,35 @@ return arguments.length ? (height = typeof _ === "function" ? _ : constant(_), box) : height;

/**
@memberof box
@desc If *value* is specified, sets the id accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current id accessor.
@param {Function|Number} [*value*]
@example
function(d, i) {
return d.id || i + "";
}
*/
box.id = function(_) {
return arguments.length ? (id = typeof _ === "function" ? _ : constant(_), box) : id;
};
/**
@memberof box
@desc If *value* is specified, sets the line height accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current line height accessor, which is 1.1 times the [font size](#box.fontSize) by default.
@param {Function|Number} [*value*]
*/
box.lineHeight = function(_) {
return arguments.length ? (lineHeight = typeof _ === "function" ? _ : constant(_), box) : lineHeight;
};
/**
@memberof box
@desc If *selector* is specified, sets the SVG container element to the specified d3 selector or DOM element and returns this box generator. If *selector* is not specified, returns the current SVG container element, which adds an SVG element to the page by default.
@param {String|HTMLElement} [*selector*]
*/
box.select = function(_) {
if (arguments.length) {
select = d3(_);
if (text === void 0) {
text = constant(select.text());
if (fontColor === void 0) fontColor = constant(select.style("font-color"));
if (fontFamily === void 0) fontFamily = constant(select.style("font-family"));
if (fontSize === void 0) {
fontSize = constant(parseFloat(select.style("font-size"), 10));
lineHeight = constant(Math.ceil(fontSize() * 1.1));
}
}
select = d3.select(_);
if (fontColor === void 0) box.fontColor(select.style("font-color"));
if (fontFamily === void 0) box.fontFamily(select.style("font-family"));
if (fontSize === void 0) box.fontSize(parseFloat(select.style("font-size"), 10));
return box;

@@ -151,2 +443,7 @@ }

/**
@memberof box
@desc If *value* is specified, sets the word split function to the specified function and returns this box generator. If *value* is not specified, returns the current word split function.
@param {Function} [*value*] A function that, when passed a string, is expected to return that string split into an array of words to wrap. The default split function splits strings on the following characters: `-`, `/`, `;`, `:`, `&`
*/
box.split = function(_) {

@@ -156,2 +453,11 @@ return arguments.length ? (split = _, box) : split;

/**
@memberof box
@desc If *value* is specified, sets the text accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current text accessor.
@param {Function|String} [*value*]
@example
function(d) {
return d.text;
}
*/
box.text = function(_) {

@@ -161,2 +467,29 @@ return arguments.length ? (text = typeof _ === "function" ? _ : constant(_), box) : text;

/**
@memberof box
@desc If *value* is specified, sets the horizontal text anchor accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current horizontal text anchor accessor.
@param {Function|String} [*value* = "start"] Analagous to the SVG [text-anchor](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/text-anchor) property.
*/
box.textAnchor = function(_) {
return arguments.length ? (textAnchor = typeof _ === "function" ? _ : constant(_), box) : textAnchor;
};
/**
@memberof box
@desc If *value* is specified, sets the vertical alignment accessor to the specified function or string and returns this box generator. If *value* is not specified, returns the current vertical alignment accessor.
@param {Function|String} [*value* = "top"] Accepts `"top"`, `"middle"`, and `"bottom"`.
*/
box.verticalAlign = function(_) {
return arguments.length ? (verticalAlign = typeof _ === "function" ? _ : constant(_), box) : verticalAlign;
};
/**
@memberof box
@desc If *value* is specified, sets the width accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current width accessor.
@param {Function|Number} [*value*]
@example
function(d) {
return d.width || 200;
}
*/
box.width = function(_) {

@@ -166,2 +499,11 @@ return arguments.length ? (width = typeof _ === "function" ? _ : constant(_), box) : width;

/**
@memberof box
@desc If *value* is specified, sets the x accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current x accessor. The number returned should correspond to the left position of the box.
@param {Function|Number} [*value*]
@example
function(d) {
return d.x || 0;
}
*/
box.x = function(_) {

@@ -171,2 +513,11 @@ return arguments.length ? (x = typeof _ === "function" ? _ : constant(_), box) : x;

/**
@memberof box
@desc If *value* is specified, sets the y accessor to the specified function or number and returns this box generator. If *value* is not specified, returns the current y accessor. The number returned should correspond to the top position of the box.
@param {Function|Number} [*value*]
@example
function(d) {
return d.y || 0;
}
*/
box.y = function(_) {

@@ -176,4 +527,4 @@ return arguments.length ? (y = typeof _ === "function" ? _ : constant(_), box) : y;

return box;
return data.length ? box() : box;
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc