From 53cd362fa290978b9d478838d6f86f5e2a791210 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mateusz=20Podle=C5=9Bny?=
<17722512+mfazer@users.noreply.github.com>
Date: Sun, 30 May 2021 23:53:01 +0200
Subject: [PATCH 1/3] Initial commit - first try of the word wrapping
---
src/components/annotations/draw.js | 2 +-
src/components/legend/draw.js | 2 +-
src/components/titles/index.js | 20 +++++++--
src/lib/svg_text_utils.js | 68 ++++++++++++++++++++++++------
src/plots/cartesian/axes.js | 9 +++-
src/traces/table/plot.js | 2 +-
6 files changed, 84 insertions(+), 19 deletions(-)
diff --git a/src/components/annotations/draw.js b/src/components/annotations/draw.js
index bf1ebf7fbec..deb849cc47f 100644
--- a/src/components/annotations/draw.js
+++ b/src/components/annotations/draw.js
@@ -248,7 +248,7 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
}[options.align] || 'middle'
});
- svgTextUtils.convertToTspans(s, gd, drawGraphicalElements);
+ svgTextUtils.convertToTspans(s, gd, null, drawGraphicalElements);
return s;
}
diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js
index 6b6e783a68b..7e97c29f146 100644
--- a/src/components/legend/draw.js
+++ b/src/components/legend/draw.js
@@ -496,7 +496,7 @@ function setupTraceToggle(g, gd) {
function textLayout(s, g, gd, legendObj, aTitle) {
if(legendObj._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover
- svgTextUtils.convertToTspans(s, gd, function() {
+ svgTextUtils.convertToTspans(s, gd, null, function() {
computeTextDimensions(g, gd, legendObj, aTitle);
});
}
diff --git a/src/components/titles/index.js b/src/components/titles/index.js
index 3a260108d67..21408ca8255 100644
--- a/src/components/titles/index.js
+++ b/src/components/titles/index.js
@@ -56,6 +56,8 @@ function draw(gd, titleClass, options) {
var attributes = options.attributes;
var transform = options.transform;
var group = options.containerGroup;
+ var isAxis = options.isAxis; // Prepare documentation for this
+ var wrap = options.wrap;
var fullLayout = gd._fullLayout;
@@ -120,6 +122,7 @@ function draw(gd, titleClass, options) {
function drawTitle(titleEl) {
var transformVal;
+ var convertOptions = null;
if(transform) {
transformVal = '';
@@ -133,6 +136,17 @@ function draw(gd, titleClass, options) {
transformVal = null;
}
+ if(isAxis && wrap) {
+ var axName = options.propContainer._name;
+ var axOut = gd._fullLayout[axName];
+
+ convertOptions = {
+ wrap: wrap,
+ axisLength: axOut._length,
+ axisOrientation: axOut._id.substr(0, 1) === 'y' ? 'v' : 'h'
+ };
+ }
+
titleEl.attr('transform', transformVal);
titleEl.style({
@@ -143,13 +157,13 @@ function draw(gd, titleClass, options) {
'font-weight': Plots.fontWeight
})
.attr(attributes)
- .call(svgTextUtils.convertToTspans, gd);
+ .call(svgTextUtils.convertToTspans, gd, convertOptions);
return Plots.previousPromises(gd);
}
- function scootTitle(titleElIn) {
- var titleGroup = d3.select(titleElIn.node().parentNode);
+ function scootTitle(titleEl) {
+ var titleGroup = d3.select(titleEl.node().parentNode);
if(avoid && avoid.selection && avoid.side && txt) {
titleGroup.attr('transform', null);
diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js
index 55a4123cb70..a9bcdd77a16 100644
--- a/src/lib/svg_text_utils.js
+++ b/src/lib/svg_text_utils.js
@@ -17,7 +17,18 @@ function getSize(_selection, _dimension) {
var FIND_TEX = /([^$]*)([$]+[^$]*[$]+)([^$]*)/;
-exports.convertToTspans = function(_context, gd, _callback) {
+/**
+ * Converts to SVG element.
+ * @param {*} _context Context
+ * @param {*} gd Graph DIV
+ * @param {*} options All props are needed to wrap.
+ * [axLength]?: number
+ * [orientation]?: 'v' | 'h'
+ * [wrap]?: boolean
+ * @param {Function} _callback Callback function.
+ * @returns Modified `_context`.
+ */
+exports.convertToTspans = function(_context, gd, options, _callback) {
var str = _context.text();
// Until we get tex integrated more fully (so it can be used along with non-tex)
@@ -50,7 +61,7 @@ exports.convertToTspans = function(_context, gd, _callback) {
_context.text('')
.style('white-space', 'pre');
- var hasLink = buildSVGText(_context.node(), str);
+ var hasLink = buildSVGText(_context.node(), str, options);
if(hasLink) {
// at least in Chrome, pointer-events does not seem
@@ -428,17 +439,16 @@ function fromCodePoint(code) {
);
}
-/*
- * buildSVGText: convert our pseudo-html into SVG tspan elements, and attach these
- * to containerNode
+/**
+ * Converts SVG `` elements from pseudo-html into, and attach these to `containerNode`.
*
* @param {svg text element} containerNode: the node to insert this text into
* @param {string} str: the pseudo-html string to convert to svg
- *
+ * @param {{ axLength: number, axOrientation: 'v' | 'h', wrap?: boolean }} options
* @returns {bool}: does the result contain any links? We need to handle the text element
* somewhat differently if it does, so just keep track of this when it happens.
*/
-function buildSVGText(containerNode, str) {
+function buildSVGText(containerNode, str, options) {
/*
* Normalize behavior between IE and others wrt newlines and whitespace:pre
* this combination makes IE barf https://github.com/plotly/plotly.js/issues/746
@@ -446,6 +456,7 @@ function buildSVGText(containerNode, str) {
* I feel like at some point we turned these into
but currently we don't so
* I'm just going to cement what we do now in Chrome and FF
*/
+ str = options && options.axOrientation === 'v' ? 'One very long string that is soo long, that
I dont get it!' : str;
str = str.replace(NEWLINES, ' ');
var hasLink = false;
@@ -530,7 +541,11 @@ function buildSVGText(containerNode, str) {
}
function addTextNode(node, text) {
- node.appendChild(document.createTextNode(text));
+ return node.appendChild(document.createTextNode(text));
+ }
+
+ function removeTextNode(node, child) {
+ node.removeChild(child);
}
function exitNode(type) {
@@ -550,16 +565,19 @@ function buildSVGText(containerNode, str) {
currentNode = nodeStack[nodeStack.length - 1].node;
}
- var hasLines = BR_TAG.test(str);
+ var hasBrLines = BR_TAG.test(str);
- if(hasLines) newLine();
+ if(hasBrLines) newLine();
else {
currentNode = containerNode;
nodeStack = [{node: containerNode}];
}
var parts = str.split(SPLIT_TAGS);
- for(var i = 0; i < parts.length; i++) {
+ // eslint-disable-next-line no-console
+ // options && options.axOrientation === 'v' && console.log(parts);
+ var i = 0;
+ for(i; i < parts.length; i++) {
var parti = parts[i];
var match = parti.match(ONE_TAG);
var tagType = match && match[2].toLowerCase();
@@ -568,7 +586,33 @@ function buildSVGText(containerNode, str) {
if(tagType === 'br') {
newLine();
} else if(tagStyle === undefined) {
- addTextNode(currentNode, convertEntities(parti));
+ // addTextNode(currentNode, convertEntities(parti));
+
+ if(options && options.axOrientation === 'v') {
+ if(options.wrap) {
+ var wordId = 0;
+ var wordsArray = parti.split(' ');
+ for(wordId; wordId < wordsArray.length; wordId++) {
+ var word = wordsArray[wordId];
+ var preSpace = wordId === 0 ? '' : ' ';
+ var child = addTextNode(currentNode, convertEntities(preSpace + word));
+ if(currentNode.getBBox().width > options.axLength) {
+ removeTextNode(currentNode, child);
+ newLine();
+ addTextNode(currentNode, convertEntities(word));
+ }
+ // eslint-disable-next-line no-console
+ // console.log(currentNode.getBBox().width);
+ }
+
+ // eslint-disable-next-line no-console
+ // options && options.axOrientation === 'v' && console.log(currentNode.getBoundingClientRect().height);
+ // eslint-disable-next-line no-console
+ // options && options.axOrientation === 'v' && console.log(currentNode.getBBox().width);
+ } else {
+ addTextNode(currentNode, convertEntities(parti));
+ }
+ }
} else {
// tag - open or close
if(match[1]) {
diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 44ebc870209..842121904ea 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -3464,6 +3464,8 @@ function drawTitle(gd, ax) {
var axId = ax._id;
var axLetter = axId.charAt(0);
var fontSize = ax.title.font.size;
+ // var wrap = ax.title.wrap; // TODO: Update documentation and the TypeScript types.
+
var titleStandoff;
if(ax.title.hasOwnProperty('standoff')) {
@@ -3536,7 +3538,12 @@ function drawTitle(gd, ax) {
placeholder: fullLayout._dfltTitle[axLetter],
avoid: avoid,
transform: transform,
- attributes: {x: x, y: y, 'text-anchor': 'middle'}
+ attributes: {x: x, y: y, 'text-anchor': 'middle'},
+ isAxis: true,
+ // wrap: wrap
+ // HEY! This is for testing only!
+ wrap: 'breakword',
+ // TODO: options: 'breakword' | 'breakchar' | undefined (default)
});
}
diff --git a/src/traces/table/plot.js b/src/traces/table/plot.js
index 4ef177226f3..10fc0f46497 100644
--- a/src/traces/table/plot.js
+++ b/src/traces/table/plot.js
@@ -561,7 +561,7 @@ function populateCellText(cellText, tableControlView, allColumnBlock, gd) {
var renderCallback = d.wrappingNeeded ? wrapTextMaker : updateYPositionMaker;
if(d.needsConvertToTspans) {
- svgUtil.convertToTspans(selection, gd, renderCallback(allColumnBlock, element, tableControlView, gd, d));
+ svgUtil.convertToTspans(selection, gd, null, renderCallback(allColumnBlock, element, tableControlView, gd, d));
} else {
d3.select(element.parentNode)
// basic cell adjustment - compliance with `cellPad`
From 66f50aa33ca5cffa775cc93ff99a9bf9063920da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mateusz=20Podle=C5=9Bny?=
<17722512+mfazer@users.noreply.github.com>
Date: Tue, 1 Jun 2021 13:50:25 +0200
Subject: [PATCH 2/3] Minor fix: Vars renamed
---
src/components/titles/index.js | 2 +-
src/lib/svg_text_utils.js | 16 ++++++++--------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/components/titles/index.js b/src/components/titles/index.js
index 21408ca8255..3c45205fa84 100644
--- a/src/components/titles/index.js
+++ b/src/components/titles/index.js
@@ -122,7 +122,7 @@ function draw(gd, titleClass, options) {
function drawTitle(titleEl) {
var transformVal;
- var convertOptions = null;
+ var convertOptions;
if(transform) {
transformVal = '';
diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js
index a9bcdd77a16..668c7a8d788 100644
--- a/src/lib/svg_text_utils.js
+++ b/src/lib/svg_text_utils.js
@@ -22,7 +22,7 @@ var FIND_TEX = /([^$]*)([$]+[^$]*[$]+)([^$]*)/;
* @param {*} _context Context
* @param {*} gd Graph DIV
* @param {*} options All props are needed to wrap.
- * [axLength]?: number
+ * [axisLength]?: number
* [orientation]?: 'v' | 'h'
* [wrap]?: boolean
* @param {Function} _callback Callback function.
@@ -444,7 +444,7 @@ function fromCodePoint(code) {
*
* @param {svg text element} containerNode: the node to insert this text into
* @param {string} str: the pseudo-html string to convert to svg
- * @param {{ axLength: number, axOrientation: 'v' | 'h', wrap?: boolean }} options
+ * @param {{ axisLength: number, axisOrientation: 'v' | 'h', wrap?: boolean }} options
* @returns {bool}: does the result contain any links? We need to handle the text element
* somewhat differently if it does, so just keep track of this when it happens.
*/
@@ -456,7 +456,7 @@ function buildSVGText(containerNode, str, options) {
* I feel like at some point we turned these into
but currently we don't so
* I'm just going to cement what we do now in Chrome and FF
*/
- str = options && options.axOrientation === 'v' ? 'One very long string that is soo long, that
I dont get it!' : str;
+ str = options && options.axisOrientation === 'v' ? 'One very long string that is soo long, that
I dont get it!' : str;
str = str.replace(NEWLINES, ' ');
var hasLink = false;
@@ -575,7 +575,7 @@ function buildSVGText(containerNode, str, options) {
var parts = str.split(SPLIT_TAGS);
// eslint-disable-next-line no-console
- // options && options.axOrientation === 'v' && console.log(parts);
+ // options && options.axisOrientation === 'v' && console.log(parts);
var i = 0;
for(i; i < parts.length; i++) {
var parti = parts[i];
@@ -588,7 +588,7 @@ function buildSVGText(containerNode, str, options) {
} else if(tagStyle === undefined) {
// addTextNode(currentNode, convertEntities(parti));
- if(options && options.axOrientation === 'v') {
+ if(options && options.axisOrientation === 'v') {
if(options.wrap) {
var wordId = 0;
var wordsArray = parti.split(' ');
@@ -596,7 +596,7 @@ function buildSVGText(containerNode, str, options) {
var word = wordsArray[wordId];
var preSpace = wordId === 0 ? '' : ' ';
var child = addTextNode(currentNode, convertEntities(preSpace + word));
- if(currentNode.getBBox().width > options.axLength) {
+ if(currentNode.getBBox().width > options.axisLength) {
removeTextNode(currentNode, child);
newLine();
addTextNode(currentNode, convertEntities(word));
@@ -606,9 +606,9 @@ function buildSVGText(containerNode, str, options) {
}
// eslint-disable-next-line no-console
- // options && options.axOrientation === 'v' && console.log(currentNode.getBoundingClientRect().height);
+ // options && options.axisOrientation === 'v' && console.log(currentNode.getBoundingClientRect().height);
// eslint-disable-next-line no-console
- // options && options.axOrientation === 'v' && console.log(currentNode.getBBox().width);
+ // options && options.axisOrientation === 'v' && console.log(currentNode.getBBox().width);
} else {
addTextNode(currentNode, convertEntities(parti));
}
From e00d52831b520757a273ab82abe513db99fd4db6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mateusz=20Podle=C5=9Bny?=
<17722512+mfazer@users.noreply.github.com>
Date: Tue, 1 Jun 2021 15:32:06 +0200
Subject: [PATCH 3/3] Minor vars renaming / Comments update / code cleaning
---
src/components/titles/index.js | 10 +++---
src/lib/svg_text_utils.js | 56 +++++++++++++---------------------
src/plots/cartesian/axes.js | 7 ++---
3 files changed, 28 insertions(+), 45 deletions(-)
diff --git a/src/components/titles/index.js b/src/components/titles/index.js
index 3c45205fa84..39ed094c664 100644
--- a/src/components/titles/index.js
+++ b/src/components/titles/index.js
@@ -56,7 +56,7 @@ function draw(gd, titleClass, options) {
var attributes = options.attributes;
var transform = options.transform;
var group = options.containerGroup;
- var isAxis = options.isAxis; // Prepare documentation for this
+ var isAxis = options.isAxis;
var wrap = options.wrap;
var fullLayout = gd._fullLayout;
@@ -137,13 +137,13 @@ function draw(gd, titleClass, options) {
}
if(isAxis && wrap) {
- var axName = options.propContainer._name;
- var axOut = gd._fullLayout[axName];
+ var axisName = options.propContainer._name;
+ var axis = gd._fullLayout[axisName];
convertOptions = {
wrap: wrap,
- axisLength: axOut._length,
- axisOrientation: axOut._id.substr(0, 1) === 'y' ? 'v' : 'h'
+ axisLength: axis._length,
+ axisOrientation: axis._id.substr(0, 1) === 'y' ? 'v' : 'h'
};
}
diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js
index 668c7a8d788..2c82c97fd3b 100644
--- a/src/lib/svg_text_utils.js
+++ b/src/lib/svg_text_utils.js
@@ -19,13 +19,10 @@ var FIND_TEX = /([^$]*)([$]+[^$]*[$]+)([^$]*)/;
/**
* Converts to SVG element.
- * @param {*} _context Context
- * @param {*} gd Graph DIV
- * @param {*} options All props are needed to wrap.
- * [axisLength]?: number
- * [orientation]?: 'v' | 'h'
- * [wrap]?: boolean
- * @param {Function} _callback Callback function.
+ * @param {*} _context
+ * @param {*} gd `graphDiv`.
+ * @param {{ axisLength: number, axisOrientation: 'v' | 'h', wrap?: boolean }} options
+ * @param {Function} _callback
* @returns Modified `_context`.
*/
exports.convertToTspans = function(_context, gd, options, _callback) {
@@ -440,7 +437,7 @@ function fromCodePoint(code) {
}
/**
- * Converts SVG `` elements from pseudo-html into, and attach these to `containerNode`.
+ * Converts our pseudo-html SVG `` into elements, and attach these to `containerNode`.
*
* @param {svg text element} containerNode: the node to insert this text into
* @param {string} str: the pseudo-html string to convert to svg
@@ -456,7 +453,6 @@ function buildSVGText(containerNode, str, options) {
* I feel like at some point we turned these into
but currently we don't so
* I'm just going to cement what we do now in Chrome and FF
*/
- str = options && options.axisOrientation === 'v' ? 'One very long string that is soo long, that
I dont get it!' : str;
str = str.replace(NEWLINES, ' ');
var hasLink = false;
@@ -574,8 +570,7 @@ function buildSVGText(containerNode, str, options) {
}
var parts = str.split(SPLIT_TAGS);
- // eslint-disable-next-line no-console
- // options && options.axisOrientation === 'v' && console.log(parts);
+
var i = 0;
for(i; i < parts.length; i++) {
var parti = parts[i];
@@ -586,31 +581,22 @@ function buildSVGText(containerNode, str, options) {
if(tagType === 'br') {
newLine();
} else if(tagStyle === undefined) {
- // addTextNode(currentNode, convertEntities(parti));
-
- if(options && options.axisOrientation === 'v') {
- if(options.wrap) {
- var wordId = 0;
- var wordsArray = parti.split(' ');
- for(wordId; wordId < wordsArray.length; wordId++) {
- var word = wordsArray[wordId];
- var preSpace = wordId === 0 ? '' : ' ';
- var child = addTextNode(currentNode, convertEntities(preSpace + word));
- if(currentNode.getBBox().width > options.axisLength) {
- removeTextNode(currentNode, child);
- newLine();
- addTextNode(currentNode, convertEntities(word));
- }
- // eslint-disable-next-line no-console
- // console.log(currentNode.getBBox().width);
- }
+ if(!(options && options.wrap)) return void(addTextNode(currentNode, convertEntities(parti)));
- // eslint-disable-next-line no-console
- // options && options.axisOrientation === 'v' && console.log(currentNode.getBoundingClientRect().height);
- // eslint-disable-next-line no-console
- // options && options.axisOrientation === 'v' && console.log(currentNode.getBBox().width);
- } else {
- addTextNode(currentNode, convertEntities(parti));
+ var wordId = 0;
+ var wordsArray = parti.split(' ');
+
+ newLine();
+
+ for(wordId; wordId < wordsArray.length; wordId++) {
+ var word = wordsArray[wordId];
+ var preSpace = wordId === 0 ? '' : ' ';
+ var child = addTextNode(currentNode, convertEntities(preSpace + word));
+
+ if(currentNode.getBBox().width > options.axisLength) {
+ removeTextNode(currentNode, child);
+ newLine();
+ addTextNode(currentNode, convertEntities(word));
}
}
} else {
diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 842121904ea..7b16ab6c298 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -3464,7 +3464,7 @@ function drawTitle(gd, ax) {
var axId = ax._id;
var axLetter = axId.charAt(0);
var fontSize = ax.title.font.size;
- // var wrap = ax.title.wrap; // TODO: Update documentation and the TypeScript types.
+ var wrap = ax.title.wrap; // TODO: Update our API documentation and the TypeScript types!.
var titleStandoff;
@@ -3539,11 +3539,8 @@ function drawTitle(gd, ax) {
avoid: avoid,
transform: transform,
attributes: {x: x, y: y, 'text-anchor': 'middle'},
+ wrap: wrap,
isAxis: true,
- // wrap: wrap
- // HEY! This is for testing only!
- wrap: 'breakword',
- // TODO: options: 'breakword' | 'breakchar' | undefined (default)
});
}