var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
define(["require", "exports", 'react', './DetailsList.Props', './DetailsGroup', './DetailsHeader', '../../FocusZone', '../../utilities/decorators/withViewport', '../../utilities/object', '../../utilities/css', '../../utilities/selection/index', '../../utilities/eventGroup/EventGroup', '../../utilities/rtl', '../../utilities/KeyCodes', '../../utilities/dragdrop/DragDropHelper', './DetailsList.scss'], function (require, exports, React, DetailsList_Props_1, DetailsGroup_1, DetailsHeader_1, FocusZone_1, withViewport_1, object_1, css_1, index_1, EventGroup_1, rtl_1, KeyCodes_1, DragDropHelper_1) {
    "use strict";
    var MIN_COLUMN_WIDTH = 100; // this is the global min width
    var CHECKBOX_WIDTH = 36;
    var GROUP_EXPAND_WIDTH = 36;
    var DEFAULT_INNER_PADDING = 16;
    var DetailsList = (function (_super) {
        __extends(DetailsList, _super);
        function DetailsList(props) {
            _super.call(this, props);
            this._activeRows = {};
            this._columnOverrides = {};
            this._onColumnIsSizingChanged = this._onColumnIsSizingChanged.bind(this);
            this._onColumnResized = this._onColumnResized.bind(this);
            this._onColumnAutoResized = this._onColumnAutoResized.bind(this);
            this._onAllSelectedChanged = this._onAllSelectedChanged.bind(this);
            this._onRowDidMount = this._onRowDidMount.bind(this);
            this._onRowWillUnmount = this._onRowWillUnmount.bind(this);
            this._onToggleCollapse = this._onToggleCollapse.bind(this);
            this._onToggleCollapseAll = this._onToggleCollapseAll.bind(this);
            this._onToggleSelectGroup = this._onToggleSelectGroup.bind(this);
            this._onToggleSummarize = this._onToggleSummarize.bind(this);
            this._getGroupKey = this._getGroupKey.bind(this);
            this._onActiveRowChanged = this._onActiveRowChanged.bind(this);
            this._onHeaderKeyDown = this._onHeaderKeyDown.bind(this);
            this._onContentKeyDown = this._onContentKeyDown.bind(this);
            this.state = {
                lastWidth: 0,
                adjustedColumns: this._getAdjustedColumns(props),
                layoutMode: props.layoutMode,
                groups: props.groups,
                isAllCollapsed: props.groupProps && props.groupProps.isAllGroupsCollapsed,
                isSizing: false
            };
            this._events = new EventGroup_1.EventGroup(this);
            this._selection = props.selection || new index_1.Selection(null, props.getKey);
            this._selection.setItems(props.items, false);
            this._dragDropHelper = props.dragDropEvents ? new DragDropHelper_1.DragDropHelper({ selection: this._selection }) : null;
        }
        DetailsList.prototype.componentWillUnmount = function () {
            this._events.dispose();
            if (this._dragDropHelper) {
                this._dragDropHelper.dispose();
            }
        };
        DetailsList.prototype.componentDidUpdate = function (prevProps, prevState) {
            if (this.props.onDidUpdate) {
                this.props.onDidUpdate(this);
            }
        };
        DetailsList.prototype.componentWillReceiveProps = function (newProps) {
            var _a = this.props, setKey = _a.setKey, groups = _a.groups, items = _a.items, selectionMode = _a.selectionMode, columns = _a.columns;
            var layoutMode = this.state.layoutMode;
            var shouldResetSelection = (newProps.setKey !== setKey) || newProps.setKey === undefined;
            var shouldForceUpdates = false;
            if (newProps.layoutMode !== this.props.layoutMode) {
                layoutMode = newProps.layoutMode;
                this.setState({ layoutMode: layoutMode });
                shouldForceUpdates = true;
            }
            if (newProps.items !== items) {
                this._selection.setItems(newProps.items, shouldResetSelection);
            }
            if (newProps.columns !== columns) {
                shouldForceUpdates = true;
            }
            this._adjustColumns(newProps, true, layoutMode);
            if (newProps.groups !== groups) {
                this.setState({ groups: newProps.groups });
                shouldForceUpdates = true;
            }
            if (newProps.selectionMode !== selectionMode) {
                shouldForceUpdates = true;
            }
            if (shouldForceUpdates) {
                this._forceListUpdates();
            }
        };
        DetailsList.prototype.render = function () {
            var _this = this;
            var _a = this.props, ariaLabelForListHeader = _a.ariaLabelForListHeader, ariaLabelForSelectAllCheckbox = _a.ariaLabelForSelectAllCheckbox, checkboxVisibility = _a.checkboxVisibility, className = _a.className, constrainMode = _a.constrainMode, dragDropEvents = _a.dragDropEvents, groupProps = _a.groupProps, isHeaderVisible = _a.isHeaderVisible, items = _a.items, onItemInvoked = _a.onItemInvoked, onRenderMissingItem = _a.onRenderMissingItem, onRenderItemColumn = _a.onRenderItemColumn, onColumnHeaderClick = _a.onColumnHeaderClick, onColumnHeaderContextMenu = _a.onColumnHeaderContextMenu, rowElementEventMap = _a.rowElementEventMap, selectionMode = _a.selectionMode, viewport = _a.viewport, getRowAriaLabel = _a.getRowAriaLabel, ariaLabel = _a.ariaLabel, canSelectItem = _a.canSelectItem, checkButtonAriaLabel = _a.checkButtonAriaLabel, ariaLabelForGrid = _a.ariaLabelForGrid, _b = _a.shouldApplyApplicationRole, shouldApplyApplicationRole = _b === void 0 ? false : _b;
            var _c = this.state, adjustedColumns = _c.adjustedColumns, groups = _c.groups, isSizing = _c.isSizing, isAllCollapsed = _c.isAllCollapsed, layoutMode = _c.layoutMode;
            var selection = this._selection;
            var groupNestingDepth = this._getGroupNestingDepth();
            if (!groups) {
                groups = [null];
            }
            // override group header/footer props as needed
            var headerProps = object_1.assign({}, groupProps && groupProps.headerProps ? groupProps.headerProps : {}, {
                onToggleCollapse: this._onToggleCollapse,
                onToggleSelectGroup: this._onToggleSelectGroup
            });
            var footerProps = object_1.assign({}, groupProps && groupProps.footerProps ? groupProps.footerProps : {}, {
                onToggleSummarize: this._onToggleSummarize
            });
            var renderedGroups = groups.map(function (group, groupIndex) { return ((!group || group.count > 0) ? (React.createElement(DetailsGroup_1.DetailsGroup, {ref: 'detailsGroup_' + groupIndex, key: _this._getGroupKey(group, groupIndex), items: items, columns: adjustedColumns, group: group, groupIndex: groupIndex, groupNestingDepth: groupNestingDepth, getGroupItemLimit: groupProps && groupProps.getGroupItemLimit, selectionMode: selectionMode, selection: selection, isSizing: isSizing, eventsToRegister: rowElementEventMap, onRowDidMount: _this._onRowDidMount, onRowWillUnmount: _this._onRowWillUnmount, onRenderItemColumn: onRenderItemColumn, dragDropEvents: dragDropEvents, onRenderMissingItem: onRenderMissingItem, dragDropHelper: _this._dragDropHelper, viewport: viewport, headerProps: headerProps, footerProps: footerProps, checkboxVisibility: checkboxVisibility, getRowAriaLabel: getRowAriaLabel, canSelectItem: canSelectItem, checkButtonAriaLabel: checkButtonAriaLabel})) : null); });
            return (
            // If shouldApplyApplicationRole is true, role application will be applied to make arrow keys work
            // with JAWS.
            React.createElement("div", {ref: 'root', className: css_1.css('ms-DetailsList', className, {
                'is-fixed': layoutMode === DetailsList_Props_1.DetailsListLayoutMode.fixedColumns,
                'is-horizontalConstrained': constrainMode === DetailsList_Props_1.ConstrainMode.horizontalConstrained
            }), "data-automationid": 'DetailsList', "data-is-scrollable": 'false', "aria-label": ariaLabel, role: shouldApplyApplicationRole ? 'application' : ''}, React.createElement("div", {ref: 'headerContainer', onKeyDown: this._onHeaderKeyDown, role: 'grid', "aria-label": ariaLabelForGrid}, isHeaderVisible && (React.createElement(DetailsHeader_1.DetailsHeader, {ref: 'header', selectionMode: selectionMode, layoutMode: layoutMode, selection: selection, columns: adjustedColumns, onColumnClick: onColumnHeaderClick, onColumnContextMenu: onColumnHeaderContextMenu, onColumnResized: this._onColumnResized, onColumnIsSizingChanged: this._onColumnIsSizingChanged, onColumnAutoResized: this._onColumnAutoResized, groupNestingDepth: groupNestingDepth, isAllCollapsed: isAllCollapsed, onToggleCollapseAll: this._onToggleCollapseAll, ariaLabel: ariaLabelForListHeader, ariaLabelForSelectAllCheckbox: ariaLabelForSelectAllCheckbox}))), React.createElement("div", {ref: 'contentContainer', onKeyDown: this._onContentKeyDown}, React.createElement(FocusZone_1.FocusZone, {ref: 'focusZone', direction: FocusZone_1.FocusZoneDirection.vertical, isInnerZoneKeystroke: function (ev) { return (ev.which === rtl_1.getRTLSafeKeyCode(KeyCodes_1.KeyCodes.right)); }, onActiveElementChanged: this._onActiveRowChanged}, React.createElement(index_1.SelectionZone, {selection: selection, selectionMode: selectionMode, onItemInvoked: onItemInvoked}, renderedGroups)))));
        };
        DetailsList.prototype.forceUpdate = function () {
            _super.prototype.forceUpdate.call(this);
            this._forceListUpdates();
        };
        DetailsList.prototype._onColumnIsSizingChanged = function (column, isSizing) {
            this.setState({ isSizing: isSizing });
        };
        DetailsList.prototype._onHeaderKeyDown = function (ev) {
            if (ev.which === KeyCodes_1.KeyCodes.down) {
                if (this.refs.focusZone.focus()) {
                    ev.preventDefault();
                    ev.stopPropagation();
                }
            }
        };
        DetailsList.prototype._onContentKeyDown = function (ev) {
            if (ev.which === KeyCodes_1.KeyCodes.up) {
                if (this.refs.header.focus()) {
                    ev.preventDefault();
                    ev.stopPropagation();
                }
            }
        };
        DetailsList.prototype._getGroupKey = function (group, groupIndex) {
            return 'group-' + (group ?
                group.key + '-' + group.count :
                '');
        };
        DetailsList.prototype._getGroupNestingDepth = function () {
            var groups = this.state.groups;
            var level = 0;
            var groupsInLevel = groups;
            while (groupsInLevel && groupsInLevel.length > 0) {
                level++;
                groupsInLevel = groupsInLevel[0].children;
            }
            return level;
        };
        DetailsList.prototype._onRowDidMount = function (row) {
            var onRowDidMount = this.props.onRowDidMount;
            var index = row.props.itemIndex;
            this._activeRows[index] = row; // this is used for column auto resize
            if (onRowDidMount) {
                onRowDidMount(row.props.item, index);
            }
        };
        DetailsList.prototype._onRowWillUnmount = function (row) {
            var onRowWillUnmount = this.props.onRowWillUnmount;
            var index = row.props.itemIndex;
            delete this._activeRows[index];
            this._events.off(row.refs.root);
            if (onRowWillUnmount) {
                onRowWillUnmount(row.props.item, index);
            }
        };
        DetailsList.prototype._onAllSelectedChanged = function () {
            this._selection.toggleAllSelected();
        };
        DetailsList.prototype._onToggleCollapse = function (group) {
            var groupProps = this.props.groupProps;
            var onToggleCollapse = groupProps && groupProps.headerProps && groupProps.headerProps.onToggleCollapse;
            if (group) {
                if (onToggleCollapse) {
                    onToggleCollapse(group);
                }
                group.isCollapsed = !group.isCollapsed;
                this.forceUpdate();
            }
        };
        DetailsList.prototype._onToggleCollapseAll = function (allCollapsed) {
            var groups = this.state.groups;
            var groupProps = this.props.groupProps;
            var onToggleCollapseAll = groupProps && groupProps.onToggleCollapseAll;
            if (groups) {
                if (onToggleCollapseAll) {
                    onToggleCollapseAll(allCollapsed);
                }
                for (var groupIndex = 0; groupIndex < groups.length; groupIndex++) {
                    groups[groupIndex].isCollapsed = allCollapsed;
                }
                this.setState({
                    isAllCollapsed: allCollapsed
                });
                this.forceUpdate();
            }
        };
        DetailsList.prototype._onToggleSelectGroup = function (group) {
            var groups = this.state.groups;
            if (group) {
                var isSelected = !group.isSelected;
                this._selectGroup(group, isSelected);
                this.setState({
                    groups: groups
                });
            }
        };
        DetailsList.prototype._selectGroup = function (group, isSelected) {
            var _this = this;
            var _a = this.props, groupProps = _a.groupProps, items = _a.items;
            group.isSelected = isSelected;
            if (group.children && group.children.length > 0) {
                group.children.forEach(function (childGroup) {
                    _this._selectGroup(childGroup, isSelected);
                });
            }
            else {
                var getGroupItemLimit = groupProps && groupProps.getGroupItemLimit;
                var groupItemLimit = getGroupItemLimit ? getGroupItemLimit(group) : items.length;
                var start = group.startIndex;
                var end = group.startIndex + Math.min(group.count, groupItemLimit);
                for (var idx = start; idx < end; idx++) {
                    this._selection.setIndexSelected(idx, isSelected, false /* shouldAnchor */);
                }
            }
        };
        DetailsList.prototype._forceListUpdates = function (groups) {
            groups = groups || this.state.groups;
            var groupCount = groups ? groups.length : 1;
            for (var i = 0; i < groupCount; i++) {
                var detailsGroup = this.refs['detailsGroup_' + String(i)];
                if (detailsGroup) {
                    detailsGroup.forceListUpdate();
                }
            }
        };
        DetailsList.prototype._onToggleSummarize = function (group) {
            var groups = this.state.groups;
            var groupProps = this.props.groupProps;
            var onToggleSummarize = groupProps && groupProps.footerProps && groupProps.footerProps.onToggleSummarize;
            if (onToggleSummarize) {
                onToggleSummarize(group);
            }
            else {
                if (group) {
                    group.isShowingAll = !group.isShowingAll;
                    this.setState({
                        groups: groups
                    });
                }
            }
        };
        DetailsList.prototype._adjustColumns = function (newProps, forceUpdate, layoutMode) {
            var adjustedColumns = this._getAdjustedColumns(newProps, forceUpdate, layoutMode);
            var viewportWidth = this.props.viewport.width;
            if (adjustedColumns) {
                this.setState({
                    adjustedColumns: adjustedColumns,
                    lastWidth: viewportWidth,
                    layoutMode: layoutMode
                });
            }
        };
        /** Returns adjusted columns, given the viewport size and layout mode. */
        DetailsList.prototype._getAdjustedColumns = function (newProps, forceUpdate, layoutMode) {
            var _this = this;
            var newColumns = newProps.columns, viewportWidth = newProps.viewport.width, selectionMode = newProps.selectionMode;
            if (layoutMode === undefined) {
                layoutMode = newProps.layoutMode;
            }
            var columns = this.props ? this.props.columns : [];
            var lastWidth = this.state ? this.state.lastWidth : -1;
            var lastSelectionMode = this.state ? this.state.lastSelectionMode : undefined;
            if (viewportWidth !== undefined) {
                if (!forceUpdate &&
                    lastWidth === viewportWidth &&
                    lastSelectionMode === selectionMode &&
                    (!columns || newColumns === columns)) {
                    return;
                }
            }
            else {
                viewportWidth = this.props.viewport.width;
            }
            newColumns = newColumns || buildColumns(this.props.items);
            var adjustedColumns;
            if (layoutMode === DetailsList_Props_1.DetailsListLayoutMode.fixedColumns) {
                adjustedColumns = this._getFixedColumns(newColumns);
            }
            else {
                adjustedColumns = this._getJustifiedColumns(newColumns, viewportWidth);
            }
            // Preserve adjusted column calculated widths.
            adjustedColumns.forEach(function (column) {
                var overrides = _this._columnOverrides[column.key] = _this._columnOverrides[column.key] || {};
                overrides.calculatedWidth = column.calculatedWidth;
            });
            return adjustedColumns;
        };
        /** Builds a set of columns based on the given columns mixed with the current overrides. */
        DetailsList.prototype._getFixedColumns = function (newColumns) {
            var _this = this;
            return newColumns.map(function (column) {
                var newColumn = object_1.assign({}, column, _this._columnOverrides[column.key]);
                if (!newColumn.calculatedWidth) {
                    newColumn.calculatedWidth = newColumn.maxWidth || newColumn.minWidth || MIN_COLUMN_WIDTH;
                }
                return newColumn;
            });
        };
        /** Builds a set of columns to fix within the viewport width. */
        DetailsList.prototype._getJustifiedColumns = function (newColumns, viewportWidth) {
            var _a = this.props, selectionMode = _a.selectionMode, groups = _a.groups;
            var outerPadding = DEFAULT_INNER_PADDING;
            var rowCheckWidth = (selectionMode !== index_1.SelectionMode.none) ? CHECKBOX_WIDTH : 0;
            var groupExpandWidth = groups ? GROUP_EXPAND_WIDTH : 0;
            var totalWidth = 0; // offset because we have one less inner padding.
            var availableWidth = viewportWidth - outerPadding - rowCheckWidth - groupExpandWidth;
            var adjustedColumns = newColumns.map(function (column, i) {
                var newColumn = object_1.assign({}, column, {
                    calculatedWidth: column.minWidth || MIN_COLUMN_WIDTH
                });
                totalWidth += newColumn.calculatedWidth + (i > 0 ? DEFAULT_INNER_PADDING : 0);
                return newColumn;
            });
            var lastIndex = adjustedColumns.length - 1;
            // Remove collapsable columns.
            while (lastIndex > 1 && totalWidth > availableWidth) {
                var column = adjustedColumns[lastIndex];
                if (column.isCollapsable) {
                    totalWidth -= column.calculatedWidth + DEFAULT_INNER_PADDING;
                    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 maxWidth = column.maxWidth;
                var minWidth = column.minWidth || maxWidth || MIN_COLUMN_WIDTH;
                var spaceLeft = availableWidth - totalWidth;
                var increment = Math.min(spaceLeft, maxWidth - minWidth);
                // Add remaining space to the last column.
                if (i === (adjustedColumns.length - 1)) {
                    increment = spaceLeft;
                }
                column.calculatedWidth += increment;
                totalWidth += increment;
            }
            // Mark the last column as not resizable to avoid extra scrolling issues.
            if (adjustedColumns.length) {
                adjustedColumns[adjustedColumns.length - 1].isResizable = false;
            }
            return adjustedColumns;
        };
        DetailsList.prototype._onColumnResized = function (resizingColumn, newWidth) {
            this._columnOverrides[resizingColumn.key].calculatedWidth = Math.max(resizingColumn.minWidth || MIN_COLUMN_WIDTH, newWidth);
            this._adjustColumns(this.props, true, DetailsList_Props_1.DetailsListLayoutMode.fixedColumns);
            this._forceListUpdates();
        };
        /**
         * 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.
         *
         * @private
         * @param {IColumn} column (double clicked column definition)
         * @param {number} 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
         */
        DetailsList.prototype._onColumnAutoResized = function (column, columnIndex) {
            var _this = this;
            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);
                        }
                    });
                }
            }
        };
        /**
         * Call back function when an element in FocusZone becomes active. It will transalate it into item
         * and call onActiveItemChanged callback if specified.
         *
         * @private
         * @param {el} row element that became active in Focus Zone
         * @param {ev} focus event from Focus Zone
         */
        DetailsList.prototype._onActiveRowChanged = function (el, ev) {
            var _a = this.props, items = _a.items, onActiveItemChanged = _a.onActiveItemChanged;
            if (!onActiveItemChanged || !el) {
                return;
            }
            var index = Number(el.getAttribute('data-item-index'));
            if (index >= 0) {
                onActiveItemChanged(items[index], index, ev);
            }
        };
        ;
        DetailsList.defaultProps = {
            layoutMode: DetailsList_Props_1.DetailsListLayoutMode.justified,
            selectionMode: index_1.SelectionMode.multiple,
            constrainMode: DetailsList_Props_1.ConstrainMode.horizontalConstrained,
            checkboxVisibility: DetailsList_Props_1.CheckboxVisibility.onHover,
            isHeaderVisible: true
        };
        DetailsList = __decorate([
            withViewport_1.withViewport
        ], DetailsList);
        return DetailsList;
    }(React.Component));
    exports.DetailsList = DetailsList;
    function buildColumns(items, canResizeColumns, onColumnClick, sortedColumnKey, isSortedDescending, groupedColumnKey, isMultiline) {
        var columns = [];
        if (items && items.length) {
            var firstItem = items[0];
            var isFirstColumn = true;
            for (var propName in firstItem) {
                if (firstItem.hasOwnProperty(propName)) {
                    columns.push({
                        key: propName,
                        name: propName,
                        fieldName: propName,
                        minWidth: MIN_COLUMN_WIDTH,
                        maxWidth: 300,
                        isCollapsable: !!columns.length,
                        isMultiline: (isMultiline === undefined) ? false : isMultiline,
                        isSorted: sortedColumnKey === propName,
                        isSortedDescending: !!isSortedDescending,
                        isRowHeader: false,
                        columnActionsMode: DetailsList_Props_1.ColumnActionsMode.clickable,
                        isResizable: canResizeColumns,
                        onColumnClick: onColumnClick,
                        isGrouped: groupedColumnKey === propName
                    });
                    isFirstColumn = false;
                }
            }
        }
        return columns;
    }
    exports.buildColumns = buildColumns;
});
