UNPKG

@sap-ux/ui-components

Version:

SAP UI Components Library

255 lines 8.74 kB
import React from 'react'; import { Sticky } from '@fluentui/react'; import { UIIcon } from '../index.js'; /** * Method to copy and sort. * * @param {T[]} items * @param {string }columnKey * @param {boolean} isSortedDescending * @returns {T[]} */ export function _copyAndSort(items, columnKey, isSortedDescending) { const key = columnKey; return items.slice(0).sort((a, b) => { const comparison = a[key] < b[key]; const shouldSwap = isSortedDescending ? !comparison : comparison; return shouldSwap ? 1 : -1; }); } /** * Focus edited cell. * * @param {EditedCell | undefined} editedCell * @param {UITableProps} props * @param {string} direction * @returns {Promise<void>} */ export function focusEditedCell(editedCell, props, direction = '') { const rowIndex = editedCell?.rowIndex || 0; const colKey = editedCell?.column?.key || ''; let newRowIndex = rowIndex; let newColKey = colKey; if (!colKey) { return Promise.resolve(); } // go up/down/left/right if (direction) { const editableColumnKeys = props.columns.filter((c) => c.editable).map((c) => c.key); const rowsLen = props.items.length; const colIdx = editableColumnKeys.indexOf(colKey); const moves = { right: () => { if (colIdx < editableColumnKeys.length - 1) { newColKey = editableColumnKeys[colIdx + 1]; } }, left: () => { if (colIdx > 0) { newColKey = editableColumnKeys[colIdx - 1]; } }, down: () => { if (rowIndex < rowsLen - 1) { newRowIndex = rowIndex + 1; } }, up: () => { if (rowIndex > 0) { newRowIndex = rowIndex - 1; } } }; moves[direction]?.(); } return new Promise((resolve) => { requestAnimationFrame(() => { const cell = getCellFromCoords(newRowIndex, newColKey, props.columns, props.showRowNumbers); if (cell && direction) { cell.click(); } cell?.focus(); requestAnimationFrame(() => resolve()); }); }); } /** * Get cell from coordinates. * * @param {number} rowIdx * @param {string} columnKey * @param {UIColumn} columns * @param {boolean} addOneToColIndex * @returns {NodeListOf<Element>} */ export function getCellFromCoords(rowIdx, columnKey, columns, addOneToColIndex = false) { const selectedIdx = (columns.findIndex((col) => col.key === columnKey) || 0) + +addOneToColIndex; const row = document.querySelector(`.ms-DetailsList .ms-DetailsList-contentWrapper .ms-List-page .ms-DetailsRow[data-item-index="${rowIdx || 0}"]`); const cols = row?.querySelectorAll('.ms-DetailsRow-cell'); return cols?.length && cols[selectedIdx]; } // manual workaround due to the lack of API for selecting columns /** * Scrolls to column. * * @param {string} columnKey * @param {UIColumn[]} columns * @param {number} selectedRow * @param {boolean} addOneToColIndex */ export function scrollToColumn(columnKey, columns, selectedRow, addOneToColIndex = false) { const sidebar = document.querySelector('.data-editor__sidebar'); const sidebarWidth = (sidebar?.getBoundingClientRect().width ?? 0) + (addOneToColIndex ? 20 : 0); const scrollContainer = document.querySelector('.ms-ScrollablePane--contentContainer'); const cell = getCellFromCoords(selectedRow, columnKey, columns, addOneToColIndex); const box = cell?.getBoundingClientRect(); if (scrollContainer && box) { const left = scrollContainer.scrollLeft + Math.ceil(box.x) - sidebarWidth; requestAnimationFrame(() => scrollContainer.scrollTo({ left, behavior: 'smooth' })); } } /** * Scrolls to row. * * @param {number} idx * @param {IDetailsList | null} table */ export function scrollToRow(idx = 0, table = null) { if (!table) { return; } table.focusIndex(idx, false); const slctr = `.ms-DetailsList-contentWrapper .ms-DetailsRow[data-selection-index="${idx}"]`; waitFor(slctr) .then((rowEl) => { rowEl.setAttribute('tabIndex', '0'); rowEl.click(); rowEl.focus(); showFocus(); }) .catch((e) => { throw e; }); } /** * Wait for selector. * * @param {string} selector * @param {number} count - number of tries, after which the element is considered not found * (so that the function doesn't run forever) * @returns {Promise<Element>} */ export async function waitFor(selector, count = 10) { if (count === 0) { throw new Error('Element for selector not found: ' + selector); } await sleep(); const el = document.querySelector(selector); return el ? Promise.resolve(el) : waitFor(selector, count - 1); } /** * Promisified setTimeout. * * @param ms * @returns Promise */ export function sleep(ms = 200) { return new Promise((resolve) => setTimeout(resolve, ms)); } /** * Adds row numbers. * * @param {any} columns * @param {boolean} showRowNumbers * @returns {any} */ export function addRowNumbers(columns, showRowNumbers = false) { if (showRowNumbers && columns[0].key !== '__row_number__') { columns.unshift({ key: '__row_number__', name: '#', fieldName: '__row_number__', minWidth: 24, maxWidth: 24, isResizable: false }); } return columns; } export const _onHeaderRender = (props, defaultRender) => { if (!props || !defaultRender) { return null; } const customProps = { ...props, onRenderColumnHeaderTooltip: (item, tooltipDefaultRender) => { if (item?.column) { const column = item.column; const editable = (column.editable ? '' : 'not-') + 'editable-container'; const isRowNumber = column.key === '__row_number__'; const containerDiv = isRowNumber ? React.createElement("div", { className: editable }) : null; // row number column header is focusable on the parent of this cell const isFocusable = !isRowNumber; return (React.createElement("div", { className: "data-editor__header-cell", "data-is-focusable": isFocusable }, column.iconName && (React.createElement(UIIcon, { className: "type-icon", iconName: column.iconName, title: column.iconTooltip || '' })), React.createElement("span", { title: column?.headerTooltip || column.name }, column.name), containerDiv)); } const isCheckTooltip = item?.hostClassName === 'ms-DetailsHeader-checkTooltip'; if (props.selection && isCheckTooltip && tooltipDefaultRender) { return tooltipDefaultRender(item); } return null; } }; return React.createElement(Sticky, null, defaultRender(customProps)); }; /** * Gets styles for selected cell. * * @param state * @returns {Partial<IDetailsListStyles>} */ export function getStylesForSelectedCell(state) { let styles = {}; const editedRow = state.editedCell ? state.editedCell.rowIndex : undefined; const editedColumn = state.editedCell ? state.editedCell.column : undefined; if (editedRow !== undefined && editedColumn !== undefined) { styles = { root: { ['div.ms-List-cell']: { position: 'relative', zIndex: 0 }, [`div.ms-List-cell[data-list-index="${editedRow}"]`]: { zIndex: 1 }, [`.ms-DetailsRow-cell`]: { overflow: 'hidden' }, [`div.ms-List-cell[data-list-index="${editedRow}"] .ms-DetailsRow-cell[data-automation-key="${editedColumn.key}"]`]: { overflow: 'visible' } } }; } return styles; } export function showFocus() { document.body.classList.remove('ms-Fabric--isFocusHidden'); document.body.classList.add('ms-Fabric--isFocusVisible'); } export function hideFocus() { document.body.classList.add('ms-Fabric--isFocusHidden'); document.body.classList.remove('ms-Fabric--isFocusVisible'); } /** * Gets ComboBox input. * * @param ref * @returns {HTMLInputElement | undefined } */ export function getComboBoxInput(ref) { return ref?.current?.querySelector('input'); } //# sourceMappingURL=UITable-helper.js.map