@mui/x-data-grid
Version:
The Community plan edition of the Data Grid components (MUI X).
204 lines (202 loc) • 6.88 kB
JavaScript
import { gridClasses } from "../constants/gridClasses.js";
export function isOverflown(element) {
return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
}
export function findParentElementFromClassName(elem, className) {
return elem.closest(`.${className}`);
}
// TODO, eventually replaces this function with CSS.escape, once available in jsdom, either added manually or built in
// https://github.com/jsdom/jsdom/issues/1550#issuecomment-236734471
export function escapeOperandAttributeSelector(operand) {
return operand.replace(/["\\]/g, '\\$&');
}
export function getGridColumnHeaderElement(root, field) {
return root.querySelector(`[role="columnheader"][data-field="${escapeOperandAttributeSelector(field)}"]`);
}
function getGridRowElementSelector(id) {
return `.${gridClasses.row}[data-id="${escapeOperandAttributeSelector(String(id))}"]`;
}
export function getGridRowElement(root, id) {
return root.querySelector(getGridRowElementSelector(id));
}
export function getGridCellElement(root, {
id,
field
}) {
const rowSelector = getGridRowElementSelector(id);
const cellSelector = `.${gridClasses.cell}[data-field="${escapeOperandAttributeSelector(field)}"]`;
const selector = `${rowSelector} ${cellSelector}`;
return root.querySelector(selector);
}
// https://www.abeautifulsite.net/posts/finding-the-active-element-in-a-shadow-root/
export const getActiveElement = (root = document) => {
const activeEl = root.activeElement;
if (!activeEl) {
return null;
}
if (activeEl.shadowRoot) {
return getActiveElement(activeEl.shadowRoot);
}
return activeEl;
};
export function isEventTargetInPortal(event) {
if (
// The target is not an element when triggered by a Select inside the cell
// See https://github.com/mui/material-ui/issues/10534
event.target.nodeType === 1 && !event.currentTarget.contains(event.target)) {
return true;
}
return false;
}
export function getFieldFromHeaderElem(colCellEl) {
return colCellEl.getAttribute('data-field');
}
export function findHeaderElementFromField(elem, field) {
return elem.querySelector(`[data-field="${escapeOperandAttributeSelector(field)}"]`);
}
export function getFieldsFromGroupHeaderElem(colCellEl) {
return colCellEl.getAttribute('data-fields').slice(2, -2).split('-|-');
}
export function findGroupHeaderElementsFromField(elem, field) {
return Array.from(elem.querySelectorAll(`[data-fields*="|-${escapeOperandAttributeSelector(field)}-|"]`) ?? []);
}
export function findGridCellElementsFromCol(col, api) {
const root = findParentElementFromClassName(col, gridClasses.root);
if (!root) {
throw new Error('MUI X: The root element is not found.');
}
const ariaColIndex = col.getAttribute('aria-colindex');
if (!ariaColIndex) {
return [];
}
const colIndex = Number(ariaColIndex) - 1;
const cells = [];
if (!api.virtualScrollerRef?.current) {
return [];
}
queryRows(api).forEach(rowElement => {
const rowId = rowElement.getAttribute('data-id');
if (!rowId) {
return;
}
let columnIndex = colIndex;
const cellColSpanInfo = api.unstable_getCellColSpanInfo(rowId, colIndex);
if (cellColSpanInfo && cellColSpanInfo.spannedByColSpan) {
columnIndex = cellColSpanInfo.leftVisibleCellIndex;
}
const cell = rowElement.querySelector(`[data-colindex="${columnIndex}"]`);
if (cell) {
cells.push(cell);
}
});
return cells;
}
export function findGridElement(api, klass) {
return api.rootElementRef.current.querySelector(`.${gridClasses[klass]}`);
}
const findPinnedCells = ({
api,
colIndex,
position,
filterFn
}) => {
if (colIndex === null) {
return [];
}
const cells = [];
queryRows(api).forEach(rowElement => {
const rowId = rowElement.getAttribute('data-id');
if (!rowId) {
return;
}
rowElement.querySelectorAll(`.${gridClasses[position === 'left' ? 'cell--pinnedLeft' : 'cell--pinnedRight']}`).forEach(cell => {
const currentColIndex = parseCellColIndex(cell);
if (currentColIndex !== null && filterFn(currentColIndex)) {
cells.push(cell);
}
});
});
return cells;
};
export function findLeftPinnedCellsAfterCol(api, col, isRtl) {
const colIndex = parseCellColIndex(col);
return findPinnedCells({
api,
colIndex,
position: isRtl ? 'right' : 'left',
filterFn: index => isRtl ? index < colIndex : index > colIndex
});
}
export function findRightPinnedCellsBeforeCol(api, col, isRtl) {
const colIndex = parseCellColIndex(col);
return findPinnedCells({
api,
colIndex,
position: isRtl ? 'left' : 'right',
filterFn: index => isRtl ? index > colIndex : index < colIndex
});
}
const findPinnedHeaders = ({
api,
colIndex,
position,
filterFn
}) => {
if (!api.columnHeadersContainerRef?.current) {
return [];
}
if (colIndex === null) {
return [];
}
const elements = [];
api.columnHeadersContainerRef.current.querySelectorAll(`.${gridClasses[position === 'left' ? 'columnHeader--pinnedLeft' : 'columnHeader--pinnedRight']}`).forEach(element => {
const currentColIndex = parseCellColIndex(element);
if (currentColIndex !== null && filterFn(currentColIndex, element)) {
elements.push(element);
}
});
return elements;
};
export function findLeftPinnedHeadersAfterCol(api, col, isRtl) {
const colIndex = parseCellColIndex(col);
return findPinnedHeaders({
api,
position: isRtl ? 'right' : 'left',
colIndex,
filterFn: index => isRtl ? index < colIndex : index > colIndex
});
}
export function findRightPinnedHeadersBeforeCol(api, col, isRtl) {
const colIndex = parseCellColIndex(col);
return findPinnedHeaders({
api,
position: isRtl ? 'left' : 'right',
colIndex,
filterFn: (index, element) => {
if (element.classList.contains(gridClasses['columnHeader--last'])) {
return false;
}
return isRtl ? index > colIndex : index < colIndex;
}
});
}
export function findGridHeader(api, field) {
const headers = api.columnHeadersContainerRef.current;
return headers.querySelector(`:scope > div > [data-field="${escapeOperandAttributeSelector(field)}"][role="columnheader"]`);
}
export function findGridCells(api, field) {
const container = api.virtualScrollerRef.current;
return Array.from(container.querySelectorAll(`:scope > div > div > div > [data-field="${escapeOperandAttributeSelector(field)}"][role="gridcell"]`));
}
function queryRows(api) {
return api.virtualScrollerRef.current.querySelectorAll(
// Use > to ignore rows from nested Data Grids (for example in detail panel)
`:scope > div > div > .${gridClasses.row}`);
}
function parseCellColIndex(col) {
const ariaColIndex = col.getAttribute('aria-colindex');
if (!ariaColIndex) {
return null;
}
return Number(ariaColIndex) - 1;
}