@d3plus/text 3.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ /**
2
+ @const fontFamily
3
+ @desc The default fallback font list used for all text labels as an Array of Strings.
4
+ @returns {Array<string>}
5
+ */ export var fontFamily = [
6
+ "Inter",
7
+ "Helvetica Neue",
8
+ "HelveticaNeue",
9
+ "Helvetica",
10
+ "Arial",
11
+ "sans-serif"
12
+ ];
13
+ /**
14
+ @const fontFamilyStringify
15
+ @desc Converts an Array of font-family names into a CSS font-family string.
16
+ @param {String|Array<string>} *family*
17
+ @returns {String}
18
+ */ export var fontFamilyStringify = function(family) {
19
+ return (typeof family === "string" ? [
20
+ family
21
+ ] : family).map(function(d) {
22
+ return d.match(/^[a-z-_]{1,}$/) ? d : "'".concat(d, "'");
23
+ }).join(", ");
24
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ @function stringify
3
+ @desc Coerces value into a String.
4
+ @param {String} value
5
+ */ function _instanceof(left, right) {
6
+ if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
7
+ return !!right[Symbol.hasInstance](left);
8
+ } else {
9
+ return left instanceof right;
10
+ }
11
+ }
12
+ export default function(value) {
13
+ if (value === void 0) value = "undefined";
14
+ else if (!(typeof value === "string" || _instanceof(value, String))) value = JSON.stringify(value);
15
+ return value;
16
+ }
@@ -0,0 +1,110 @@
1
+ // great unicode list: http://asecuritysite.com/coding/asc2
2
+ var diacritics = [
3
+ [
4
+ /[\300-\305]/g,
5
+ "A"
6
+ ],
7
+ [
8
+ /[\340-\345]/g,
9
+ "a"
10
+ ],
11
+ [
12
+ /[\306]/g,
13
+ "AE"
14
+ ],
15
+ [
16
+ /[\346]/g,
17
+ "ae"
18
+ ],
19
+ [
20
+ /[\337]/g,
21
+ "B"
22
+ ],
23
+ [
24
+ /[\307]/g,
25
+ "C"
26
+ ],
27
+ [
28
+ /[\347]/g,
29
+ "c"
30
+ ],
31
+ [
32
+ /[\320\336\376]/g,
33
+ "D"
34
+ ],
35
+ [
36
+ /[\360]/g,
37
+ "d"
38
+ ],
39
+ [
40
+ /[\310-\313]/g,
41
+ "E"
42
+ ],
43
+ [
44
+ /[\350-\353]/g,
45
+ "e"
46
+ ],
47
+ [
48
+ /[\314-\317]/g,
49
+ "I"
50
+ ],
51
+ [
52
+ /[\354-\357]/g,
53
+ "i"
54
+ ],
55
+ [
56
+ /[\321]/g,
57
+ "N"
58
+ ],
59
+ [
60
+ /[\361]/g,
61
+ "n"
62
+ ],
63
+ [
64
+ /[\u014c\322-\326\330]/g,
65
+ "O"
66
+ ],
67
+ [
68
+ /[\u014d\362-\366\370]/g,
69
+ "o"
70
+ ],
71
+ [
72
+ /[\u016a\331-\334]/g,
73
+ "U"
74
+ ],
75
+ [
76
+ /[\u016b\371-\374]/g,
77
+ "u"
78
+ ],
79
+ [
80
+ /[\327]/g,
81
+ "x"
82
+ ],
83
+ [
84
+ /[\335]/g,
85
+ "Y"
86
+ ],
87
+ [
88
+ /[\375\377]/g,
89
+ "y"
90
+ ]
91
+ ];
92
+ /**
93
+ @function strip
94
+ @desc Removes all non ASCII characters from a string.
95
+ @param {String} value
96
+ @param {String} [spacer = "-"]
97
+ */ export default function(value) {
98
+ var spacer = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "-";
99
+ return "".concat(value).replace(/[^A-Za-z0-9\-_\u0621-\u064A]/g, function(char) {
100
+ if (char === " ") return spacer;
101
+ var ret = false;
102
+ for(var d = 0; d < diacritics.length; d++){
103
+ if (new RegExp(diacritics[d][0]).test(char)) {
104
+ ret = diacritics[d][1];
105
+ break;
106
+ }
107
+ }
108
+ return ret || "";
109
+ });
110
+ }
@@ -0,0 +1,76 @@
1
+ import { default as stringify } from "./stringify.js";
2
+ import { default as combiningMarks } from "./combiningMarks.js";
3
+ import { merge } from "d3-array";
4
+ var splitChars = [
5
+ "-",
6
+ ";",
7
+ ":",
8
+ "&",
9
+ "|",
10
+ "u0E2F",
11
+ "u0EAF",
12
+ "u0EC6",
13
+ "u0ECC",
14
+ "u104A",
15
+ "u104B",
16
+ "u104C",
17
+ "u104D",
18
+ "u104E",
19
+ "u104F",
20
+ "u2013",
21
+ "u2014",
22
+ "u2027",
23
+ "u3000",
24
+ "u3001",
25
+ "u3002",
26
+ "uFF0C",
27
+ "uFF5E" // wave dash
28
+ ];
29
+ var prefixChars = [
30
+ "'",
31
+ "<",
32
+ "(",
33
+ "{",
34
+ "[",
35
+ "u00AB",
36
+ "u300A",
37
+ "u3008" // left angle bracket
38
+ ];
39
+ var suffixChars = [
40
+ "'",
41
+ ">",
42
+ ")",
43
+ "}",
44
+ "]",
45
+ ".",
46
+ "!",
47
+ "?",
48
+ "/",
49
+ "u00BB",
50
+ "u300B",
51
+ "u3009" // right angle bracket
52
+ ].concat(splitChars);
53
+ var burmeseRange = "\u1000-\u102A\u103F-\u1049\u1050-\u1055";
54
+ var japaneseRange = "\u3040-\u309f\u30a0-\u30ff\uff00-\uff0b\uff0d-\uff5d\uff5f-\uff9f\u3400-\u4dbf";
55
+ var chineseRange = "\u3400-\u9FBF";
56
+ var laoRange = "\u0E81-\u0EAE\u0EB0-\u0EC4\u0EC8-\u0ECB\u0ECD-\u0EDD";
57
+ var noSpaceRange = burmeseRange + chineseRange + japaneseRange + laoRange;
58
+ var splitWords = new RegExp("(\\".concat(splitChars.join("|\\"), ")*[^\\s|\\").concat(splitChars.join("|\\"), "]*(\\").concat(splitChars.join("|\\"), ")*"), "g");
59
+ var noSpaceLanguage = new RegExp("[".concat(noSpaceRange, "]"));
60
+ var splitAllChars = new RegExp("(\\".concat(prefixChars.join("|\\"), ")*[").concat(noSpaceRange, "](\\").concat(suffixChars.join("|\\"), "|\\").concat(combiningMarks.join("|\\"), ")*|[a-z0-9]+"), "gi");
61
+ /**
62
+ @function textSplit
63
+ @desc Splits a given sentence into an array of words.
64
+ @param {String} sentence
65
+ */ export default function(sentence) {
66
+ if (!noSpaceLanguage.test(sentence)) return stringify(sentence).match(splitWords).filter(function(w) {
67
+ return w.length;
68
+ });
69
+ return merge(stringify(sentence).match(splitWords).map(function(d) {
70
+ if (noSpaceLanguage.test(d)) return d.match(splitAllChars);
71
+ return [
72
+ d
73
+ ];
74
+ }));
75
+ }
76
+ export { prefixChars, splitChars, splitWords, suffixChars };
@@ -0,0 +1,120 @@
1
+ import { textWidth } from "@d3plus/dom";
2
+ import defaultSplit from "./textSplit.js";
3
+ import stringify from "./stringify.js";
4
+ import { trimRight } from "./trim.js";
5
+ /**
6
+ @function textWrap
7
+ @desc Based on the defined styles and dimensions, breaks a string into an array of strings for each line of text.
8
+ */ export default function() {
9
+ var fontFamily = "sans-serif", fontSize = 10, fontWeight = 400, height = 200, lineHeight, maxLines = null, overflow = false, split = defaultSplit, width = 200;
10
+ /**
11
+ The inner return object and wraps the text and returns the line data array.
12
+ @private
13
+ */ function textWrap(sentence) {
14
+ sentence = stringify(sentence);
15
+ if (lineHeight === void 0) lineHeight = Math.ceil(fontSize * 1.4);
16
+ var words = split(sentence);
17
+ var style = {
18
+ "font-family": fontFamily,
19
+ "font-size": fontSize,
20
+ "font-weight": fontWeight,
21
+ "line-height": lineHeight
22
+ };
23
+ var line = 1, textProg = "", truncated = false, widthProg = 0;
24
+ var lineData = [], sizes = textWidth(words, style), space = textWidth(" ", style);
25
+ for(var i = 0; i < words.length; i++){
26
+ var word = words[i];
27
+ var wordWidth = sizes[words.indexOf(word)];
28
+ word += sentence.slice(textProg.length + word.length).match("^( |\n)*", "g")[0];
29
+ if (textProg.slice(-1) === "\n" || widthProg + wordWidth > width) {
30
+ if (!i && !overflow) {
31
+ truncated = true;
32
+ break;
33
+ }
34
+ if (lineData.length >= line) lineData[line - 1] = trimRight(lineData[line - 1]);
35
+ line++;
36
+ if (lineHeight * line > height || wordWidth > width && !overflow || maxLines && line > maxLines) {
37
+ truncated = true;
38
+ break;
39
+ }
40
+ widthProg = 0;
41
+ lineData.push(word);
42
+ } else if (!i) lineData[0] = word;
43
+ else lineData[line - 1] += word;
44
+ textProg += word;
45
+ widthProg += wordWidth;
46
+ widthProg += word.match(/[\s]*$/g)[0].length * space;
47
+ }
48
+ return {
49
+ lines: lineData,
50
+ sentence: sentence,
51
+ truncated: truncated,
52
+ widths: textWidth(lineData, style),
53
+ words: words
54
+ };
55
+ }
56
+ /**
57
+ @memberof textWrap
58
+ @desc If *value* is specified, sets the font family accessor to the specified function or string and returns this generator. If *value* is not specified, returns the current font family.
59
+ @param {Function|String} [*value* = "sans-serif"]
60
+ */ textWrap.fontFamily = function(_) {
61
+ return arguments.length ? (fontFamily = _, textWrap) : fontFamily;
62
+ };
63
+ /**
64
+ @memberof textWrap
65
+ @desc If *value* is specified, sets the font size accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current font size.
66
+ @param {Function|Number} [*value* = 10]
67
+ */ textWrap.fontSize = function(_) {
68
+ return arguments.length ? (fontSize = _, textWrap) : fontSize;
69
+ };
70
+ /**
71
+ @memberof textWrap
72
+ @desc If *value* is specified, sets the font weight accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current font weight.
73
+ @param {Function|Number|String} [*value* = 400]
74
+ */ textWrap.fontWeight = function(_) {
75
+ return arguments.length ? (fontWeight = _, textWrap) : fontWeight;
76
+ };
77
+ /**
78
+ @memberof textWrap
79
+ @desc If *value* is specified, sets height limit to the specified value and returns this generator. If *value* is not specified, returns the current value.
80
+ @param {Number} [*value* = 200]
81
+ */ textWrap.height = function(_) {
82
+ return arguments.length ? (height = _, textWrap) : height;
83
+ };
84
+ /**
85
+ @memberof textWrap
86
+ @desc If *value* is specified, sets the line height accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current line height accessor, which is 1.1 times the [font size](#textWrap.fontSize) by default.
87
+ @param {Function|Number} [*value*]
88
+ */ textWrap.lineHeight = function(_) {
89
+ return arguments.length ? (lineHeight = _, textWrap) : lineHeight;
90
+ };
91
+ /**
92
+ @memberof textWrap
93
+ @desc If *value* is specified, sets the maximum number of lines allowed when wrapping.
94
+ @param {Function|Number} [*value*]
95
+ */ textWrap.maxLines = function(_) {
96
+ return arguments.length ? (maxLines = _, textWrap) : maxLines;
97
+ };
98
+ /**
99
+ @memberof textWrap
100
+ @desc If *value* is specified, sets the overflow to the specified boolean and returns this generator. If *value* is not specified, returns the current overflow value.
101
+ @param {Boolean} [*value* = false]
102
+ */ textWrap.overflow = function(_) {
103
+ return arguments.length ? (overflow = _, textWrap) : overflow;
104
+ };
105
+ /**
106
+ @memberof textWrap
107
+ @desc If *value* is specified, sets the word split function to the specified function and returns this generator. If *value* is not specified, returns the current word split function.
108
+ @param {Function} [*value*] A function that, when passed a string, is expected to return that string split into an array of words to textWrap. The default split function splits strings on the following characters: `-`, `/`, `;`, `:`, `&`
109
+ */ textWrap.split = function(_) {
110
+ return arguments.length ? (split = _, textWrap) : split;
111
+ };
112
+ /**
113
+ @memberof textWrap
114
+ @desc If *value* is specified, sets width limit to the specified value and returns this generator. If *value* is not specified, returns the current value.
115
+ @param {Number} [*value* = 200]
116
+ */ textWrap.width = function(_) {
117
+ return arguments.length ? (width = _, textWrap) : width;
118
+ };
119
+ return textWrap;
120
+ }
@@ -0,0 +1,75 @@
1
+ import { suffixChars, default as textSplit } from "./textSplit.js";
2
+ var lowercase = [
3
+ "a",
4
+ "an",
5
+ "and",
6
+ "as",
7
+ "at",
8
+ "but",
9
+ "by",
10
+ "for",
11
+ "from",
12
+ "if",
13
+ "in",
14
+ "into",
15
+ "near",
16
+ "nor",
17
+ "of",
18
+ "on",
19
+ "onto",
20
+ "or",
21
+ "per",
22
+ "that",
23
+ "the",
24
+ "to",
25
+ "with",
26
+ "via",
27
+ "vs",
28
+ "vs."
29
+ ];
30
+ var uppercase = [
31
+ "CEO",
32
+ "CFO",
33
+ "CNC",
34
+ "COO",
35
+ "CPU",
36
+ "GDP",
37
+ "HVAC",
38
+ "ID",
39
+ "IT",
40
+ "R&D",
41
+ "TV",
42
+ "UI"
43
+ ];
44
+ /**
45
+ @function titleCase
46
+ @desc Capitalizes the first letter of each word in a phrase/sentence.
47
+ @param {String} str The string to apply the title case logic.
48
+ */ export default function(str) {
49
+ if (str === void 0) return "";
50
+ var smalls = lowercase.map(function(s) {
51
+ return s.toLowerCase();
52
+ });
53
+ var bigs = uppercase.slice();
54
+ bigs = bigs.concat(bigs.map(function(b) {
55
+ return "".concat(b, "s");
56
+ }));
57
+ var biglow = bigs.map(function(b) {
58
+ return b.toLowerCase();
59
+ });
60
+ var split = textSplit(str);
61
+ return split.map(function(s, i) {
62
+ if (s) {
63
+ var lower = s.toLowerCase();
64
+ var stripped = suffixChars.includes(lower.charAt(lower.length - 1)) ? lower.slice(0, -1) : lower;
65
+ var bigindex = biglow.indexOf(stripped);
66
+ if (bigindex >= 0) return bigs[bigindex];
67
+ else if (smalls.includes(stripped) && i !== 0 && i !== split.length - 1) return lower;
68
+ else return s.charAt(0).toUpperCase() + s.substr(1).toLowerCase();
69
+ } else return "";
70
+ }).reduce(function(ret, s, i) {
71
+ if (i && str.charAt(ret.length) === " ") ret += " ";
72
+ ret += s;
73
+ return ret;
74
+ }, "");
75
+ }
package/es/src/trim.js ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ @function trim
3
+ @desc Cross-browser implementation of [trim](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim).
4
+ @param {String} str
5
+ */ function trim(str) {
6
+ return str.toString().replace(/^\s+|\s+$/g, "");
7
+ }
8
+ /**
9
+ @function trimLeft
10
+ @desc Cross-browser implementation of [trimLeft](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/TrimLeft).
11
+ @param {String} str
12
+ */ function trimLeft(str) {
13
+ return str.toString().replace(/^\s+/, "");
14
+ }
15
+ /**
16
+ @function trimRight
17
+ @desc Cross-browser implementation of [trimRight](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/TrimRight).
18
+ @param {String} str
19
+ */ function trimRight(str) {
20
+ return str.toString().replace(/\s+$/, "");
21
+ }
22
+ export { trim, trimLeft, trimRight };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@d3plus/text",
3
+ "version": "3.0.0-alpha.0",
4
+ "description": "A smart SVG text box with line wrapping and automatic font size scaling.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "exports": "./es/index.js",
8
+ "browser": "./umd/d3plus-text.full.js",
9
+ "engines": {
10
+ "node": ">=18"
11
+ },
12
+ "sideEffects": false,
13
+ "files": [
14
+ "umd",
15
+ "es"
16
+ ],
17
+ "homepage": "https://d3plus.org",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/d3plus/d3plus.git",
21
+ "directory": "packages/text"
22
+ },
23
+ "keywords": [
24
+ "text",
25
+ "d3",
26
+ "d3plus",
27
+ "data",
28
+ "visualization"
29
+ ],
30
+ "scripts": {
31
+ "build:esm": "node ../../scripts/build-esm.js",
32
+ "build:umd": "node ../../scripts/build-umd.js",
33
+ "dev": "node ../../scripts/dev.js",
34
+ "test": "eslint index.js src/**/*.js && eslint --global=it test && mocha 'test/**/*-test.js'"
35
+ },
36
+ "dependencies": {
37
+ "@d3plus/dom": "3.0.0-alpha.0",
38
+ "d3-array": "^3.2.4",
39
+ "d3-selection": "^3.0.0",
40
+ "d3-transition": "^3.0.1"
41
+ }
42
+ }