@sap-ux/ui-components
Version:
SAP UI Components Library
280 lines • 14.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UIFlexibleTable = UIFlexibleTable;
const react_1 = __importStar(require("react"));
const react_movable_1 = require("react-movable");
const __1 = require("..");
const UIFlexibleTableRow_1 = require("./UIFlexibleTableRow");
const UIFlexibleTableRowNoData_1 = require("./UIFlexibleTableRowNoData");
const types_1 = require("./types");
require("./UIFlexibleTable.scss");
const utils_1 = require("./utils");
const RowActions_1 = require("./RowActions");
const RowData_1 = require("./RowData");
/**
* UIFlexibleTable component.
*
* @exports
* @param {UIFlexibleTableProps<T>} props
* @returns {React.ReactElement}
*/
function UIFlexibleTable(props) {
const [currentFocusedRowIndex, setCurrentFocusedRowIndex] = (0, react_1.useState)();
const [currentFocusedRowAction, setCurrentFocusedRowAction] = (0, react_1.useState)('');
const [isInRowLayout, setIsInRowLayout] = (0, react_1.useState)(props.layout === types_1.UIFlexibleTableLayout.InlineFlex);
const [titleRowRightPadding, setTitleRowRightPadding] = (0, react_1.useState)(0);
const [rowToNavigate, setRowToNavigate] = (0, react_1.useState)();
const scrollableElement = (0, react_1.useRef)(null);
const tableRootElementRef = (0, react_1.useRef)(null);
const contentElementRef = (0, react_1.useRef)(null);
const tableBodyElementRef = (0, react_1.useRef)(null);
const scrollTargetRef = (0, react_1.useRef)(null);
const onResize = (element) => {
setIsInRowLayout(props.layout === types_1.UIFlexibleTableLayout.InlineFlex &&
isRowFitsContainer(props.inRowLayoutMinWidth, tableRootElementRef));
const rootContainer = tableRootElementRef.current;
if (element && props.onContentSizeChange && rootContainer) {
props.onContentSizeChange({ height: rootContainer.clientHeight, width: rootContainer.clientWidth });
}
};
const onScrollBarStateChange = () => {
const content = contentElementRef
.current;
const scrollableContent = scrollableElement.current;
const scrollBarSize = content && scrollableContent ? content.clientWidth - scrollableContent.clientWidth : 0;
setTitleRowRightPadding(scrollBarSize);
};
const [resizeObserver] = (0, react_1.useState)(new ResizeObserver(onResize));
const scrollBarObserver = (0, react_1.useRef)(new ResizeObserver(onScrollBarStateChange));
(0, react_1.useEffect)(() => {
if (tableRootElementRef.current) {
resizeObserver.observe(tableRootElementRef.current);
onResize();
}
}, [tableRootElementRef.current]);
(0, react_1.useEffect)(() => {
if (currentFocusedRowIndex !== undefined) {
restoreFocus(currentFocusedRowAction, currentFocusedRowIndex, props.id);
}
if (!props.isContentLoading && scrollTargetRef.current) {
scrollTargetRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
setRowToNavigate(undefined);
}
}, [props.isContentLoading, props.rows]);
(0, react_1.useEffect)(() => {
return () => {
resizeObserver.disconnect();
};
}, []);
(0, react_1.useEffect)(() => {
onResize();
}, [props.layout, props.maxWidth]);
const reorderTable = (oldIndex, newIndex) => {
if (props.onTableReorder) {
const result = props.onTableReorder({ oldIndex, newIndex });
if (typeof result === 'object' && result.isDropDisabled) {
return;
}
setCurrentFocusedRowIndex(newIndex);
}
};
const addNewRow = () => {
if (props.addRowButton?.onClick) {
const result = props.addRowButton.onClick();
if (result instanceof Promise) {
result.then((data) => setRowToNavigate(data?.scrollToRow)).catch(() => undefined);
}
else {
setRowToNavigate(result?.scrollToRow);
}
}
};
const onFocusRowAction = (rowIndex, actionName = '') => {
setCurrentFocusedRowIndex(rowIndex);
setCurrentFocusedRowAction(actionName);
};
const renderRowActions = (rowIndex) => {
return (react_1.default.createElement(RowActions_1.RowActions, { rowIndex: rowIndex, tableProps: props, onFocusRowAction: (actionName) => onFocusRowAction(rowIndex, actionName), onMoveDownClick: () => reorderTable(rowIndex, rowIndex + 1), onMoveUpClick: () => reorderTable(rowIndex, rowIndex - 1) }));
};
const renderRowData = (params) => (react_1.default.createElement(RowData_1.RowDataCells, { key: params.index ?? 0, isInRowLayout: isInRowLayout, row: params.value, rowIndex: params.index ?? 0, tableProps: props }));
/**
* Renders row.
*
* @param {NodeDragAndDropSortingParams} params
* @returns {React.ReactElement<UIFlexibleTableRowProps<T>, 'UIFlexibleTableRow'>}
*/
function renderRow(params) {
const rowIndex = params.index;
const { isDropWarning } = props.onRenderRowContainer
? props.onRenderRowContainer({
readonly: !!props.readonly,
rowIndex,
isDragged: params.isDragged
})
: { isDropWarning: false };
return (react_1.default.createElement(UIFlexibleTableRow_1.UIFlexibleTableRow, { key: `row-${rowIndex}`, dragAndDropParams: params, rowActions: renderRowActions(rowIndex ?? 0), rowData: renderRowData(params), tableProps: props, rowRef: typeof rowToNavigate === 'number' && rowIndex === rowToNavigate ? scrollTargetRef : undefined, className: isDropWarning ? 'highlight-drop-warning' : '' }));
}
const renderTable = (params) => {
const ulElement = params.props.ref?.current;
let { children } = params;
if (ulElement && scrollableElement.current !== ulElement) {
scrollableElement.current = ulElement;
scrollBarObserver.current.observe(ulElement);
}
if (props.rows.length === 0 && props.noDataText) {
children = (react_1.default.createElement(UIFlexibleTableRowNoData_1.UIFlexibleTableRowNoData, { noRowBackground: props.noRowBackground, reverseBackground: props.reverseBackground }, props.noDataText));
}
const getCursorStyle = () => {
let cursorIcon = 'default';
if (props.onTableReorder) {
cursorIcon = params.isDragged ? 'grabbing' : 'grab';
}
return cursorIcon;
};
return (react_1.default.createElement("ul", { ref: params.props.ref, className: `flexible-table-content-table${params.isDragged ? ' dragged' : ''}`, style: {
cursor: getCursorStyle(),
maxHeight: props.maxScrollableContentHeight ? `${props.maxScrollableContentHeight}px` : undefined
} }, children));
};
const tableBody = getTableBody(props, renderTable, renderRow, reorderTable, tableBodyElementRef);
const tableClasses = (0, utils_1.composeClassNames)('flexible-table', [
props.layout === types_1.UIFlexibleTableLayout.InlineFlex ? 'inline-layout' : 'wrapping-layout',
props.readonly ? 'readonly' : ''
]);
const showTitleRow = props.showColumnTitles && isInRowLayout && !props.showColumnTitlesInCells;
const tableRootElementStyle = {
maxWidth: props.maxWidth ? `${props.maxWidth}px` : '100%'
};
const getCustomTableActions = (actionsGenerator) => {
if (actionsGenerator) {
const customActions = actionsGenerator({ readonly: !!props.readonly });
return customActions.map((actionElement) => (react_1.default.createElement(react_1.default.Fragment, { key: `table-action-${actionElement.key}` },
react_1.default.createElement("div", { className: "flexible-table-header-action" }, actionElement))));
}
return [];
};
const primaryTableActions = getCustomTableActions(props.onRenderPrimaryTableActions);
const secondaryTableActions = getCustomTableActions(props.onRenderSecondaryTableActions);
const isEmptyHeader = !props.addRowButton && primaryTableActions.length + secondaryTableActions.length === 0;
const contentClasses = (0, utils_1.composeClassNames)('flexible-table-content', [
props.rows.length === 0 && !props.noDataText ? 'empty-table' : '',
props.isContentLoading ? 'loading' : '',
isEmptyHeader ? 'empty-table-header' : ''
]);
return (react_1.default.createElement("div", { className: tableClasses, ref: tableRootElementRef, id: props.id, style: tableRootElementStyle, onBlur: () => {
onFocusRowAction();
} },
isEmptyHeader ? (react_1.default.createElement(react_1.default.Fragment, null)) : (react_1.default.createElement("div", { className: "flexible-table-header" },
react_1.default.createElement("div", { className: "flexible-table-header-primary-actions" },
props.addRowButton && (react_1.default.createElement("div", { className: "flexible-table-header-action" },
react_1.default.createElement(__1.UIDefaultButton, { iconProps: { iconName: 'Add' }, className: "flexible-table-btn-add", id: (0, utils_1.getTableActionButtonId)(props.id, 'add-row'), primary: true, disabled: props.isAddItemDisabled || props.isContentLoading || props.readonly, onClick: addNewRow, title: props.addRowButton.title, "aria-label": props.addRowButton.ariaLabel ?? props.addRowButton.label }, props.addRowButton.label))),
primaryTableActions),
react_1.default.createElement("div", { className: "flexible-table-header-secondary-actions" }, secondaryTableActions))),
react_1.default.createElement("div", { className: contentClasses, ref: contentElementRef },
showTitleRow && (0, UIFlexibleTableRow_1.renderTitleRow)(props, titleRowRightPadding),
tableBody,
props.isContentLoading && (react_1.default.createElement(__1.UILoader, { className: 'uiLoaderXLarge flexible-table-overlay-loader', blockDOM: true })))));
}
/**
* Gets table body.
*
* @param {UIFlexibleTableProps<T>} props
* @param {(params: { children: React.ReactNode; isDragged: boolean; props: { ref?: React.RefObject<any>; }; }) => React.ReactNode} renderTable
* @param {( params: NodeDragAndDropSortingParams) => React.ReactElement<UIFlexibleTableRowProps<T>, 'UIFlexibleTableRow'>} renderRow
* @param { (oldIndex: number, newIndex: number) => void} reorderTable
* @param {React.MutableRefObject<any>} tableBodyElementRef
* @returns {React.ReactNode}
*/
function getTableBody(props, renderTable, renderRow, reorderTable, tableBodyElementRef) {
let tableBody;
const { rows } = props.onBeforeTableRender ? props.onBeforeTableRender({ rows: props.rows }) : { rows: props.rows };
if (rows.length > 0 && props.onTableReorder && !props.readonly) {
tableBody = (react_1.default.createElement(react_movable_1.List, { values: rows, lockVertically: props.lockVertically, onChange: (params) => reorderTable(params.oldIndex, params.newIndex), renderList: (params) => renderTable(params), renderItem: (params) => renderRow(params), removableByMove: true, beforeDrag: props.onStartDragging }));
}
else {
const children = props.rows.map((row, index) => {
const rowProps = {
key: index
};
const dragAndDropParams = {
index,
isDragged: false,
isOutOfBounds: false,
isSelected: false,
props: rowProps,
value: row
};
return renderRow(dragAndDropParams);
});
tableBody = renderTable({ children, isDragged: false, props: { ...props, ref: tableBodyElementRef } });
}
return tableBody;
}
/**
* Restores focus.
*
* @param {string} currentFocusedRowAction
* @param {number | undefined} currentFocusedRowIndex
* @param {string} tableId
*/
function restoreFocus(currentFocusedRowAction, currentFocusedRowIndex, tableId) {
if (currentFocusedRowAction) {
let actionElement = document.getElementById((0, utils_1.getRowActionButtonId)(tableId, currentFocusedRowIndex, currentFocusedRowAction));
if (actionElement) {
if (!actionElement.hasAttribute('disabled')) {
actionElement.focus();
}
else {
if (currentFocusedRowAction === 'up') {
actionElement = document.getElementById((0, utils_1.getRowActionButtonId)(tableId, currentFocusedRowIndex, 'down'));
}
else {
actionElement = document.getElementById((0, utils_1.getRowActionButtonId)(tableId, currentFocusedRowIndex, 'up'));
}
if (actionElement) {
actionElement.focus();
}
}
}
}
}
/**
* Check row fit container.
*
* @param {number | undefined} propThreshold
* @param {React.MutableRefObject<HTMLDivElement | null>} containerRef
* @returns {boolean}
*/
function isRowFitsContainer(propThreshold, containerRef) {
let result = true;
const inRowMinWidth = propThreshold ?? 600;
const containerWidth = containerRef?.current?.clientWidth ?? 0;
if (containerWidth) {
result = containerWidth >= inRowMinWidth;
}
return result;
}
//# sourceMappingURL=UIFlexibleTable.js.map