office-ui-fabric-react
Version:
Reusable React components for building experiences for Microsoft 365.
882 lines • 52.3 kB
JavaScript
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