summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/lib/utils/text_utility.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/lib/utils/text_utility.js')
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js81
1 files changed, 79 insertions, 2 deletions
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index be3fe1ed620..e2953ce330c 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -1,4 +1,9 @@
-import { isString } from 'lodash';
+import { isString, memoize } from 'lodash';
+
+import {
+ TRUNCATE_WIDTH_DEFAULT_WIDTH,
+ TRUNCATE_WIDTH_DEFAULT_FONT_SIZE,
+} from '~/lib/utils/constants';
/**
* Adds a , to a string composed by numbers, at every 3 chars.
@@ -73,7 +78,79 @@ export const slugifyWithUnderscore = str => slugify(str, '_');
* @param {Number} maxLength
* @returns {String}
*/
-export const truncate = (string, maxLength) => `${string.substr(0, maxLength - 3)}...`;
+export const truncate = (string, maxLength) => {
+ if (string.length - 1 > maxLength) {
+ return `${string.substr(0, maxLength - 1)}…`;
+ }
+
+ return string;
+};
+
+/**
+ * This function calculates the average char width. It does so by placing a string in the DOM and measuring the width.
+ * NOTE: This will cause a reflow and should be used sparsely!
+ * The default fontFamily is 'sans-serif' and 12px in ECharts, so that is the default basis for calculating the average with.
+ * https://echarts.apache.org/en/option.html#xAxis.nameTextStyle.fontFamily
+ * https://echarts.apache.org/en/option.html#xAxis.nameTextStyle.fontSize
+ * @param {Object} options
+ * @param {Number} options.fontSize style to size the text for measurement
+ * @param {String} options.fontFamily style of font family to measure the text with
+ * @param {String} options.chars string of chars to use as a basis for calculating average width
+ * @return {Number}
+ */
+const getAverageCharWidth = memoize(function getAverageCharWidth(options = {}) {
+ const {
+ fontSize = 12,
+ fontFamily = 'sans-serif',
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ chars = ' ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
+ } = options;
+ const div = document.createElement('div');
+
+ div.style.fontFamily = fontFamily;
+ div.style.fontSize = `${fontSize}px`;
+ // Place outside of view
+ div.style.position = 'absolute';
+ div.style.left = -1000;
+ div.style.top = -1000;
+
+ div.innerHTML = chars;
+
+ document.body.appendChild(div);
+ const width = div.clientWidth;
+ document.body.removeChild(div);
+
+ return width / chars.length / fontSize;
+});
+
+/**
+ * This function returns a truncated version of `string` if its estimated rendered width is longer than `maxWidth`,
+ * otherwise it will return the original `string`
+ * Inspired by https://bl.ocks.org/tophtucker/62f93a4658387bb61e4510c37e2e97cf
+ * @param {String} string text to truncate
+ * @param {Object} options
+ * @param {Number} options.maxWidth largest rendered width the text may have
+ * @param {Number} options.fontSize size of the font used to render the text
+ * @return {String} either the original string or a truncated version
+ */
+export const truncateWidth = (string, options = {}) => {
+ const {
+ maxWidth = TRUNCATE_WIDTH_DEFAULT_WIDTH,
+ fontSize = TRUNCATE_WIDTH_DEFAULT_FONT_SIZE,
+ } = options;
+ const { truncateIndex } = string.split('').reduce(
+ (memo, char, index) => {
+ let newIndex = index;
+ if (memo.width > maxWidth) {
+ newIndex = memo.truncateIndex;
+ }
+ return { width: memo.width + getAverageCharWidth() * fontSize, truncateIndex: newIndex };
+ },
+ { width: 0, truncateIndex: 0 },
+ );
+
+ return truncate(string, truncateIndex);
+};
/**
* Truncate SHA to 8 characters