132 lines
4.0 KiB
JavaScript
132 lines
4.0 KiB
JavaScript
"use strict";
|
|
|
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.getStyleString = exports.getStringSize = exports.MEASUREMENT_SPAN_ID = void 0;
|
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
// DOM utils taken from
|
|
// https://github.com/recharts/recharts/blob/master/src/util/DOMUtils.ts
|
|
|
|
function isSsr() {
|
|
return typeof window === 'undefined';
|
|
}
|
|
const stringCache = new Map();
|
|
const MAX_CACHE_NUM = 2000;
|
|
const SPAN_STYLE = {
|
|
position: 'absolute',
|
|
top: '-20000px',
|
|
left: 0,
|
|
padding: 0,
|
|
margin: 0,
|
|
border: 'none',
|
|
whiteSpace: 'pre'
|
|
};
|
|
const STYLE_LIST = ['minWidth', 'maxWidth', 'width', 'minHeight', 'maxHeight', 'height', 'top', 'left', 'fontSize', 'padding', 'margin', 'paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom', 'marginLeft', 'marginRight', 'marginTop', 'marginBottom'];
|
|
const MEASUREMENT_SPAN_ID = exports.MEASUREMENT_SPAN_ID = 'mui_measurement_span';
|
|
|
|
/**
|
|
*
|
|
* @param name CSS property name
|
|
* @param value
|
|
* @returns add 'px' for distance properties
|
|
*/
|
|
function autoCompleteStyle(name, value) {
|
|
if (STYLE_LIST.indexOf(name) >= 0 && value === +value) {
|
|
return `${value}px`;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param text camelcase css property
|
|
* @returns css property
|
|
*/
|
|
function camelToMiddleLine(text) {
|
|
const strs = text.split('');
|
|
const formatStrs = strs.reduce((result, entry) => {
|
|
if (entry === entry.toUpperCase()) {
|
|
return [...result, '-', entry.toLowerCase()];
|
|
}
|
|
return [...result, entry];
|
|
}, []);
|
|
return formatStrs.join('');
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param style React style object
|
|
* @returns CSS styling string
|
|
*/
|
|
const getStyleString = style => Object.keys(style).sort().reduce((result, s) => `${result}${camelToMiddleLine(s)}:${autoCompleteStyle(s, style[s])};`, '');
|
|
exports.getStyleString = getStyleString;
|
|
let domCleanTimeout;
|
|
|
|
/**
|
|
*
|
|
* @param text The string to estimate
|
|
* @param style The style applied
|
|
* @returns width and height of the text
|
|
*/
|
|
const getStringSize = (text, style = {}) => {
|
|
if (text === undefined || text === null || isSsr()) {
|
|
return {
|
|
width: 0,
|
|
height: 0
|
|
};
|
|
}
|
|
const str = `${text}`;
|
|
const styleString = getStyleString(style);
|
|
const cacheKey = `${str}-${styleString}`;
|
|
const size = stringCache.get(cacheKey);
|
|
if (size) {
|
|
return size;
|
|
}
|
|
try {
|
|
let measurementSpan = document.getElementById(MEASUREMENT_SPAN_ID);
|
|
if (measurementSpan === null) {
|
|
measurementSpan = document.createElement('span');
|
|
measurementSpan.setAttribute('id', MEASUREMENT_SPAN_ID);
|
|
measurementSpan.setAttribute('aria-hidden', 'true');
|
|
document.body.appendChild(measurementSpan);
|
|
}
|
|
// Need to use CSS Object Model (CSSOM) to be able to comply with Content Security Policy (CSP)
|
|
// https://en.wikipedia.org/wiki/Content_Security_Policy
|
|
const measurementSpanStyle = (0, _extends2.default)({}, SPAN_STYLE, style);
|
|
Object.keys(measurementSpanStyle).map(styleKey => {
|
|
measurementSpan.style[camelToMiddleLine(styleKey)] = autoCompleteStyle(styleKey, measurementSpanStyle[styleKey]);
|
|
return styleKey;
|
|
});
|
|
measurementSpan.textContent = str;
|
|
const rect = measurementSpan.getBoundingClientRect();
|
|
const result = {
|
|
width: rect.width,
|
|
height: rect.height
|
|
};
|
|
stringCache.set(cacheKey, result);
|
|
if (stringCache.size + 1 > MAX_CACHE_NUM) {
|
|
stringCache.clear();
|
|
}
|
|
if (process.env.NODE_ENV === 'test') {
|
|
// In test environment, we clean the measurement span immediately
|
|
measurementSpan.textContent = '';
|
|
} else {
|
|
if (domCleanTimeout) {
|
|
clearTimeout(domCleanTimeout);
|
|
}
|
|
domCleanTimeout = setTimeout(() => {
|
|
// Limit node cleaning to once per render cycle
|
|
measurementSpan.textContent = '';
|
|
}, 0);
|
|
}
|
|
return result;
|
|
} catch {
|
|
return {
|
|
width: 0,
|
|
height: 0
|
|
};
|
|
}
|
|
};
|
|
exports.getStringSize = getStringSize; |