UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Microsoft 365.

882 lines • 52.3 kB
import { __assign, __decorate, __extends } from "tslib"; import * as React from 'react'; import { initializeComponentRef, FocusRects, Async, KeyCodes, elementContains, getRTLSafeKeyCode, classNamesFunction, memoizeFunction, getId, composeRenderFunction, composeComponentAs, } from '../../Utilities'; import { CheckboxVisibility, ColumnActionsMode, ConstrainMode, DetailsListLayoutMode, ColumnDragEndLocation, } from '../DetailsList/DetailsList.types'; import { DetailsHeader } from '../DetailsList/DetailsHeader'; import { SelectAllVisibility, } from '../DetailsList/DetailsHeader.types'; import { DetailsRow } from '../DetailsList/DetailsRow'; import { FocusZone, FocusZoneDirection } from '../../FocusZone'; import { Selection, SelectionMode, SelectionZone } from '../../utilities/selection/index'; import { DragDropHelper } from '../../utilities/dragdrop/DragDropHelper'; import { GroupedList } from '../../GroupedList'; import { List } from '../../List'; import { withViewport } from '../../utilities/decorators/withViewport'; import { GetGroupCount } from '../../utilities/groupedList/GroupedListUtility'; import { DEFAULT_CELL_STYLE_PROPS } from './DetailsRow.styles'; import { CHECK_CELL_WIDTH as CHECKBOX_WIDTH } from './DetailsRowCheck.styles'; // For every group level there is a GroupSpacer added. Importing this const to have the source value in one place. import { SPACER_WIDTH as GROUP_EXPAND_WIDTH } from '../GroupedList/GroupSpacer'; import { useConst } from '@uifabric/react-hooks'; var getClassNames = classNamesFunction(); var MIN_COLUMN_WIDTH = 100; // this is the global min width var DEFAULT_RENDERED_WINDOWS_AHEAD = 2; var DEFAULT_RENDERED_WINDOWS_BEHIND = 2; /** * Hooks-based implementation of DetailsList. * Since many existing consumers of DetailsList expect `ref` to return a `DetailsList`, * this inner component handles rendering while the outer maintains compatibility. */ var DetailsListInner = function (props) { var _a; var selection = props.selection; var ariaLabelForListHeader = props.ariaLabelForListHeader, ariaLabelForSelectAllCheckbox = props.ariaLabelForSelectAllCheckbox, ariaLabelForSelectionColumn = props.ariaLabelForSelectionColumn, className = props.className, checkboxVisibility = props.checkboxVisibility, compact = props.compact, constrainMode = props.constrainMode, dragDropEvents = props.dragDropEvents, groups = props.groups, groupProps = props.groupProps, indentWidth = props.indentWidth, items = props.items, isPlaceholderData = props.isPlaceholderData, isHeaderVisible = props.isHeaderVisible, layoutMode = props.layoutMode, onItemInvoked = props.onItemInvoked, onItemContextMenu = props.onItemContextMenu, onColumnHeaderClick = props.onColumnHeaderClick, onColumnHeaderContextMenu = props.onColumnHeaderContextMenu, _b = props.selectionMode, selectionMode = _b === void 0 ? selection.mode : _b, selectionPreservedOnEmptyClick = props.selectionPreservedOnEmptyClick, selectionZoneProps = props.selectionZoneProps, ariaLabel = props.ariaLabel, ariaLabelForGrid = props.ariaLabelForGrid, rowElementEventMap = props.rowElementEventMap, _c = props.shouldApplyApplicationRole, shouldApplyApplicationRole = _c === void 0 ? false : _c, getKey = props.getKey, listProps = props.listProps, usePageCache = props.usePageCache, onShouldVirtualize = props.onShouldVirtualize, viewport = props.viewport, minimumPixelsForDrag = props.minimumPixelsForDrag, getGroupHeight = props.getGroupHeight, styles = props.styles, theme = props.theme, _d = props.cellStyleProps, cellStyleProps = _d === void 0 ? DEFAULT_CELL_STYLE_PROPS : _d, onRenderCheckbox = props.onRenderCheckbox, useFastIcons = props.useFastIcons, dragDropHelper = props.dragDropHelper, adjustedColumns = props.adjustedColumns, isCollapsed = props.isCollapsed, isSizing = props.isSizing, isSomeGroupExpanded = props.isSomeGroupExpanded, version = props.version, rootRef = props.rootRef, listRef = props.listRef, focusZoneRef = props.focusZoneRef, columnReorderOptions = props.columnReorderOptions, groupedListRef = props.groupedListRef, headerRef = props.headerRef, onGroupExpandStateChanged = props.onGroupExpandStateChanged, onColumnIsSizingChanged = props.onColumnIsSizingChanged, onRowDidMount = props.onRowDidMount, onRowWillUnmount = props.onRowWillUnmount, disableSelectionZone = props.disableSelectionZone, _e = props.isSelectedOnFocus, isSelectedOnFocus = _e === void 0 ? true : _e, onColumnResized = props.onColumnResized, onColumnAutoResized = props.onColumnAutoResized, onToggleCollapse = props.onToggleCollapse, onActiveRowChanged = props.onActiveRowChanged, onBlur = props.onBlur, eventsToRegister = props.rowElementEventMap, onRenderMissingItem = props.onRenderMissingItem, onRenderItemColumn = props.onRenderItemColumn, getCellValueKey = props.getCellValueKey, getRowAriaLabel = props.getRowAriaLabel, getRowAriaDescribedBy = props.getRowAriaDescribedBy, checkButtonAriaLabel = props.checkButtonAriaLabel, checkButtonGroupAriaLabel = props.checkButtonGroupAriaLabel, checkboxCellClassName = props.checkboxCellClassName, useReducedRowRenderer = props.useReducedRowRenderer, enableUpdateAnimations = props.enableUpdateAnimations, enterModalSelectionOnTouch = props.enterModalSelectionOnTouch, onRenderDefaultRow = props.onRenderDefaultRow, selectionZoneRef = props.selectionZoneRef; var defaultRole = 'grid'; var role = props.role ? props.role : defaultRole; var rowId = getId('row'); var groupNestingDepth = getGroupNestingDepth(groups); var groupedDetailsListIndexMap = useGroupedDetailsListIndexMap(groups); var additionalListProps = React.useMemo(function () { return __assign({ renderedWindowsAhead: isSizing ? 0 : DEFAULT_RENDERED_WINDOWS_AHEAD, renderedWindowsBehind: isSizing ? 0 : DEFAULT_RENDERED_WINDOWS_BEHIND, getKey: getKey, version: version }, listProps); }, [isSizing, getKey, version, listProps]); var selectAllVisibility = SelectAllVisibility.none; // for SelectionMode.none if (selectionMode === SelectionMode.single) { selectAllVisibility = SelectAllVisibility.hidden; } if (selectionMode === SelectionMode.multiple) { // if isCollapsedGroupSelectVisible is false, disable select all when the list has all collapsed groups var isCollapsedGroupSelectVisible = groupProps && groupProps.headerProps && groupProps.headerProps.isCollapsedGroupSelectVisible; if (isCollapsedGroupSelectVisible === undefined) { isCollapsedGroupSelectVisible = true; } var isSelectAllVisible = isCollapsedGroupSelectVisible || !groups || isSomeGroupExpanded; selectAllVisibility = isSelectAllVisible ? SelectAllVisibility.visible : SelectAllVisibility.hidden; } if (checkboxVisibility === CheckboxVisibility.hidden) { selectAllVisibility = SelectAllVisibility.none; } var defaultOnRenderDetailsHeader = React.useCallback(function (detailsHeaderProps) { return React.createElement(DetailsHeader, __assign({}, detailsHeaderProps)); }, []); var defaultOnRenderDetailsFooter = React.useCallback(function () { return null; }, []); var propsOnRenderDetailsHeader = props.onRenderDetailsHeader; var onRenderDetailsHeader = React.useMemo(function () { return propsOnRenderDetailsHeader ? composeRenderFunction(propsOnRenderDetailsHeader, defaultOnRenderDetailsHeader) : defaultOnRenderDetailsHeader; }, [propsOnRenderDetailsHeader, defaultOnRenderDetailsHeader]); var propsOnRenderDetailsFooter = props.onRenderDetailsFooter; var onRenderDetailsFooter = React.useMemo(function () { return propsOnRenderDetailsFooter ? composeRenderFunction(propsOnRenderDetailsFooter, defaultOnRenderDetailsFooter) : defaultOnRenderDetailsFooter; }, [propsOnRenderDetailsFooter, defaultOnRenderDetailsFooter]); var detailsFooterProps = React.useMemo(function () { return { columns: adjustedColumns, groupNestingDepth: groupNestingDepth, selection: selection, selectionMode: selectionMode, viewport: viewport, checkboxVisibility: checkboxVisibility, indentWidth: indentWidth, cellStyleProps: cellStyleProps, }; }, [ adjustedColumns, groupNestingDepth, selection, selectionMode, viewport, checkboxVisibility, indentWidth, cellStyleProps, ]); var columnReorderOnDragEnd = columnReorderOptions && columnReorderOptions.onDragEnd; var onColumnDragEnd = React.useCallback(function (_a, event) { var dropLocation = _a.dropLocation; var finalDropLocation = ColumnDragEndLocation.outside; if (columnReorderOnDragEnd) { if (dropLocation && dropLocation !== ColumnDragEndLocation.header) { finalDropLocation = dropLocation; } else if (rootRef.current) { var clientRect = rootRef.current.getBoundingClientRect(); if (event.clientX > clientRect.left && event.clientX < clientRect.right && event.clientY > clientRect.top && event.clientY < clientRect.bottom) { finalDropLocation = ColumnDragEndLocation.surface; } } columnReorderOnDragEnd(finalDropLocation); } }, [columnReorderOnDragEnd, rootRef]); var columnReorderProps = React.useMemo(function () { if (columnReorderOptions) { return __assign(__assign({}, columnReorderOptions), { onColumnDragEnd: onColumnDragEnd }); } }, [columnReorderOptions, onColumnDragEnd]); var rowCount = (isHeaderVisible ? 1 : 0) + GetGroupCount(groups) + (items ? items.length : 0); var colCount = (selectAllVisibility !== SelectAllVisibility.none ? 1 : 0) + (adjustedColumns ? adjustedColumns.length : 0) + (groups ? 1 : 0); var classNames = React.useMemo(function () { return getClassNames(styles, { theme: theme, compact: compact, isFixed: layoutMode === DetailsListLayoutMode.fixedColumns, isHorizontalConstrained: constrainMode === ConstrainMode.horizontalConstrained, className: className, }); }, [styles, theme, compact, layoutMode, constrainMode, className]); var onRenderDetailsGroupFooter = groupProps && groupProps.onRenderFooter; var finalOnRenderDetailsGroupFooter = React.useMemo(function () { return onRenderDetailsGroupFooter ? function (groupFooterProps, defaultRender) { return onRenderDetailsGroupFooter(__assign(__assign({}, groupFooterProps), { columns: adjustedColumns, groupNestingDepth: groupNestingDepth, indentWidth: indentWidth, selection: selection, selectionMode: selectionMode, viewport: viewport, checkboxVisibility: checkboxVisibility, cellStyleProps: cellStyleProps }), defaultRender); } : undefined; }, [ onRenderDetailsGroupFooter, adjustedColumns, groupNestingDepth, indentWidth, selection, selectionMode, viewport, checkboxVisibility, cellStyleProps, ]); var onRenderDetailsGroupHeader = groupProps && groupProps.onRenderHeader; var finalOnRenderDetailsGroupHeader = React.useMemo(function () { return onRenderDetailsGroupHeader ? function (groupHeaderProps, defaultRender) { var groupIndex = groupHeaderProps.groupIndex; var groupKey = groups && groupIndex !== undefined && groups[groupIndex] !== undefined ? groups[groupIndex].key : undefined; var totalRowCount = groupKey !== undefined && groupedDetailsListIndexMap[groupKey] ? groupedDetailsListIndexMap[groupKey].totalRowCount : 0; return onRenderDetailsGroupHeader(__assign(__assign({}, groupHeaderProps), { columns: adjustedColumns, groupNestingDepth: groupNestingDepth, indentWidth: indentWidth, selection: selection, selectionMode: checkboxVisibility !== CheckboxVisibility.hidden ? selectionMode : SelectionMode.none, viewport: viewport, checkboxVisibility: checkboxVisibility, cellStyleProps: cellStyleProps, ariaColSpan: adjustedColumns.length, ariaPosInSet: undefined, ariaSetSize: undefined, ariaRowCount: undefined, ariaRowIndex: groupIndex !== undefined ? totalRowCount + (isHeaderVisible ? 1 : 0) : undefined }), defaultRender); } : function (groupHeaderProps, defaultRender) { var groupIndex = groupHeaderProps.groupIndex; var groupKey = groups && groupIndex !== undefined && groups[groupIndex] !== undefined ? groups[groupIndex].key : undefined; var totalRowCount = groupKey !== undefined && groupedDetailsListIndexMap[groupKey] ? groupedDetailsListIndexMap[groupKey].totalRowCount : 0; return defaultRender(__assign(__assign({}, groupHeaderProps), { ariaColSpan: adjustedColumns.length, ariaPosInSet: undefined, ariaSetSize: undefined, ariaRowCount: undefined, ariaRowIndex: groupIndex !== undefined ? totalRowCount + (isHeaderVisible ? 1 : 0) : undefined })); }; }, [ onRenderDetailsGroupHeader, adjustedColumns, groups, groupNestingDepth, indentWidth, isHeaderVisible, selection, selectionMode, viewport, checkboxVisibility, cellStyleProps, groupedDetailsListIndexMap, ]); var finalGroupProps = React.useMemo(function () { var _a, _b, _c; return __assign(__assign({}, groupProps), { role: role === defaultRole ? 'rowgroup' : 'presentation', onRenderFooter: finalOnRenderDetailsGroupFooter, onRenderHeader: finalOnRenderDetailsGroupHeader, // pass through custom group header checkbox label headerProps: __assign(__assign({}, (_a = groupProps) === null || _a === void 0 ? void 0 : _a.headerProps), { selectAllButtonProps: __assign({ 'aria-label': checkButtonGroupAriaLabel }, (_c = (_b = groupProps) === null || _b === void 0 ? void 0 : _b.headerProps) === null || _c === void 0 ? void 0 : _c.selectAllButtonProps) }) }); }, [groupProps, finalOnRenderDetailsGroupFooter, finalOnRenderDetailsGroupHeader, checkButtonGroupAriaLabel, role]); var sumColumnWidths = useConst(function () { return memoizeFunction(function (columns) { var totalWidth = 0; columns.forEach(function (column) { return (totalWidth += column.calculatedWidth || column.minWidth); }); return totalWidth; }); }); var collapseAllVisibility = groupProps && groupProps.collapseAllVisibility; var rowWidth = React.useMemo(function () { return sumColumnWidths(adjustedColumns); }, [adjustedColumns, sumColumnWidths]); var onRenderCell = React.useCallback(function (nestingDepth, item, index, group) { var finalOnRenderRow = props.onRenderRow ? composeRenderFunction(props.onRenderRow, onRenderDefaultRow) : onRenderDefaultRow; var groupKey = group ? group.key : undefined; var numOfGroupHeadersBeforeItem = groupKey && groupedDetailsListIndexMap[groupKey] ? groupedDetailsListIndexMap[groupKey].numOfGroupHeadersBeforeItem : 0; var rowRole = role === defaultRole ? undefined : 'presentation'; var rowProps = { item: item, itemIndex: index, flatIndexOffset: (isHeaderVisible ? 2 : 1) + numOfGroupHeadersBeforeItem, compact: compact, columns: adjustedColumns, groupNestingDepth: nestingDepth, id: rowId + "-" + index, selectionMode: selectionMode, selection: selection, onDidMount: onRowDidMount, onWillUnmount: onRowWillUnmount, onRenderItemColumn: onRenderItemColumn, getCellValueKey: getCellValueKey, eventsToRegister: eventsToRegister, dragDropEvents: dragDropEvents, dragDropHelper: dragDropHelper, viewport: viewport, checkboxVisibility: checkboxVisibility, collapseAllVisibility: collapseAllVisibility, getRowAriaLabel: getRowAriaLabel, getRowAriaDescribedBy: getRowAriaDescribedBy, checkButtonAriaLabel: checkButtonAriaLabel, checkboxCellClassName: checkboxCellClassName, useReducedRowRenderer: useReducedRowRenderer, indentWidth: indentWidth, cellStyleProps: cellStyleProps, onRenderDetailsCheckbox: onRenderCheckbox, enableUpdateAnimations: enableUpdateAnimations, rowWidth: rowWidth, useFastIcons: useFastIcons, role: rowRole, }; if (!item) { if (onRenderMissingItem) { return onRenderMissingItem(index, rowProps); } return null; } return finalOnRenderRow(rowProps); }, [ compact, adjustedColumns, selectionMode, selection, rowId, onRowDidMount, onRowWillUnmount, onRenderItemColumn, getCellValueKey, eventsToRegister, dragDropEvents, dragDropHelper, viewport, checkboxVisibility, collapseAllVisibility, getRowAriaLabel, getRowAriaDescribedBy, isHeaderVisible, checkButtonAriaLabel, checkboxCellClassName, useReducedRowRenderer, indentWidth, cellStyleProps, onRenderCheckbox, enableUpdateAnimations, useFastIcons, onRenderDefaultRow, onRenderMissingItem, props.onRenderRow, rowWidth, role, groupedDetailsListIndexMap, ]); var onRenderListCell = React.useCallback(function (nestingDepth) { return function (item, itemIndex) { return onRenderCell(nestingDepth, item, itemIndex); }; }, [onRenderCell]); var isRightArrow = React.useCallback(function (event) { return event.which === getRTLSafeKeyCode(KeyCodes.right, theme); }, [theme]); var focusZoneProps = { componentRef: focusZoneRef, className: classNames.focusZone, direction: FocusZoneDirection.vertical, shouldEnterInnerZone: isRightArrow, onActiveElementChanged: onActiveRowChanged, shouldRaiseClicks: false, onBlur: onBlur, }; var FinalGroupedList = groups && ((_a = groupProps) === null || _a === void 0 ? void 0 : _a.groupedListAs) ? composeComponentAs(groupProps.groupedListAs, GroupedList) : GroupedList; var list = groups ? (React.createElement(FinalGroupedList, { focusZoneProps: focusZoneProps, componentRef: groupedListRef, groups: groups, groupProps: finalGroupProps, items: items, onRenderCell: onRenderCell, role: "presentation", selection: selection, selectionMode: checkboxVisibility !== CheckboxVisibility.hidden ? selectionMode : SelectionMode.none, dragDropEvents: dragDropEvents, dragDropHelper: dragDropHelper, eventsToRegister: rowElementEventMap, listProps: additionalListProps, onGroupExpandStateChanged: onGroupExpandStateChanged, usePageCache: usePageCache, onShouldVirtualize: onShouldVirtualize, getGroupHeight: getGroupHeight, compact: compact })) : (React.createElement(FocusZone, __assign({}, focusZoneProps), React.createElement(List, __assign({ ref: listRef, role: "presentation", items: items, onRenderCell: onRenderListCell(0), usePageCache: usePageCache, onShouldVirtualize: onShouldVirtualize }, additionalListProps)))); var onHeaderKeyDown = React.useCallback(function (ev) { if (ev.which === KeyCodes.down) { if (focusZoneRef.current && focusZoneRef.current.focus()) { // select the first item in list after down arrow key event // only if nothing was selected; otherwise start with the already-selected item if (isSelectedOnFocus && selection.getSelectedIndices().length === 0) { selection.setIndexSelected(0, true, false); } ev.preventDefault(); ev.stopPropagation(); } } }, [selection, focusZoneRef, isSelectedOnFocus]); var onContentKeyDown = React.useCallback(function (ev) { if (ev.which === KeyCodes.up && !ev.altKey) { if (headerRef.current && headerRef.current.focus()) { ev.preventDefault(); ev.stopPropagation(); } } }, [headerRef]); return ( // If shouldApplyApplicationRole is true, role application will be applied to make arrow keys work // with JAWS. React.createElement("div", __assign({ ref: rootRef, className: classNames.root, "data-automationid": "DetailsList", "data-is-scrollable": "false", "aria-label": ariaLabel }, (shouldApplyApplicationRole ? { role: 'application' } : {})), React.createElement(FocusRects, null), React.createElement("div", { role: role, "aria-label": ariaLabelForGrid, "aria-rowcount": isPlaceholderData ? -1 : rowCount, "aria-colcount": colCount, "aria-readonly": "true", "aria-busy": isPlaceholderData }, React.createElement("div", { onKeyDown: onHeaderKeyDown, role: "presentation", className: classNames.headerWrapper }, isHeaderVisible && onRenderDetailsHeader({ componentRef: headerRef, selectionMode: selectionMode, layoutMode: layoutMode, selection: selection, columns: adjustedColumns, onColumnClick: onColumnHeaderClick, onColumnContextMenu: onColumnHeaderContextMenu, onColumnResized: onColumnResized, onColumnIsSizingChanged: onColumnIsSizingChanged, onColumnAutoResized: onColumnAutoResized, groupNestingDepth: groupNestingDepth, isAllCollapsed: isCollapsed, onToggleCollapseAll: onToggleCollapse, ariaLabel: ariaLabelForListHeader, ariaLabelForSelectAllCheckbox: ariaLabelForSelectAllCheckbox, ariaLabelForSelectionColumn: ariaLabelForSelectionColumn, selectAllVisibility: selectAllVisibility, collapseAllVisibility: groupProps && groupProps.collapseAllVisibility, viewport: viewport, columnReorderProps: columnReorderProps, minimumPixelsForDrag: minimumPixelsForDrag, cellStyleProps: cellStyleProps, checkboxVisibility: checkboxVisibility, indentWidth: indentWidth, onRenderDetailsCheckbox: onRenderCheckbox, rowWidth: sumColumnWidths(adjustedColumns), useFastIcons: useFastIcons, }, onRenderDetailsHeader)), React.createElement("div", { onKeyDown: onContentKeyDown, role: "presentation", className: classNames.contentWrapper }, !disableSelectionZone ? (React.createElement(SelectionZone, __assign({ ref: selectionZoneRef, selection: selection, selectionPreservedOnEmptyClick: selectionPreservedOnEmptyClick, selectionMode: selectionMode, isSelectedOnFocus: isSelectedOnFocus, selectionClearedOnEscapePress: isSelectedOnFocus, toggleWithoutModifierPressed: !isSelectedOnFocus, onItemInvoked: onItemInvoked, onItemContextMenu: onItemContextMenu, enterModalOnTouch: enterModalSelectionOnTouch }, (selectionZoneProps || {})), list)) : (list)), onRenderDetailsFooter(__assign({}, detailsFooterProps))))); }; var DetailsListBase = /** @class */ (function (_super) { __extends(DetailsListBase, _super); function DetailsListBase(props) { var _this = _super.call(this, props) || this; _this._root = React.createRef(); _this._header = React.createRef(); _this._groupedList = React.createRef(); _this._list = React.createRef(); _this._focusZone = React.createRef(); _this._selectionZone = React.createRef(); _this._onRenderRow = function (props, defaultRender) { return React.createElement(DetailsRow, __assign({}, props)); }; _this._getDerivedStateFromProps = function (nextProps, previousState) { var _a = _this.props, checkboxVisibility = _a.checkboxVisibility, items = _a.items, setKey = _a.setKey, _b = _a.selectionMode, selectionMode = _b === void 0 ? _this._selection.mode : _b, columns = _a.columns, viewport = _a.viewport, compact = _a.compact, dragDropEvents = _a.dragDropEvents; var _c = (_this.props.groupProps || {}).isAllGroupsCollapsed, isAllGroupsCollapsed = _c === void 0 ? undefined : _c; var newViewportWidth = (nextProps.viewport && nextProps.viewport.width) || 0; var oldViewportWidth = (viewport && viewport.width) || 0; var shouldResetSelection = nextProps.setKey !== setKey || nextProps.setKey === undefined; var shouldForceUpdates = false; if (nextProps.layoutMode !== _this.props.layoutMode) { shouldForceUpdates = true; } var nextState = previousState; if (shouldResetSelection) { _this._initialFocusedIndex = nextProps.initialFocusedIndex; // reset focusedItemIndex when setKey changes nextState = __assign(__assign({}, nextState), { focusedItemIndex: _this._initialFocusedIndex !== undefined ? _this._initialFocusedIndex : -1 }); } if (!_this.props.disableSelectionZone && nextProps.items !== items) { _this._selection.setItems(nextProps.items, shouldResetSelection); } if (nextProps.checkboxVisibility !== checkboxVisibility || nextProps.columns !== columns || newViewportWidth !== oldViewportWidth || nextProps.compact !== compact) { shouldForceUpdates = true; } nextState = __assign(__assign({}, nextState), _this._adjustColumns(nextProps, nextState, true)); if (nextProps.selectionMode !== selectionMode) { shouldForceUpdates = true; } if (isAllGroupsCollapsed === undefined && nextProps.groupProps && nextProps.groupProps.isAllGroupsCollapsed !== undefined) { nextState = __assign(__assign({}, nextState), { isCollapsed: nextProps.groupProps.isAllGroupsCollapsed, isSomeGroupExpanded: !nextProps.groupProps.isAllGroupsCollapsed }); } if (nextProps.dragDropEvents !== dragDropEvents) { _this._dragDropHelper && _this._dragDropHelper.dispose(); _this._dragDropHelper = nextProps.dragDropEvents ? new DragDropHelper({ selection: _this._selection, minimumPixelsForDrag: nextProps.minimumPixelsForDrag, }) : undefined; shouldForceUpdates = true; } if (shouldForceUpdates) { nextState = __assign(__assign({}, nextState), { version: {} }); } return nextState; }; _this._onGroupExpandStateChanged = function (isSomeGroupExpanded) { _this.setState({ isSomeGroupExpanded: isSomeGroupExpanded }); }; _this._onColumnIsSizingChanged = function (column, isSizing) { _this.setState({ isSizing: isSizing }); }; _this._onRowDidMount = function (row) { var _a = row.props, item = _a.item, itemIndex = _a.itemIndex; var itemKey = _this._getItemKey(item, itemIndex); _this._activeRows[itemKey] = row; // this is used for column auto resize _this._setFocusToRowIfPending(row); var onRowDidMount = _this.props.onRowDidMount; if (onRowDidMount) { onRowDidMount(item, itemIndex); } }; _this._onRowWillUnmount = function (row) { var onRowWillUnmount = _this.props.onRowWillUnmount; var _a = row.props, item = _a.item, itemIndex = _a.itemIndex; var itemKey = _this._getItemKey(item, itemIndex); delete _this._activeRows[itemKey]; if (onRowWillUnmount) { onRowWillUnmount(item, itemIndex); } }; _this._onToggleCollapse = function (collapsed) { _this.setState({ isCollapsed: collapsed, }); if (_this._groupedList.current) { _this._groupedList.current.toggleCollapseAll(collapsed); } }; _this._onColumnResized = function (resizingColumn, newWidth, resizingColumnIndex) { var newCalculatedWidth = Math.max(resizingColumn.minWidth || MIN_COLUMN_WIDTH, newWidth); if (_this.props.onColumnResize) { _this.props.onColumnResize(resizingColumn, newCalculatedWidth, resizingColumnIndex); } _this._rememberCalculatedWidth(resizingColumn, newCalculatedWidth); _this.setState(__assign(__assign({}, _this._adjustColumns(_this.props, _this.state, true, resizingColumnIndex)), { version: {} })); }; /** * Callback function when double clicked on the details header column resizer * which will measure the column cells of all the active rows and resize the * column to the max cell width. * * @param column - double clicked column definition * @param columnIndex - double clicked column index * TODO: min width 100 should be changed to const value and should be consistent with the * value used on _onSizerMove method in DetailsHeader */ _this._onColumnAutoResized = function (column, columnIndex) { var max = 0; var count = 0; var totalCount = Object.keys(_this._activeRows).length; for (var key in _this._activeRows) { if (_this._activeRows.hasOwnProperty(key)) { var currentRow = _this._activeRows[key]; currentRow.measureCell(columnIndex, function (width) { max = Math.max(max, width); count++; if (count === totalCount) { _this._onColumnResized(column, max, columnIndex); } }); } } }; /** * Call back function when an element in FocusZone becomes active. It will translate it into item * and call onActiveItemChanged callback if specified. * * @param row - element that became active in Focus Zone * @param focus - event from Focus Zone */ _this._onActiveRowChanged = function (el, ev) { var _a = _this.props, items = _a.items, onActiveItemChanged = _a.onActiveItemChanged; if (!el) { return; } // Check and assign index only if the event was raised from any DetailsRow element if (el.getAttribute('data-item-index')) { var index = Number(el.getAttribute('data-item-index')); if (index >= 0) { if (onActiveItemChanged) { onActiveItemChanged(items[index], index, ev); } _this.setState({ focusedItemIndex: index, }); } } }; _this._onBlur = function (event) { _this.setState({ focusedItemIndex: -1, }); }; initializeComponentRef(_this); _this._async = new Async(_this); _this._activeRows = {}; _this._columnOverrides = {}; _this.state = { focusedItemIndex: -1, lastWidth: 0, adjustedColumns: _this._getAdjustedColumns(props, undefined), isSizing: false, isCollapsed: props.groupProps && props.groupProps.isAllGroupsCollapsed, isSomeGroupExpanded: props.groupProps && !props.groupProps.isAllGroupsCollapsed, version: {}, getDerivedStateFromProps: _this._getDerivedStateFromProps, }; _this._selection = props.selection || new Selection({ onSelectionChanged: undefined, getKey: props.getKey, selectionMode: props.selectionMode, }); if (!_this.props.disableSelectionZone) { _this._selection.setItems(props.items, false); } _this._dragDropHelper = props.dragDropEvents ? new DragDropHelper({ selection: _this._selection, minimumPixelsForDrag: props.minimumPixelsForDrag, }) : undefined; _this._initialFocusedIndex = props.initialFocusedIndex; return _this; } DetailsListBase.getDerivedStateFromProps = function (nextProps, previousState) { return previousState.getDerivedStateFromProps(nextProps, previousState); }; DetailsListBase.prototype.scrollToIndex = function (index, measureItem, scrollToMode) { this._list.current && this._list.current.scrollToIndex(index, measureItem, scrollToMode); this._groupedList.current && this._groupedList.current.scrollToIndex(index, measureItem, scrollToMode); }; DetailsListBase.prototype.focusIndex = function (index, forceIntoFirstElement, measureItem, scrollToMode) { if (forceIntoFirstElement === void 0) { forceIntoFirstElement = false; } var item = this.props.items[index]; if (item) { this.scrollToIndex(index, measureItem, scrollToMode); var itemKey = this._getItemKey(item, index); var row = this._activeRows[itemKey]; if (row) { this._setFocusToRow(row, forceIntoFirstElement); } } }; DetailsListBase.prototype.getStartItemIndexInView = function () { if (this._list && this._list.current) { return this._list.current.getStartItemIndexInView(); } else if (this._groupedList && this._groupedList.current) { return this._groupedList.current.getStartItemIndexInView(); } return 0; }; DetailsListBase.prototype.componentWillUnmount = function () { if (this._dragDropHelper) { // TODO If the DragDropHelper was passed via props, this will dispose it, which is incorrect behavior. this._dragDropHelper.dispose(); } this._async.dispose(); }; DetailsListBase.prototype.componentDidUpdate = function (prevProps, prevState) { this._notifyColumnsResized(); if (this._initialFocusedIndex !== undefined) { var item = this.props.items[this._initialFocusedIndex]; if (item) { var itemKey = this._getItemKey(item, this._initialFocusedIndex); var row = this._activeRows[itemKey]; if (row) { this._setFocusToRowIfPending(row); } } } if (this.props.items !== prevProps.items && this.props.items.length > 0 && this.state.focusedItemIndex !== -1 && !elementContains(this._root.current, document.activeElement, false)) { // Item set has changed and previously-focused item is gone. // Set focus to item at index of previously-focused item if it is in range, // else set focus to the last item. var index = this.state.focusedItemIndex < this.props.items.length ? this.state.focusedItemIndex : this.props.items.length - 1; var item = this.props.items[index]; var itemKey = this._getItemKey(item, this.state.focusedItemIndex); var row = this._activeRows[itemKey]; if (row) { this._setFocusToRow(row); } else { this._initialFocusedIndex = index; } } if (this.props.onDidUpdate) { this.props.onDidUpdate(this); } }; DetailsListBase.prototype.render = function () { return (React.createElement(DetailsListInner, __assign({}, this.props, this.state, { selection: this._selection, dragDropHelper: this._dragDropHelper, rootRef: this._root, listRef: this._list, groupedListRef: this._groupedList, focusZoneRef: this._focusZone, headerRef: this._header, selectionZoneRef: this._selectionZone, onGroupExpandStateChanged: this._onGroupExpandStateChanged, onColumnIsSizingChanged: this._onColumnIsSizingChanged, onRowDidMount: this._onRowDidMount, onRowWillUnmount: this._onRowWillUnmount, onColumnResized: this._onColumnResized, onColumnAutoResized: this._onColumnAutoResized, onToggleCollapse: this._onToggleCollapse, onActiveRowChanged: this._onActiveRowChanged, onBlur: this._onBlur, onRenderDefaultRow: this._onRenderRow }))); }; DetailsListBase.prototype.forceUpdate = function () { _super.prototype.forceUpdate.call(this); this._forceListUpdates(); }; DetailsListBase.prototype._getGroupNestingDepth = function () { var groups = this.props.groups; var level = 0; var groupsInLevel = groups; while (groupsInLevel && groupsInLevel.length > 0) { level++; groupsInLevel = groupsInLevel[0].children; } return level; }; DetailsListBase.prototype._setFocusToRowIfPending = function (row) { var itemIndex = row.props.itemIndex; if (this._initialFocusedIndex !== undefined && itemIndex === this._initialFocusedIndex) { this._setFocusToRow(row); delete this._initialFocusedIndex; } }; DetailsListBase.prototype._setFocusToRow = function (row, forceIntoFirstElement) { if (forceIntoFirstElement === void 0) { forceIntoFirstElement = false; } if (this._selectionZone.current) { this._selectionZone.current.ignoreNextFocus(); } this._async.setTimeout(function () { row.focus(forceIntoFirstElement); }, 0); }; DetailsListBase.prototype._forceListUpdates = function () { if (this._groupedList.current) { this._groupedList.current.forceUpdate(); } if (this._list.current) { this._list.current.forceUpdate(); } }; DetailsListBase.prototype._notifyColumnsResized = function () { this.state.adjustedColumns.forEach(function (column) { if (column.onColumnResize) { column.onColumnResize(column.currentWidth); } }); }; DetailsListBase.prototype._adjustColumns = function (newProps, previousState, forceUpdate, resizingColumnIndex) { var adjustedColumns = this._getAdjustedColumns(newProps, previousState, forceUpdate, resizingColumnIndex); var viewport = this.props.viewport; var viewportWidth = viewport && viewport.width ? viewport.width : 0; return __assign(__assign({}, previousState), { adjustedColumns: adjustedColumns, lastWidth: viewportWidth }); }; /** Returns adjusted columns, given the viewport size and layout mode. */ DetailsListBase.prototype._getAdjustedColumns = function (newProps, previousState, forceUpdate, resizingColumnIndex) { var _this = this; var newItems = newProps.items, layoutMode = newProps.layoutMode, selectionMode = newProps.selectionMode, viewport = newProps.viewport; var viewportWidth = viewport && viewport.width ? viewport.width : 0; var newColumns = newProps.columns; var columns = this.props ? this.props.columns : []; var lastWidth = previousState ? previousState.lastWidth : -1; var lastSelectionMode = previousState ? previousState.lastSelectionMode : undefined; if (!forceUpdate && lastWidth === viewportWidth && lastSelectionMode === selectionMode && (!columns || newColumns === columns)) { return newColumns || []; } newColumns = newColumns || buildColumns(newItems, true); var adjustedColumns; if (layoutMode === DetailsListLayoutMode.fixedColumns) { adjustedColumns = this._getFixedColumns(newColumns, viewportWidth, newProps); // Preserve adjusted column calculated widths. adjustedColumns.forEach(function (column) { _this._rememberCalculatedWidth(column, column.calculatedWidth); }); } else { adjustedColumns = this._getJustifiedColumns(newColumns, viewportWidth, newProps); adjustedColumns.forEach(function (column) { _this._getColumnOverride(column.key).currentWidth = column.calculatedWidth; }); } return adjustedColumns; }; /** Builds a set of columns based on the given columns mixed with the current overrides. */ DetailsListBase.prototype._getFixedColumns = function (newColumns, viewportWidth, props) { var _this = this; var _a = this.props, _b = _a.selectionMode, selectionMode = _b === void 0 ? this._selection.mode : _b, checkboxVisibility = _a.checkboxVisibility, flexMargin = _a.flexMargin, skipViewportMeasures = _a.skipViewportMeasures; var remainingWidth = viewportWidth - (flexMargin || 0); var sumProportionalWidth = 0; newColumns.forEach(function (col) { if (skipViewportMeasures || !col.flexGrow) { remainingWidth -= col.maxWidth || col.minWidth || MIN_COLUMN_WIDTH; } else { remainingWidth -= col.minWidth || MIN_COLUMN_WIDTH; sumProportionalWidth += col.flexGrow; } remainingWidth -= getPaddedWidth(col, props, true); }); var rowCheckWidth = selectionMode !== SelectionMode.none && checkboxVisibility !== CheckboxVisibility.hidden ? CHECKBOX_WIDTH : 0; var groupExpandWidth = this._getGroupNestingDepth() * GROUP_EXPAND_WIDTH; remainingWidth -= rowCheckWidth + groupExpandWidth; var widthFraction = remainingWidth / sumProportionalWidth; // Shrinks proportional columns to their max width and adds the remaining width to distribute to other columns. if (!skipViewportMeasures) { newColumns.forEach(function (column) { var newColumn = __assign(__assign({}, column), _this._columnOverrides[column.key]); if (newColumn.flexGrow && newColumn.maxWidth) { var fullWidth = newColumn.flexGrow * widthFraction + newColumn.minWidth; var shrinkWidth = fullWidth - newColumn.maxWidth; if (shrinkWidth > 0) { remainingWidth += shrinkWidth; sumProportionalWidth -= (shrinkWidth / (fullWidth - newColumn.minWidth)) * newColumn.flexGrow; } } }); } widthFraction = remainingWidth > 0 ? remainingWidth / sumProportionalWidth : 0; return newColumns.map(function (column) { var newColumn = __assign(__assign({}, column), _this._columnOverrides[column.key]); // Delay computation until viewport width is available. if (!skipViewportMeasures && newColumn.flexGrow && remainingWidth <= 0) { return newColumn; } if (!newColumn.calculatedWidth) { if (!skipViewportMeasures && newColumn.flexGrow) { // Assigns the proportion of the remaining extra width after all columns have met minimum widths. newColumn.calculatedWidth = newColumn.minWidth + newColumn.flexGrow * widthFraction; newColumn.calculatedWidth = Math.min(newColumn.calculatedWidth, newColumn.maxWidth || Number.MAX_VALUE); } else { newColumn.calculatedWidth = newColumn.maxWidth || newColumn.minWidth || MIN_COLUMN_WIDTH; } } return newColumn; }); }; /** Builds a set of columns to fix within the viewport width. */ DetailsListBase.prototype._getJustifiedColumns = function (newColumns, viewportWidth, props) { var _this = this; var _a = props.selectionMode, selectionMode = _a === void 0 ? this._selection.mode : _a, checkboxVisibility = props.checkboxVisibility; var rowCheckWidth = selectionMode !== SelectionMode.none && checkboxVisibility !== CheckboxVisibility.hidden ? CHECKBOX_WIDTH : 0; var groupExpandWidth = this._getGroupNestingDepth() * GROUP_EXPAND_WIDTH; var totalWidth = 0; // offset because we have one less inner padding. var minimumWidth = 0; var availableWidth = viewportWidth - (rowCheckWidth + groupExpandWidth); var adjustedColumns = newColumns.map(function (column, i) { var baseColumn = __assign(__assign({}, column), { calculatedWidth: column.minWidth || MIN_COLUMN_WIDTH }); var newColumn = __assign(__assign({}, baseColumn), _this._columnOverrides[column.key]); // eslint-disable-next-line deprecation/deprecation if (!(baseColumn.isCollapsible || baseColumn.isCollapsable)) { minimumWidth += getPaddedWidth(baseColumn, props); } totalWidth += getPaddedWidth(newColumn, props); return newColumn; }); if (minimumWidth > availableWidth) { return adjustedColumns; } var lastIndex = adjustedColumns.length - 1; // Shrink or remove collapsable columns. while (lastIndex >= 0 && totalWidth > availableWidth) { var column = adjustedColumns[lastIndex]; var minWidth = column.minWidth || MIN_COLUMN_WIDTH; var overflowWidth = totalWidth - availableWidth; // eslint-disable-next-line deprecation/deprecation if (column.calculatedWidth - minWidth >= overflowWidth || !(column.isCollapsible || column.isCollapsable)) { var originalWidth = column.calculatedWidth; column.calculatedWidth = Math.max(column.calculatedWidth - overflowWidth, minWidth); totalWidth -= originalWidth - column.calculatedWidth; } else { totalWidth -= getPaddedWidth(column, props); adjustedColumns.splice(lastIndex, 1); } lastIndex--; } // Then expand columns starting at the beginning, until we've filled the width. for (var i = 0; i < adjustedColumns.length && totalWidth < availableWidth; i++) { var column = adjustedColumns[i]; var isLast = i === adjustedColumns.length - 1; var overrides = this._columnOverrides[column.key]; if (overrides && overrides.calculatedWidth && !isLast) { continue; } var spaceLeft = availableWidth - totalWidth; var increment = void 0; if (isLast) { increment = spaceLeft; } else { var maxWidth = column.maxWidth; var minWidth = column.minWidth || maxWidth || MIN_COLUMN_WIDTH; increment = maxWidth ? Math.min(spaceLeft, maxWidth - minWidth) : spaceLeft; } column.calculatedWidth = column.calculatedWidth + increment; totalWidth += increment; } return adjustedColumns; }; DetailsListBase.prototype._rememberCalculatedWidth = function (column, newCalculatedWidth) { var overrides = this._getColumnOverride(column.key); overrides.calculatedWidth = newCalculatedWidth; overrides.currentWidth = newCalculatedWidth; }; DetailsListBase.prototype._getColumnOverride = function (key) { return (this._columnOverrides[key] = this._columnOverrides[key] || {}); }; DetailsListBase.prototype._getItemKey = function (item, itemIndex) { var getKey = this.props.getKey; var itemKey = undefined; if (item) { itemKey = item.key; } if (getKey) { itemKey = getKey(item, itemIndex); } if (!itemKey) { itemKey = itemIndex; } return itemKey; }; DetailsListBase.defaultProps = { layoutMode: DetailsListLayoutMode.justified, selectionMode: SelectionMode.multiple, constrainMode: ConstrainMode.horizontalConstrained, checkboxVisibility: CheckboxVisibility.onHover, isHeaderVisible: true, compact: false, useFastIcons: true, }; DetailsListBase = __decorate([ withViewport ], DetailsListBase); return DetailsListBase; }(React.Component)); export { DetailsListBase }; export function buildColumns(items, canResizeColumns, onColumnClick, sortedColumnKey, isSortedDescending, groupedColumnKey, isMultiline) { var columns = []; if (items && items.l