handsontable
Version:
Handsontable is a JavaScript Data Grid available for React, Angular and Vue.
136 lines (134 loc) • 4.47 kB
JavaScript
import { arrayEach } from "../../../helpers/array.mjs";
import { rangeEach } from "../../../helpers/number.mjs";
import { addClass, setAttribute } from "../../../helpers/dom/element.mjs";
import BaseUI from "./_base.mjs";
import { A11Y_EXPANDED, A11Y_HIDDEN } from "../../../helpers/a11y.mjs";
/**
* Class responsible for the UI in the Nested Rows' row headers.
*
* @private
* @class HeadersUI
* @augments BaseUI
*/
class HeadersUI extends BaseUI {
/**
* CSS classes used in the row headers.
*
* @type {object}
*/
static get CSS_CLASSES() {
return {
indicatorContainer: 'ht_nestingLevels',
parent: 'ht_nestingParent',
indicator: 'ht_nestingLevel',
emptyIndicator: 'ht_nestingLevel_empty',
button: 'ht_nestingButton',
expandButton: 'ht_nestingExpand',
collapseButton: 'ht_nestingCollapse'
};
}
constructor(nestedRowsPlugin, hotInstance) {
super(nestedRowsPlugin, hotInstance);
/**
* Reference to the DataManager instance connected with the Nested Rows plugin.
*
* @type {DataManager}
*/
this.dataManager = this.plugin.dataManager;
// /**
// * Level cache array.
// *
// * @type {Array}
// */
// this.levelCache = this.dataManager.cache.levels;
/**
* Reference to the CollapsingUI instance connected with the Nested Rows plugin.
*
* @type {CollapsingUI}
*/
this.collapsingUI = this.plugin.collapsingUI;
/**
* Cache for the row headers width.
*
* @type {null|number}
*/
this.rowHeaderWidthCache = null;
}
/**
* Append nesting indicators and buttons to the row headers.
*
* @private
* @param {number} row Row index.
* @param {HTMLElement} TH TH 3element.
*/
appendLevelIndicators(row, TH) {
const rowIndex = this.hot.toPhysicalRow(row);
const rowObject = this.dataManager.getDataObject(rowIndex);
if (!rowObject) {
return;
}
const rowLevel = this.dataManager.getRowLevel(rowIndex);
const innerDiv = TH.getElementsByTagName('DIV')[0];
const innerSpan = innerDiv.querySelector('span.rowHeader');
const previousIndicators = innerDiv.querySelectorAll('[class^="ht_nesting"]');
const ariaEnabled = this.hot.getSettings().ariaTags;
arrayEach(previousIndicators, elem => {
if (elem) {
innerDiv.removeChild(elem);
}
});
addClass(TH, HeadersUI.CSS_CLASSES.indicatorContainer);
if (rowLevel) {
const {
rootDocument
} = this.hot;
const initialContent = innerSpan.cloneNode(true);
innerDiv.innerHTML = '';
rangeEach(0, rowLevel - 1, () => {
const levelIndicator = rootDocument.createElement('SPAN');
addClass(levelIndicator, HeadersUI.CSS_CLASSES.emptyIndicator);
innerDiv.appendChild(levelIndicator);
});
innerDiv.appendChild(initialContent);
}
if (this.dataManager.hasChildren(rowObject)) {
const buttonsContainer = this.hot.rootDocument.createElement('DIV');
if (ariaEnabled) {
setAttribute(buttonsContainer, [A11Y_HIDDEN()]);
}
addClass(TH, HeadersUI.CSS_CLASSES.parent);
if (this.collapsingUI.areChildrenCollapsed(rowIndex)) {
addClass(buttonsContainer, `${HeadersUI.CSS_CLASSES.button} ${HeadersUI.CSS_CLASSES.expandButton}`);
if (ariaEnabled) {
setAttribute(TH, [A11Y_EXPANDED(false)]);
}
} else {
addClass(buttonsContainer, `${HeadersUI.CSS_CLASSES.button} ${HeadersUI.CSS_CLASSES.collapseButton}`);
if (ariaEnabled) {
setAttribute(TH, [A11Y_EXPANDED(true)]);
}
}
innerDiv.appendChild(buttonsContainer);
}
}
/**
* Update the row header width according to number of levels in the dataset.
*
* @private
* @param {number} deepestLevel Cached deepest level of nesting.
*/
updateRowHeaderWidth(deepestLevel) {
let deepestLevelIndex = deepestLevel;
if (!deepestLevelIndex) {
deepestLevelIndex = this.dataManager.cache.levelCount;
}
let completeVerticalPadding = 11;
if (!this.hot.stylesHandler.isClassicTheme()) {
const verticalPadding = this.hot.stylesHandler.getCSSVariableValue('cell-horizontal-padding');
completeVerticalPadding = verticalPadding * 2;
}
this.rowHeaderWidthCache = Math.max(50, completeVerticalPadding + 10 * deepestLevelIndex + 25);
this.hot.render();
}
}
export default HeadersUI;