UNPKG

terra-table

Version:

The Terra Table component provides user a way to display data in an accessible table format.

802 lines (765 loc) 35.6 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.TableConstants = exports.RowSelectionModes = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _reactIntl = require("react-intl"); var _uuid = require("uuid"); var KeyCode = _interopRequireWildcard(require("keycode-js")); var _bind = _interopRequireDefault(require("classnames/bind")); var _resizeObserverPolyfill = _interopRequireDefault(require("resize-observer-polyfill")); var _terraThemeContext = _interopRequireDefault(require("terra-theme-context")); var _terraVisuallyHiddenText = _interopRequireDefault(require("terra-visually-hidden-text")); var _Section = _interopRequireDefault(require("./subcomponents/Section")); var _ColumnHeader = _interopRequireDefault(require("./subcomponents/ColumnHeader")); var _ColumnContext = _interopRequireDefault(require("./utils/ColumnContext")); var _columnShape = _interopRequireDefault(require("./proptypes/columnShape")); var _GridContext = _interopRequireWildcard(require("./utils/GridContext")); var _rowShape = _interopRequireDefault(require("./proptypes/rowShape")); var _validators = require("./proptypes/validators"); var _TableModule = _interopRequireDefault(require("./Table.module.scss")); var _sectionShape = _interopRequireDefault(require("./proptypes/sectionShape")); var _focusManagement = _interopRequireDefault(require("./utils/focusManagement")); var _actionsUtils = _interopRequireDefault(require("./utils/actionsUtils")); var _tableUtils = _interopRequireDefault(require("./utils/tableUtils")); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } var cx = _bind.default.bind(_TableModule.default); var RowSelectionModes = exports.RowSelectionModes = { SINGLE: 'single', MULTIPLE: 'multiple' }; var TableConstants = exports.TableConstants = { ROW_SELECTION_COLUMN_WIDTH: 40, TABLE_MARGIN_RIGHT: 15 }; var ROW_SELECTION_COLUMN_ID = 'table-rowSelectionColumn'; var propTypes = { /** * An identifier to uniquely identify the table. */ id: _propTypes.default.string.isRequired, /** * The information for content in the body of the table when sections do not exist. Rows are rendered in the order given. * The sections property has precedence over this property. */ rows: _propTypes.default.arrayOf(_rowShape.default), /** * The information for content in the body of the table. Sections are rendered in the order given. */ sections: _propTypes.default.arrayOf(_sectionShape.default), /** * A string that identifies the element (or elements) that labels the table. */ ariaLabelledBy: _propTypes.default.string, /** * A string that labels the table for accessibility. If the ariaLabelledBy property is specified, the ariaLabel property is not used. */ ariaLabel: _propTypes.default.string, /** * @private * Column index for cell that can receive tab focus. */ activeColumnIndex: _propTypes.default.number, /** * @private * Row index for cell that can receive tab focus. */ focusedRowIndex: _propTypes.default.number, /** * CallBack to trigger re-focusing when focused row or col didn't change, but focus update is needed */ triggerFocus: _propTypes.default.func, /** * @private * Specifies if resize handle should be active. */ isActiveColumnResizing: _propTypes.default.bool, /** * A numeric increment in pixels to adjust column width when resizing with the keyboard. */ columnResizeIncrement: _propTypes.default.number, /** * The information for pinned columns. Pinned columns are the stickied leftmost columns of the table. * Columns are presented in the order given. */ pinnedColumns: _propTypes.default.arrayOf(_columnShape.default), /** * The information for overflow columns. Overflow columns are rendered in the table's horizontal overflow. * Columns are presented in the order given. */ overflowColumns: _propTypes.default.arrayOf(_columnShape.default), /** * A number indicating the default column width in pixels. This value is used if no overriding width value is provided on a per-column basis. * This value is ignored if the isAutoLayout property is set to true. */ defaultColumnWidth: _propTypes.default.number, /** * A string that specifies the column height. Any valid CSS height value is accepted. */ columnHeaderHeight: _propTypes.default.string, /** * A string that specifies the height for the rows on the table. Any valid CSS value is accepted. */ rowHeight: _propTypes.default.string, /** * A string that specifies the Minimum height for the rows on the table. rowHeight takes precedence if valid CSS value is passed. * With this property the height of the cell will grow to fit the cell content. */ rowMinimumHeight: _propTypes.default.string, /** * A number indicating the index of the column that represents the row header. The index is based on 0 and cannot exceed one less than the number of columns on the table. * Index can be set to -1 if row headers are not required. */ rowHeaderIndex: _validators.validateRowHeaderIndex, /** * A function that is called when a resizable column is resized. Parameters: * @param {string} columnId columnId * @param {string} requestedWidth requestedWidth */ onColumnResize: _propTypes.default.func, /** * A callback function that is called when a selectable cell is selected. Parameters: * @private * @param {string} rowId rowId * @param {string} columnId columnId * @param {object} event event */ onCellSelect: _propTypes.default.func, /** * A callback function that is called when one or more rows are selected or cleared. Parameters: * @param {string} rowId row id of the selected row */ onRowSelect: _propTypes.default.func, /** * A callback function that is called when a selectable column is selected. Parameters: * @param {string} columnId columnId */ onColumnSelect: _propTypes.default.func, /** * A function that is called when a collapsible section is selected. Parameters: `onSectionSelect(sectionId)` */ onSectionSelect: _propTypes.default.func, /* * A callback function that is called when the row selection column header is selected. Parameters: * @param {string} columnId columnId */ onRowSelectionHeaderSelect: _propTypes.default.func, /** * A mode that enables row selection capabilities for the table. * Use 'single' for single row selection and 'multiple' for multi-row selection. */ rowSelectionMode: _propTypes.default.oneOf(Object.values(RowSelectionModes)), /** * A Boolean value indicating whether the table columns are displayed. Setting the value to **false** hides the columns, * but a screen reader still reads the column header values for accessibility. */ hasVisibleColumnHeaders: _propTypes.default.bool, /** * A Boolean value specifying whether the table has zebra striping for rows. */ isStriped: _propTypes.default.bool, /** * A Boolean value specifying whether the auto table layout is used to render the table. */ isAutoLayout: _propTypes.default.bool, /** * @private * The intl object containing translations. This is retrieved from the context automatically by injectIntl. */ intl: _propTypes.default.shape({ formatMessage: _propTypes.default.func }).isRequired }; var defaultProps = { rowHeaderIndex: 0, defaultColumnWidth: 200, columnHeaderHeight: '2.5rem', rowMinimumHeight: 'auto', pinnedColumns: [], overflowColumns: [], rows: [], hasVisibleColumnHeaders: true }; var defaultColumnMinimumWidth = 60; var defaultColumnMaximumWidth = 300; function Table(props) { var id = props.id, ariaLabelledBy = props.ariaLabelledBy, ariaLabel = props.ariaLabel, activeColumnIndex = props.activeColumnIndex, focusedRowIndex = props.focusedRowIndex, triggerFocus = props.triggerFocus, isActiveColumnResizing = props.isActiveColumnResizing, columnResizeIncrement = props.columnResizeIncrement, rows = props.rows, sections = props.sections, pinnedColumns = props.pinnedColumns, overflowColumns = props.overflowColumns, onColumnResize = props.onColumnResize, defaultColumnWidth = props.defaultColumnWidth, columnHeaderHeight = props.columnHeaderHeight, rowHeight = props.rowHeight, rowSelectionMode = props.rowSelectionMode, onColumnSelect = props.onColumnSelect, onCellSelect = props.onCellSelect, onSectionSelect = props.onSectionSelect, onRowSelect = props.onRowSelect, onRowSelectionHeaderSelect = props.onRowSelectionHeaderSelect, hasVisibleColumnHeaders = props.hasVisibleColumnHeaders, isStriped = props.isStriped, isAutoLayout = props.isAutoLayout, rowHeaderIndex = props.rowHeaderIndex, intl = props.intl, rowMinimumHeight = props.rowMinimumHeight; // Manage column resize var _useState = (0, _react.useState)(0), _useState2 = (0, _slicedToArray2.default)(_useState, 2), tableHeight = _useState2[0], setTableHeight = _useState2[1]; var _useState3 = (0, _react.useState)(null), _useState4 = (0, _slicedToArray2.default)(_useState3, 2), activeIndex = _useState4[0], setActiveIndex = _useState4[1]; // Set an initial width so that the component intially renders sticky behavior correctly. This gets modified later through screen resize events var _useState5 = (0, _react.useState)(1000), _useState6 = (0, _slicedToArray2.default)(_useState5, 2), boundedWidth = _useState6[0], setBoundedWidth = _useState6[1]; var activeColumnPageX = (0, _react.useRef)(0); var activeColumnWidth = (0, _react.useRef)(200); var tableWidth = (0, _react.useRef)(0); var resizingDelayTimer = (0, _react.useRef)(null); var screenResizeTimer = (0, _react.useRef)(null); var resizeTimer = 100; var _useState7 = (0, _react.useState)([0]), _useState8 = (0, _slicedToArray2.default)(_useState7, 2), pinnedColumnOffsets = _useState8[0], setPinnedColumnOffsets = _useState8[1]; var _useState9 = (0, _react.useState)([0]), _useState10 = (0, _slicedToArray2.default)(_useState9, 2), pinnedColumnHeaderOffsets = _useState10[0], setPinnedColumnHeaderOffsets = _useState10[1]; var tableContainerRef = (0, _react.useRef)(); var tableRef = (0, _react.useRef)(); var _useState11 = (0, _react.useState)(false), _useState12 = (0, _slicedToArray2.default)(_useState11, 2), isTableScrollable = _useState12[0], setTableScrollable = _useState12[1]; var theme = (0, _react.useContext)(_terraThemeContext.default); var gridContext = (0, _react.useContext)(_GridContext.default); var isGridContext = gridContext.role === _GridContext.GridConstants.GRID; var rowSelectionEffectTriggered = (0, _react.useRef)(false); var selectedRows = (0, _react.useRef)([]); var _useState13 = (0, _react.useState)(null), _useState14 = (0, _slicedToArray2.default)(_useState13, 2), rowSelectionAriaLiveMessage = _useState14[0], setRowSelectionAriaLiveMessage = _useState14[1]; var _useState15 = (0, _react.useState)(null), _useState16 = (0, _slicedToArray2.default)(_useState15, 2), rowSelectionModeAriaLiveMessage = _useState16[0], setRowSelectionModeAriaLiveMessage = _useState16[1]; // Aria live region message management var _useState17 = (0, _react.useState)(null), _useState18 = (0, _slicedToArray2.default)(_useState17, 2), columnHeaderAriaLiveMessage = _useState18[0], setColumnHeaderAriaLiveMessage = _useState18[1]; var columnContextValue = (0, _react.useMemo)(function () { return { pinnedColumnOffsets: pinnedColumnOffsets, pinnedColumnHeaderOffsets: pinnedColumnHeaderOffsets, setColumnHeaderAriaLiveMessage: setColumnHeaderAriaLiveMessage }; }, [pinnedColumnOffsets, pinnedColumnHeaderOffsets]); // Initialize column width properties var initializeColumn = function initializeColumn(column) { var columnWidth = column.width || (!isAutoLayout ? defaultColumnWidth : undefined); return _objectSpread(_objectSpread({}, column), {}, { width: columnWidth, // Default column width should only apply to a fixed table layout minimumWidth: column.minimumWidth || defaultColumnMinimumWidth, maximumWidth: column.maximumWidth || defaultColumnMaximumWidth, isResizable: column.isResizable && typeof columnWidth === 'number' && !isAutoLayout }); }; var hasSelectableRows = rowSelectionMode === RowSelectionModes.MULTIPLE; var displayedColumns = (0, _react.useMemo)(function () { // Create row selection column object var tableRowSelectionColumn = { id: ROW_SELECTION_COLUMN_ID, width: TableConstants.ROW_SELECTION_COLUMN_WIDTH, displayName: intl.formatMessage({ id: 'Terra.table.row-selection-header-display' }), isDisplayVisible: false, isSelectable: !!onRowSelectionHeaderSelect, isResizable: false }; return (hasSelectableRows ? [tableRowSelectionColumn] : []).concat(pinnedColumns).concat(overflowColumns); }, [hasSelectableRows, intl, onRowSelectionHeaderSelect, overflowColumns, pinnedColumns]); var tableBodyColumns = (0, _react.useMemo)(function () { var columnData = displayedColumns.reduce(function (columns, currentColumn, columnHeaderIndex) { for (var columnSpanIndex = 0; columnSpanIndex < (currentColumn.columnSpan || 1); columnSpanIndex += 1) { columns.push(_objectSpread(_objectSpread({}, currentColumn), {}, { columnSpanIndex: columnSpanIndex, columnHeaderIndex: columnHeaderIndex })); } return columns; }, []); if (gridContext.tableBodyColumnsRef) { gridContext.tableBodyColumnsRef.current = columnData; } return columnData; }, [displayedColumns, gridContext.tableBodyColumnsRef]); var _useState19 = (0, _react.useState)(displayedColumns.map(function (column) { return initializeColumn(column); })), _useState20 = (0, _slicedToArray2.default)(_useState19, 2), tableHeaderColumns = _useState20[0], setTableHeaderColumns = _useState20[1]; var defaultSectionRef = (0, _react.useRef)((0, _uuid.v4)()); // Create section array from props var tableSections = (0, _react.useMemo)(function () { if (sections) { return (0, _toConsumableArray2.default)(sections); } return [{ id: defaultSectionRef.current, rows: rows }]; }, [rows, sections]); // check if at least one column has an action prop // same check is done in DataGrid, but as Table can be a stand-alone component, it can't relay on passed prop. var hasColumnHeaderActions = (0, _actionsUtils.default)(pinnedColumns) || (0, _actionsUtils.default)(overflowColumns); // eslint-disable-next-line no-nested-ternary var headerRowCount = hasVisibleColumnHeaders ? hasColumnHeaderActions ? 2 : 1 : 0; // Calculate total table row count var subSectionReducer = function subSectionReducer(rowCount, currentSubsection) { // eslint-disable-next-line no-param-reassign currentSubsection.subSectionRowIndex = rowCount + 1; return rowCount + currentSubsection.rows.length + 1; }; var tableSectionReducer = function tableSectionReducer(rowCount, currentSection) { if (currentSection.id !== defaultSectionRef.current) { // eslint-disable-next-line no-param-reassign currentSection.sectionRowIndex = rowCount + 1; if (currentSection.subsections) { return currentSection.subsections.reduce(subSectionReducer, rowCount + 1); } return rowCount + currentSection.rows.length + 1; } // eslint-disable-next-line no-param-reassign currentSection.sectionRowIndex = rowCount; return rowCount + currentSection.rows.length; }; var tableRowCount = tableSections.reduce(tableSectionReducer, headerRowCount); // ------------------------------------- // functions var handleCellSelection = (0, _react.useCallback)(function (selectionDetails, event) { if (!isGridContext && onRowSelect) { onRowSelect({ sectionId: selectionDetails.sectionId, rowId: selectionDetails.rowId }); return; } if (onCellSelect) { onCellSelect(selectionDetails, event); } }, [isGridContext, onCellSelect, onRowSelect]); // ------------------------------------- // useEffect Hooks (0, _react.useEffect)(function () { if (!rowSelectionEffectTriggered.current) { rowSelectionEffectTriggered.current = true; return; } // Since the row selection mode has changed, the row selection mode needs to be updated. setRowSelectionModeAriaLiveMessage(intl.formatMessage({ id: rowSelectionMode === RowSelectionModes.MULTIPLE ? 'Terra.table.row-selection-mode-enabled' : 'Terra.table.row-selection-mode-disabled' })); setTableHeaderColumns(displayedColumns.map(function (column) { return initializeColumn(column); })); // eslint-disable-next-line react-hooks/exhaustive-deps }, [rowSelectionMode]); // useEffect for row updates (0, _react.useEffect)(function () { var previousSelectedRows = (0, _toConsumableArray2.default)(selectedRows.current); var selectableRows = tableSections.flatMap(function (section) { if (section.subsections) { return section.subsections.flatMap(function (subsection) { return subsection.rows.map(function (row) { return row; }); }); } return section.rows.map(function (row) { return row; }); }); selectedRows.current = selectableRows.filter(function (row) { return row.isSelected; }).map(function (row) { return row.id; }); if (previousSelectedRows.length > 0 && selectedRows.current.length === 0) { setRowSelectionAriaLiveMessage(intl.formatMessage({ id: 'Terra.table.all-rows-unselected' })); } else if (selectedRows.current.length === selectableRows.length) { setRowSelectionAriaLiveMessage(intl.formatMessage({ id: 'Terra.table.all-rows-selected' })); } else { var rowSelectionsAdded = selectedRows.current.filter(function (row) { return !previousSelectedRows.includes(row); }); var rowSelectionsRemoved = previousSelectedRows.filter(function (row) { return !selectedRows.current.includes(row); }); var selectionUpdateAriaMessage = ''; if (rowSelectionsAdded.length === 1) { var selectedRowElement = tableRef.current.querySelector("tr[data-row-id='".concat(rowSelectionsAdded[0], "']")); if (selectedRowElement) { var selectedRowLabel = selectedRowElement.getAttribute('aria-rowindex'); selectionUpdateAriaMessage = intl.formatMessage({ id: 'Terra.table.row-selection-template' }, { row: selectedRowLabel }); } } else if (rowSelectionsAdded.length > 1) { selectionUpdateAriaMessage = intl.formatMessage({ id: 'Terra.table.multiple-rows-selected' }, { rowCount: rowSelectionsAdded.length }); } if (rowSelectionsRemoved.length === 1) { var unselectedRowElement = tableRef.current.querySelector("tr[data-row-id='".concat(rowSelectionsRemoved[0], "']")); if (unselectedRowElement) { var unselectedRowLabel = unselectedRowElement.getAttribute('aria-rowindex'); selectionUpdateAriaMessage += intl.formatMessage({ id: 'Terra.table.row-selection-cleared-template' }, { row: unselectedRowLabel }); } } else if (rowSelectionsRemoved.length > 1) { selectionUpdateAriaMessage += intl.formatMessage({ id: 'Terra.table.multiple-rows-unselected' }, { rowCount: rowSelectionsRemoved.length }); } if (selectionUpdateAriaMessage) { setRowSelectionAriaLiveMessage(selectionUpdateAriaMessage); } } }, [intl, tableSections]); // useEffect for row displayed columns (0, _react.useEffect)(function () { setTableHeaderColumns(displayedColumns.map(function (column) { return initializeColumn(column); })); // eslint-disable-next-line react-hooks/exhaustive-deps }, [pinnedColumns, overflowColumns]); // useEffect to calculate pinned column offsets (0, _react.useEffect)(function () { var headerOffsetArray = []; var cellOffsetArray = []; var cumulativeHeaderOffset = 0; var cumulativeCellOffset = 0; var lastPinnedColumnIndex; // if table has selectable rows but no pinned columns, then set the offset of the first column to 0 if (hasSelectableRows && pinnedColumns.length === 0) { lastPinnedColumnIndex = 0; headerOffsetArray.push(cumulativeHeaderOffset); setPinnedColumnOffsets(headerOffsetArray); setPinnedColumnHeaderOffsets(headerOffsetArray); return; } if (pinnedColumns.length > 0) { lastPinnedColumnIndex = hasSelectableRows ? pinnedColumns.length : pinnedColumns.length - 1; tableHeaderColumns.slice(0, lastPinnedColumnIndex + 1).forEach(function (pinnedColumn) { headerOffsetArray.push(cumulativeHeaderOffset); var currentColumnSpan = pinnedColumn.columnSpan || 1; for (var columnSpanIndex = 0; columnSpanIndex < currentColumnSpan; columnSpanIndex += 1) { cellOffsetArray.push(cumulativeCellOffset); cumulativeCellOffset += pinnedColumn.width / currentColumnSpan; } cumulativeHeaderOffset += pinnedColumn.width; }); } setPinnedColumnHeaderOffsets(headerOffsetArray); setPinnedColumnOffsets(cellOffsetArray); // eslint-disable-next-line react-hooks/exhaustive-deps }, [tableHeaderColumns]); (0, _react.useEffect)(function () { var resizeObserver = new _resizeObserverPolyfill.default(function () { clearTimeout(resizingDelayTimer.current); resizingDelayTimer.current = setTimeout(function () { if (tableRef.current) { var heightOffset = hasColumnHeaderActions ? 2 : 1; // needs 2 pixels if actions row exists in headers to avoid scroll setTableHeight(tableRef.current.offsetHeight - heightOffset); var tableContainer = tableContainerRef.current; setTableScrollable(tableContainer.scrollWidth > tableContainer.clientWidth || tableContainer.scrollHeight > tableContainer.clientHeight); } }, resizeTimer); }); resizeObserver.observe(tableRef.current); return function () { resizeObserver.disconnect(); }; }, [hasColumnHeaderActions, tableRef]); (0, _react.useEffect)(function () { // Resize handler to control the width of the sticky headers. This value needs to be responsive to window resizing. var resizeObserver = new _resizeObserverPolyfill.default(function () { clearTimeout(screenResizeTimer.current); screenResizeTimer.current = setTimeout(function () { var _tableContainerRef$cu, _tableRef$current; var containerWidth = tableContainerRef === null || tableContainerRef === void 0 || (_tableContainerRef$cu = tableContainerRef.current) === null || _tableContainerRef$cu === void 0 ? void 0 : _tableContainerRef$cu.clientWidth; // An offset is necessary in order to prevent the fixed width from being too large and bleeding outside the container. setBoundedWidth(Math.min(containerWidth || 0, (tableRef === null || tableRef === void 0 || (_tableRef$current = tableRef.current) === null || _tableRef$current === void 0 ? void 0 : _tableRef$current.clientWidth) || 0) - 25); }, resizeTimer); }); resizeObserver.observe(tableContainerRef.current); return function () { resizeObserver.disconnect(); }; }, [tableContainerRef, tableRef]); // ------------------------------------- var handleTableRef = (0, _react.useCallback)(function (node) { if (gridContext.tableRef) { gridContext.tableRef.current = node; } tableRef.current = node; }, [gridContext.tableRef]); var handleContainerRef = (0, _react.useCallback)(function (node) { if (gridContext.tableContainerRef) { gridContext.tableContainerRef.current = node; } tableContainerRef.current = node; }, [gridContext.tableContainerRef]); // ------------------------------------- // event handlers var handleColumnSelect = (0, _react.useCallback)(function (columnId) { if (columnId === ROW_SELECTION_COLUMN_ID) { if (onRowSelectionHeaderSelect) { onRowSelectionHeaderSelect(); } } else if (onColumnSelect) { onColumnSelect(columnId); } }, [onColumnSelect, onRowSelectionHeaderSelect]); var onResizeMouseDown = (0, _react.useCallback)(function (event, index, resizeColumnWidth) { // Store current table and column values for resize calculations tableWidth.current = tableRef.current.offsetWidth; activeColumnPageX.current = event.pageX; activeColumnWidth.current = resizeColumnWidth; // Set the active index to the selected column setActiveIndex(index); }, []); var onMouseMove = function onMouseMove(event) { if (activeIndex == null) { return; } // Ensure the new column width falls within the range of the minimum and maximum values var diffX = event.pageX - activeColumnPageX.current; var _tableHeaderColumns$a = tableHeaderColumns[activeIndex], minimumWidth = _tableHeaderColumns$a.minimumWidth, maximumWidth = _tableHeaderColumns$a.maximumWidth; var newColumnWidth = Math.min(Math.max(activeColumnWidth.current + diffX, minimumWidth), maximumWidth); // Update the width for the column in the state variable var newColumns = (0, _toConsumableArray2.default)(tableHeaderColumns); newColumns[activeIndex].width = newColumnWidth; setTableHeaderColumns(newColumns); // Update the column and table width tableRef.current.style.width = "".concat(tableWidth + (newColumnWidth - activeColumnWidth.current), "px"); }; var onMouseUp = function onMouseUp() { if (onColumnResize) { onColumnResize(tableHeaderColumns[activeIndex].id, tableHeaderColumns[activeIndex].width); } // Remove active index setActiveIndex(null); }; var onResizeHandleChange = (0, _react.useCallback)(function (columnIndex, increment) { var _tableHeaderColumns$c = tableHeaderColumns[columnIndex], minimumWidth = _tableHeaderColumns$c.minimumWidth, maximumWidth = _tableHeaderColumns$c.maximumWidth, width = _tableHeaderColumns$c.width; var newColumnWidth = Math.min(Math.max(width + increment, minimumWidth), maximumWidth); // Update the width for the column in the state variable var newGridColumns = (0, _toConsumableArray2.default)(tableHeaderColumns); newGridColumns[columnIndex].width = newColumnWidth; setTableHeaderColumns(newGridColumns); // Update the column and table width tableRef.current.style.width = "".concat(tableRef.current.offsetWidth + (newColumnWidth - width), "px"); // Notify consumers of the new column width if (onColumnResize) { onColumnResize(tableHeaderColumns[columnIndex].id, tableHeaderColumns[columnIndex].width); } }, [tableHeaderColumns, onColumnResize]); /** * * @param {HTMLElement} element - The element to check if it is a text input * @returns True if the element is a text input. Otherwise, false. */ var isTextInput = function isTextInput(element) { var tagName = element.tagName; if (tagName.toLowerCase() === 'input') { var validTypes = ['text', 'password', 'number', 'email', 'tel', 'url', 'search', 'date', 'datetime', 'datetime-local', 'time', 'month', 'week']; var inputType = element.type; return validTypes.indexOf(inputType) >= 0; } return false; }; var onKeyDown = function onKeyDown(event) { var targetElement = event.target; // Allow default behavior if the event target is an editable field if (event.keyCode !== KeyCode.KEY_TAB && (isTextInput(targetElement) || ['textarea', 'select'].indexOf(targetElement.tagName.toLowerCase()) >= 0 || targetElement.hasAttribute('contentEditable') && targetElement.getAttribute('contentEditable') !== false)) { return; } // Handle home and end key navigation in table var focusableTableElements; if (event.keyCode === KeyCode.KEY_HOME) { focusableTableElements = (0, _focusManagement.default)(tableRef.current); if (focusableTableElements) { focusableTableElements[0].focus(); } } else if (event.keyCode === KeyCode.KEY_END) { focusableTableElements = (0, _focusManagement.default)(tableRef.current); if (focusableTableElements) { focusableTableElements[focusableTableElements.length - 1].focus(); } } }; // Added margin to allow for resizing of last column. var hasResizableCol = tableHeaderColumns[tableHeaderColumns.length - 1].isResizable; var tableStyle = { marginRight: hasResizableCol ? "".concat(TableConstants.TABLE_MARGIN_RIGHT, "px") : '0' }; // Set first and last row Ids var firstRowId; var lastRowId; if (rows && rows.length) { firstRowId = rows[0].id; lastRowId = rows[rows.length - 1].id; } else if (sections) { var rowData = _tableUtils.default.getFirstAndLastVisibleRowData(sections); firstRowId = rowData.firstRowId; lastRowId = rowData.lastRowId; } // ------------------------------------- return /*#__PURE__*/_react.default.createElement("div", { ref: handleContainerRef, className: cx('table-container', theme.className) // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex , tabIndex: !isGridContext && isTableScrollable ? 0 : undefined }, /*#__PURE__*/_react.default.createElement("table", (0, _extends2.default)({ ref: handleTableRef, id: id, role: gridContext.role, "aria-labelledby": ariaLabelledBy, "aria-label": ariaLabel, "aria-rowcount": tableRowCount, style: tableStyle // eslint-disable-line react/forbid-dom-props , className: cx('table', { headerless: !hasVisibleColumnHeaders, 'auto-layout': isAutoLayout }), onKeyDown: !isGridContext ? onKeyDown : undefined }, activeIndex != null && { onMouseUp: onMouseUp, onMouseMove: onMouseMove, onMouseLeave: onMouseUp }), /*#__PURE__*/_react.default.createElement(_ColumnContext.default.Provider, { value: columnContextValue }, /*#__PURE__*/_react.default.createElement("colgroup", null, tableHeaderColumns.map(function (column) { var currentColumnWidth = column.width; if (typeof column.width === 'number') { currentColumnWidth = "".concat(column.width, "px"); } if (column.columnSpan) { currentColumnWidth = "calc(".concat(currentColumnWidth, " / ").concat(column.columnSpan, ")"); } // eslint-disable-next-line react/forbid-dom-props return /*#__PURE__*/_react.default.createElement("col", { span: column.columnSpan, key: column.id, style: { width: currentColumnWidth } }); })), /*#__PURE__*/_react.default.createElement(_ColumnHeader.default, { tableId: id, isActiveColumnResizing: isActiveColumnResizing, activeColumnIndex: activeColumnIndex, focusedRowIndex: focusedRowIndex, triggerFocus: triggerFocus, columns: tableHeaderColumns, hasVisibleColumnHeaders: hasVisibleColumnHeaders, headerHeight: columnHeaderHeight, columnResizeIncrement: columnResizeIncrement, tableHeight: tableHeight, onResizeMouseDown: onResizeMouseDown, onColumnSelect: handleColumnSelect, onResizeHandleChange: onResizeHandleChange, hasColumnHeaderActions: hasColumnHeaderActions }), tableSections.map(function (section) { return /*#__PURE__*/_react.default.createElement(_Section.default, { id: section.id, tableId: id, key: section.id, sectionRowIndex: section.sectionRowIndex, isCollapsible: section.isCollapsible, isCollapsed: section.isCollapsed, isHidden: section.id === defaultSectionRef.current, isTableStriped: isStriped, text: section.text, rows: section.rows, subsections: section.subsections, rowHeight: rowHeight, rowSelectionMode: rowSelectionMode, displayedColumns: tableBodyColumns, rowHeaderIndex: rowHeaderIndex, onCellSelect: isGridContext || rowSelectionMode ? handleCellSelection : undefined, onSectionSelect: onSectionSelect, rowMinimumHeight: rowMinimumHeight, boundingWidth: boundedWidth, firstRowId: firstRowId, lastRowId: lastRowId }); }))), /*#__PURE__*/_react.default.createElement(_terraVisuallyHiddenText.default, { className: cx('row-selection-mode-region'), "aria-live": "polite", text: rowSelectionModeAriaLiveMessage }), /*#__PURE__*/_react.default.createElement(_terraVisuallyHiddenText.default, { className: cx('row-selection-region'), "aria-live": "polite", text: rowSelectionAriaLiveMessage }), /*#__PURE__*/_react.default.createElement(_terraVisuallyHiddenText.default, { className: cx('column-header-region'), "aria-live": "polite", "aria-atomic": "true", text: columnHeaderAriaLiveMessage })); } Table.propTypes = propTypes; Table.defaultProps = defaultProps; var _default = exports.default = /*#__PURE__*/_react.default.memo((0, _reactIntl.injectIntl)(Table));