handsontable
Version:
Handsontable is a JavaScript Data Grid available for React, Angular and Vue.
295 lines (288 loc) • 12.2 kB
JavaScript
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); }
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); }
function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; }
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
import { warn } from "../helpers/console.mjs";
import handsontableStyles from "../styles/handsontableStyles.mjs";
/**
* The id of the core styles element injected into the document head.
*
* @type {string}
*/
const CORE_STYLES_ID = 'handsontable-core-styles';
/**
* Handles the theme-related style operations.
*/
var _hot = /*#__PURE__*/new WeakMap();
var _themeName = /*#__PURE__*/new WeakMap();
var _rootElement = /*#__PURE__*/new WeakMap();
var _rootComputedStyle = /*#__PURE__*/new WeakMap();
var _rootDocument = /*#__PURE__*/new WeakMap();
var _cssVars = /*#__PURE__*/new WeakMap();
var _computedStyles = /*#__PURE__*/new WeakMap();
var _onThemeChange = /*#__PURE__*/new WeakMap();
var _StylesHandler_brand = /*#__PURE__*/new WeakSet();
export class StylesHandler {
/**
* Initializes a new instance of the `StylesHandler` class.
*
* @param {object} options The options for the `StylesHandler` instance.
* @param {Core} options.hot The instance of the Handsontable.
* @param {HTMLElement} options.rootElement The root element of the instance.
* @param {Document} options.rootDocument The root document of the instance.
* @param {function(string)} options.onThemeChange The callback function to be called when the theme changes.
* @param {boolean} options.injectCoreCss Whether to inject the core styles into the document head.
*/
constructor(_ref) {
let {
hot,
rootElement: _rootElement2,
rootDocument: _rootDocument2,
onThemeChange = () => {},
injectCoreCss = true
} = _ref;
_classPrivateMethodInitSpec(this, _StylesHandler_brand);
/**
* The instance of the Handsontable.
*
* @type {Core}
*/
_classPrivateFieldInitSpec(this, _hot, void 0);
/**
* The name of the theme.
*
* @type {string|undefined}
*/
_classPrivateFieldInitSpec(this, _themeName, void 0);
/**
* The instance's root element.
*
* @type {HTMLElement}
*/
_classPrivateFieldInitSpec(this, _rootElement, void 0);
/**
* The computed style of the root element.
*
* @type {CSSStyleDeclaration}
* @private
*/
_classPrivateFieldInitSpec(this, _rootComputedStyle, void 0);
/**
* The root document of the instance.
*
* @type {Document}
*/
_classPrivateFieldInitSpec(this, _rootDocument, void 0);
/**
* An object to store CSS variable values.
*
* @type {object}
*/
_classPrivateFieldInitSpec(this, _cssVars, {});
/**
* Stores the computed styles for various elements.
*
* @type {object} - An object containing the computed styles if a nested structure of `element: { [element type]: {property: value} }`.
*/
_classPrivateFieldInitSpec(this, _computedStyles, {});
/**
* The callback function to be called when the theme changes.
*
* @type {function(string)}
*/
_classPrivateFieldInitSpec(this, _onThemeChange, void 0);
_classPrivateFieldSet(_hot, this, hot);
_classPrivateFieldSet(_rootElement, this, _rootElement2);
_classPrivateFieldSet(_rootDocument, this, _rootDocument2);
_classPrivateFieldSet(_onThemeChange, this, onThemeChange);
if (injectCoreCss) {
_assertClassBrand(_StylesHandler_brand, this, _injectCoreStyles).call(this);
}
}
/**
* Retrieves the value of a specified CSS variable.
*
* @param {string} variableName - The name of the CSS variable to retrieve.
* @returns {number|null|undefined} The value of the specified CSS variable, or `undefined` if not found.
*/
getCSSVariableValue(variableName) {
var _assertClassBrand$cal;
if (_classPrivateFieldGet(_cssVars, this)[`--ht-${variableName}`]) {
return _classPrivateFieldGet(_cssVars, this)[`--ht-${variableName}`];
}
const acquiredValue = (_assertClassBrand$cal = _assertClassBrand(_StylesHandler_brand, this, _getParsedNumericCSSValue).call(this, `--ht-${variableName}`)) !== null && _assertClassBrand$cal !== void 0 ? _assertClassBrand$cal : _assertClassBrand(_StylesHandler_brand, this, _getCSSValue).call(this, `--ht-${variableName}`);
if (acquiredValue !== null) {
_classPrivateFieldGet(_cssVars, this)[`--ht-${variableName}`] = acquiredValue;
return acquiredValue;
}
}
/**
* Retrieves the computed style value for a specified CSS property of a `td` element.
*
* @param {string} cssProperty - The CSS property to retrieve the value for.
* @returns {number|string|undefined} The value of the specified CSS property, or `undefined` if not found.
*/
getStyleForTD(cssProperty) {
var _classPrivateFieldGet2;
return (_classPrivateFieldGet2 = _classPrivateFieldGet(_computedStyles, this)) === null || _classPrivateFieldGet2 === void 0 || (_classPrivateFieldGet2 = _classPrivateFieldGet2.td) === null || _classPrivateFieldGet2 === void 0 ? void 0 : _classPrivateFieldGet2[cssProperty];
}
/**
* Calculates the row height based on the current theme and CSS variables.
*
* @param {number} [visualRowIndex] The visual row index.
* @returns {number} The calculated row height.
*/
getDefaultRowHeight(visualRowIndex) {
const rowHeight = _assertClassBrand(_StylesHandler_brand, this, _calculateRowHeight).call(this);
if (visualRowIndex !== undefined && visualRowIndex === _classPrivateFieldGet(_hot, this).view.getFirstRenderedVisibleRow()) {
// add 1px border-top-width compensation for the first rendered row
return rowHeight + 1;
}
return rowHeight;
}
/**
* Checks if the cells are using the `border-box` box-sizing model.
*
* @returns {boolean}
*/
areCellsBorderBox() {
return this.getStyleForTD('box-sizing') === 'border-box';
}
/**
* Applies the specified theme to the instance.
*
* @param {string|undefined|boolean} [themeName] - The name of the theme to apply.
*/
useTheme(themeName) {
if (!/ht-theme-.*/.test(themeName)) {
warn(`${themeName} isn't a valid theme name. Please ensure it follows the format ht-theme-<theme-name>.`);
return;
}
_assertClassBrand(_StylesHandler_brand, this, _clearCachedValues).call(this);
if (themeName && themeName !== _classPrivateFieldGet(_themeName, this)) {
_classPrivateFieldSet(_themeName, this, themeName);
}
_classPrivateFieldGet(_onThemeChange, this).call(this, _classPrivateFieldGet(_themeName, this));
_assertClassBrand(_StylesHandler_brand, this, _cacheStylesheetValues).call(this);
}
/**
* Gets the name of the theme.
*
* @returns {string|undefined}
*/
getThemeName() {
return _classPrivateFieldGet(_themeName, this);
}
/**
* Clears all cached CSS variable values and computed styles.
* This should be called when theme CSS variables are dynamically updated.
*/
clearCache() {
_assertClassBrand(_StylesHandler_brand, this, _clearCachedValues).call(this);
_assertClassBrand(_StylesHandler_brand, this, _cacheStylesheetValues).call(this);
}
}
function _injectCoreStyles() {
if (!_classPrivateFieldGet(_hot, this) || !_classPrivateFieldGet(_rootDocument, this) || !_classPrivateFieldGet(_rootDocument, this).head) {
return;
}
const existing = _classPrivateFieldGet(_rootDocument, this).getElementById(CORE_STYLES_ID);
if (existing && existing instanceof HTMLStyleElement) {
return;
}
const baseStyles = _classPrivateFieldGet(_rootDocument, this).createElement('style');
baseStyles.id = CORE_STYLES_ID;
baseStyles.textContent = handsontableStyles;
_classPrivateFieldGet(_rootDocument, this).head.appendChild(baseStyles);
}
/**
* Calculates the row height based on the current theme and CSS variables.
*
* @returns {number|null} The calculated row height, or `null` if any required CSS variable is not found.
*/
function _calculateRowHeight() {
const lineHeightVarValue = this.getCSSVariableValue('line-height');
const verticalPaddingVarValue = this.getCSSVariableValue('cell-vertical-padding');
const bottomBorderWidth = Math.ceil(parseFloat(this.getStyleForTD('border-bottom-width')));
if (lineHeightVarValue === null || verticalPaddingVarValue === null || isNaN(bottomBorderWidth)) {
return null;
}
return lineHeightVarValue + 2 * verticalPaddingVarValue + bottomBorderWidth;
}
/**
* Caches the computed style values for the root element and `td` element.
*/
function _cacheStylesheetValues() {
_classPrivateFieldSet(_rootComputedStyle, this, getComputedStyle(_classPrivateFieldGet(_rootElement, this)));
const stylesForTD = _assertClassBrand(_StylesHandler_brand, this, _getStylesForTD).call(this, ['box-sizing', 'border-bottom-width']);
_classPrivateFieldGet(_computedStyles, this).td = {
..._classPrivateFieldGet(_computedStyles, this).td,
...{
'box-sizing': stylesForTD['box-sizing'],
'border-bottom-width': stylesForTD['border-bottom-width']
}
};
}
/**
* Retrieves and processes the computed styles for a `td` element.
*
* This method creates a temporary table structure, appends it to the root element,
* retrieves the computed styles for the `td` element, and then removes the table
* from the DOM. The computed styles are passed to the provided callback function.
*
* @param {Array} cssProps - An array of CSS properties to retrieve.
* @returns {object} An object containing the requested computed styles for the `td` element.
* @private
*/
function _getStylesForTD(cssProps) {
const rootDocument = _classPrivateFieldGet(_rootDocument, this);
const rootElement = _classPrivateFieldGet(_rootElement, this);
const table = rootDocument.createElement('table');
const tbody = rootDocument.createElement('tbody');
const tr = rootDocument.createElement('tr');
// This needs not to be the first row in order to get "regular" values.
const tr2 = rootDocument.createElement('tr');
const td = rootDocument.createElement('td');
tr2.appendChild(td);
tbody.appendChild(tr);
tbody.appendChild(tr2);
table.appendChild(tbody);
rootElement.appendChild(table);
const computedStyle = getComputedStyle(td);
const returnObject = {};
cssProps.forEach(prop => {
returnObject[prop] = computedStyle.getPropertyValue(prop);
});
rootElement.removeChild(table);
return returnObject;
}
/**
* Parses the numeric value of a specified CSS property from the root element's computed style.
*
* @param {string} property - The CSS property to retrieve and parse.
* @returns {number|null} The parsed value of the CSS property or `null` if non-existent.
*/
function _getParsedNumericCSSValue(property) {
const parsedValue = Math.ceil(parseFloat(_assertClassBrand(_StylesHandler_brand, this, _getCSSValue).call(this, property)));
return Number.isNaN(parsedValue) ? null : parsedValue;
}
/**
* Retrieves the non-numeric value of a specified CSS property from the root element's computed style.
*
* @param {string} property - The CSS property to retrieve.
* @returns {string|null} The value of the specified CSS property or `null` if non-existent.
*/
function _getCSSValue(property) {
var _classPrivateFieldGet3;
const acquiredValue = (_classPrivateFieldGet3 = _classPrivateFieldGet(_rootComputedStyle, this)) === null || _classPrivateFieldGet3 === void 0 ? void 0 : _classPrivateFieldGet3.getPropertyValue(property);
return acquiredValue === '' ? null : acquiredValue;
}
/**
* Clears the cached values.
*/
function _clearCachedValues() {
_classPrivateFieldSet(_computedStyles, this, {});
_classPrivateFieldSet(_cssVars, this, {});
}