import * as tslib_1 from "tslib";
import { Component, Input, Output, EventEmitter, QueryList, ContentChildren, ElementRef, ViewEncapsulation, ChangeDetectionStrategy, forwardRef, ViewChild, Renderer2, NgZone, ChangeDetectorRef } from '@angular/core';
import { OnChange } from '../core/decorators';
import { addClass } from '../util/dom';
import { TableHeaderItemComponent } from './table-th';
/**
 * sort function
 *
 * @param {string} order - order type
 * @param {string} orderBy - order field
 */
export function sortFunc(order, orderBy) {
    return function (x, y) {
        var symbol = order === 'asc' ? 1 : -1;
        var a = x[orderBy];
        var b = y[orderBy];
        // 相等，返回0
        if (a === b) {
            return 0;
        }
        if (a === null && b === null) {
            return 0;
        }
        // b是null，desc时排在最后
        if (b === null) {
            return symbol * 1;
        }
        else if (a === null) {
            return symbol * (-1);
        }
        var aIsNumber = !isNaN(a);
        var bIsNumber = !isNaN(b);
        // a, b 都是数字
        if (aIsNumber && bIsNumber) {
            return symbol * (parseFloat(a) - parseFloat(b));
        }
        // a, b 如果有一个能转成数字
        // 能转成数字的永远大。
        if (aIsNumber || bIsNumber) {
            return aIsNumber ? (symbol * 1) : (symbol * -1);
        }
        // 否则就是文字对比
        return symbol * (a + '').localeCompare(b);
    };
}
/**
 * Table Component
 */
var TableComponent = /** @class */ (function () {
    function TableComponent(_el, _render, _ngZone, _cd) {
        this._el = _el;
        this._render = _render;
        this._ngZone = _ngZone;
        this._cd = _cd;
        /** table sort event emitter, emit a `SortParam` Object with the `order` and `orderBy` property. */
        this.sort = new EventEmitter();
        this.dataChange = new EventEmitter();
        /**
         * table datasource
         * @default []
         */
        this.datasource = [];
        this.datasourceChange = new EventEmitter();
        /**
         * inner filterd datasource
         * @docs-private
         */
        this.data = [];
        /**
         * whether the table is bordered
         * @default false
         */
        this.bordered = false;
        /**
         * table order state change event listener
         * @docs-private
         */
        this.orderChange = new EventEmitter();
        /**
         * table orderby field change event listener
         * @docs-private
         */
        this.orderByChange = new EventEmitter();
        /** table current page */
        this.page = 1;
        /**
         * table page change event listener
         * @docs-private
         */
        this.pageChange = new EventEmitter();
        /**
         * table pageSize change event listener
         * @docs-private
         */
        this.pageSizeChange = new EventEmitter();
        /**
         * Whether the table columns can be resizable
         * @default false
         */
        this.resizable = false;
        /**
         * the table extra theme, i.e. when theme is 'small', an extra class `nb-table-small`
         * will be appended to the host className property
         * @default ''
         */
        this.theme = '';
        /**
         * Whether the table data is loading
         */
        this.loading = false;
        /**
         * current resize column index when resizing table columns
         */
        this._columnIndex = 0;
        /**
         * whether the table is resizing column
         */
        this._isDragging = false;
        /**
         * the distance when resizing column, if < 0, then the current column width will decrease _dx
         * and the next sibling column width will increase _dx
         */
        this._distance = 0;
        this.listenSortParamChange();
        this.listenDatasourceChange();
    }
    TableComponent.prototype.startColumnResizing = function () {
        this._isDragging = true;
        this._cd.markForCheck();
        this.bindMouseEvents();
    };
    TableComponent.prototype.bindMouseEvents = function () {
        var _this = this;
        this._ngZone.runOutsideAngular(function () {
            var el = _this._el.nativeElement;
            _this._mouseMoveListener = _this._render.listen(el, 'mousemove', _this.onMouseMove.bind(_this));
        });
    };
    TableComponent.prototype.unbindMouseEvents = function () {
        if (this._mouseMoveListener) {
            this._mouseMoveListener();
            this._mouseMoveListener = null;
        }
    };
    /**
     * listen sort related param change
     * @docs-private
     */
    TableComponent.prototype.listenSortParamChange = function () {
        var _this = this;
        var self = this;
        self.orderChange.subscribe(function (order) {
            if (self._headItems) {
                // reset other head item order status
                self._headItems.forEach(function (v) {
                    v.order = (v.field !== self.orderBy ? '' : order);
                });
                // emit sort param
                self.sort.emit({
                    order: order,
                    orderBy: self.orderBy
                });
            }
        });
        self.pageChange.subscribe(function () {
            _this.data = _this.getDisplayData();
        });
    };
    TableComponent.prototype.listenDatasourceChange = function () {
        var _this = this;
        this.datasourceChange.subscribe(function (datasource) {
            if (datasource === void 0) { datasource = []; }
            if (datasource) {
                _this.data = _this.getDisplayData();
            }
        });
    };
    TableComponent.prototype.ngOnInit = function () {
        this.data = this.getDisplayData();
    };
    TableComponent.prototype.getDisplayData = function () {
        var data = (this.datasource || []).slice();
        var orderBy = this.orderBy;
        var order = this.order;
        var page = this.page;
        var pageSize = this.pageSize;
        var len = data.length;
        if (order && orderBy) {
            data = data.sort(sortFunc(order, orderBy));
        }
        if (page && pageSize) {
            data = data.slice((page - 1) * pageSize, page * pageSize);
        }
        this.dataChange.emit(data);
        return data;
    };
    TableComponent.prototype.ngAfterContentInit = function () {
        // set child item index
        if (this._headItems) {
            this._headItems.forEach(function (v, columnIndex) {
                v._columnIndex = columnIndex;
            });
        }
    };
    /**
     * get table native elment position(left && top)
     * @docs-private
     */
    TableComponent.prototype.getTablePosition = function () {
        try {
            var el = this._el.nativeElement;
            // avoid frequently computing table positions
            if (!this._tablePos) {
                this._tablePos = {
                    left: el.offsetLeft,
                    top: el.offsetTop
                };
            }
            return this._tablePos;
        }
        catch (e) {
            throw new Error('get table position error');
        }
    };
    /**
     * set column resize start position
     * @param {number} left - left position relative to the table
     * @param {number} columnIndex - currently resize column
     * @docs-private
     */
    TableComponent.prototype.setColumeResizeStartPosition = function (left, columnIndex) {
        this._startPos = left;
        this._columnIndex = columnIndex;
        this.updateResizeLinePosition(left);
    };
    /**
     * update resize indicator line
     * @param {number} left - the left position of indicator line relative to the table
     * @docs-private
     */
    TableComponent.prototype.updateResizeLinePosition = function (left) {
        var el = this._resizeLine.nativeElement;
        this._render.setStyle(el, 'left', left + 'px');
    };
    /**
     * table mousemove event, update resize indicator line position
     * @param {MouseEvent} event - mouse event
     * @docs-private
     */
    TableComponent.prototype.onMouseMove = function (event) {
        if (this._isDragging) {
            // this._ngZone.runOutsideAngular(() => {
            // table left position
            var pos = this.getTablePosition();
            // current end position relative to the table
            var endPos = event.clientX - pos.left;
            // current end position relative to the table subtracts start position relative to the table
            this._distance = endPos - this._startPos;
            // update resize indicator line position to the end position
            this.updateResizeLinePosition(endPos);
            // });
        }
        // it's important here, and it will prevent default behavior like copy content from table
        return false;
    };
    /**
     * talbe mouseup event, when not resize column, do nothing
     * when resize column, update column width and set resizing flag to false
     * @docs-private
     */
    TableComponent.prototype.onMouseUp = function () {
        var _this = this;
        if (this._isDragging) {
            this._ngZone.runOutsideAngular(function () {
                _this.updateTableHeadItemWidth();
                _this.unbindMouseEvents();
            });
            this._isDragging = false;
        }
    };
    /**
     * get origin column width array
     * @return {number[]} column width array
     * @docs-private
     */
    TableComponent.prototype.getOriginHeadItemWidth = function () {
        var columnWidthArray = [];
        if (this._headItems) {
            this._headItems.forEach(function (v, columnIndex) {
                columnWidthArray[columnIndex] = v.getElementWidth();
            });
            return columnWidthArray;
        }
        return [];
    };
    /**
     * when column resize is done, update table head item width
     * @docs-private
     */
    TableComponent.prototype.updateTableHeadItemWidth = function () {
        // set newer column width array
        var newColWidths = this.getNewColumnWidth();
        if (this._headItems) {
            this._headItems.forEach(function (v, columnIndex) {
                v.setElementWidth(newColWidths[columnIndex]);
            });
        }
    };
    /**
     * when column resize is done, compute the new column width array
     * @docs-private
     */
    TableComponent.prototype.getNewColumnWidth = function () {
        var self = this;
        // cache distance
        var distance = self._distance;
        // get origin column width array
        var colWidths = self.getOriginHeadItemWidth();
        // set newer column width array
        var newColWidths = [];
        // compute newer colum width
        colWidths.forEach(function (width, columnIndex) {
            // if is current column, add the distance, for example
            // drag to left 10px distance will be 10, drag to right 10px distance will be -10
            if (columnIndex === self._columnIndex) {
                newColWidths[columnIndex] = width + distance;
            }
            // if is next sibling column, substract the distance
            else if (columnIndex === self._columnIndex + 1) {
                newColWidths[columnIndex] = width - distance;
            }
            // other column width will remain the same
            else {
                newColWidths[columnIndex] = width;
            }
        });
        return newColWidths;
    };
    TableComponent.prototype.ngAfterViewInit = function () {
        if (this.theme) {
            addClass(this._el.nativeElement, "nb-table-" + this.theme);
        }
    };
    TableComponent.prototype.ngOnDestroy = function () {
        this.unbindMouseEvents();
    };
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], TableComponent.prototype, "sort", void 0);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], TableComponent.prototype, "dataChange", void 0);
    tslib_1.__decorate([
        OnChange(false),
        Input(),
        tslib_1.__metadata("design:type", Array)
    ], TableComponent.prototype, "datasource", void 0);
    tslib_1.__decorate([
        OnChange(true),
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], TableComponent.prototype, "bordered", void 0);
    tslib_1.__decorate([
        OnChange(false, true),
        Input(),
        tslib_1.__metadata("design:type", String)
    ], TableComponent.prototype, "order", void 0);
    tslib_1.__decorate([
        OnChange(false, true),
        Input(),
        tslib_1.__metadata("design:type", String)
    ], TableComponent.prototype, "orderBy", void 0);
    tslib_1.__decorate([
        OnChange(),
        Input(),
        tslib_1.__metadata("design:type", Number)
    ], TableComponent.prototype, "page", void 0);
    tslib_1.__decorate([
        OnChange(),
        Input(),
        tslib_1.__metadata("design:type", Number)
    ], TableComponent.prototype, "pageSize", void 0);
    tslib_1.__decorate([
        OnChange(true),
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], TableComponent.prototype, "resizable", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], TableComponent.prototype, "theme", void 0);
    tslib_1.__decorate([
        OnChange(true),
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], TableComponent.prototype, "loading", void 0);
    tslib_1.__decorate([
        ContentChildren(forwardRef(function () { return TableHeaderItemComponent; }), { descendants: true }),
        tslib_1.__metadata("design:type", QueryList)
    ], TableComponent.prototype, "_headItems", void 0);
    tslib_1.__decorate([
        ViewChild('resizeLine', { static: false }),
        tslib_1.__metadata("design:type", ElementRef)
    ], TableComponent.prototype, "_resizeLine", void 0);
    TableComponent = tslib_1.__decorate([
        Component({
            selector: 'table[nb-table]',
            template: "<ng-content></ng-content><div #resizeLine class=\"nb-table-resize-line\" [class.hide]=\"!_isDragging\"></div><div *ngIf=\"loading\" class=\"nb-table-loading-layer\"><div class=\"loading-content\"><i class=\"iconfont icon-loading\"></i></div></div>",
            encapsulation: ViewEncapsulation.None,
            changeDetection: ChangeDetectionStrategy.OnPush,
            preserveWhitespaces: false,
            host: {
                'class': 'nb-widget nb-table',
                '[class.nb-table-bordered]': 'bordered',
                '[class.nb-table-resizable]': 'resizable',
                '(mouseup)': 'onMouseUp()'
            },
            exportAs: 'nbTable'
        }),
        tslib_1.__metadata("design:paramtypes", [ElementRef,
            Renderer2,
            NgZone,
            ChangeDetectorRef])
    ], TableComponent);
    return TableComponent;
}());
export { TableComponent };
//# sourceMappingURL=table.js.map