@true-directive/grid
Version:
Angular Data Grid from Yopsilon.
1,081 lines • 137 kB
JavaScript
import * as tslib_1 from "tslib";
/**
* Copyright (c) 2018-2019 Aleksey Melnikov, True Directive Company.
* @link https://truedirective.com/
* @license MIT
*/
/*
View only. Missed features:
- Checkboxes.
- Editing.
- Selection.
*/
import { Component, Input, Output, ViewChild, ViewChildren, ContentChildren, ElementRef, QueryList, ChangeDetectorRef, KeyValueDiffers, EventEmitter, Inject } from '@angular/core';
import { Subject, Observable } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { RenderMode, DetectionMode, ColumnType } from '@true-directive/base';
import { CellPosition, GridSettings, RowLayout, SortInfo, SortType, MenuAction, LazyLoadingMode } from '@true-directive/base';
import { PagePipe, RowCalculator } from '@true-directive/base';
import { ScrollerComponent } from './scroller.component';
import { BaseComponent } from './base.component';
import { RowDirective } from './row.directive';
import { GridStateService } from './grid-state.service';
import { InternationalizationService } from './internationalization/internationalization.service';
import { FilterTextComponent } from './filters/datatypes/filter-text.component';
import { FilterDateComponent } from './filters/datatypes/filter-date.component';
import { FilterNumberComponent } from './filters/datatypes/filter-number.component';
import { FilterBooleanComponent } from './filters/datatypes/filter-boolean.component';
import { FilterPopupComponent } from './filters/filter-popup.component';
import { MenuStarterComponent } from './controls/menu-starter.component';
var GridViewComponent = /** @class */ (function (_super) {
tslib_1.__extends(GridViewComponent, _super);
function GridViewComponent(state, intl, elementRef, changeDetector, keyValueDiffers) {
var _this = _super.call(this) || this;
_this.state = state;
_this.intl = intl;
_this.elementRef = elementRef;
_this.changeDetector = changeDetector;
_this.keyValueDiffers = keyValueDiffers;
_this.destroy$ = new Subject();
_this._data = null;
/**
* The maximum grid height, unless explicitly specified.
*/
_this.maxHeight = null;
/**
* Event that will be triggered when data retrieval options are changed (filters,
* sortings)
* @param 'dataQuery' [description]
* @return [description]
*/
_this.dataQuery = new EventEmitter();
_this.queryChanged = new EventEmitter();
_this.startProcess = new EventEmitter();
_this.endProcess = new EventEmitter();
_this.headerContextMenu = new EventEmitter();
_this.customCellEvent = new EventEmitter();
_this.menuAction = new EventEmitter();
_this.uiAction = null;
_this._initialized = false; // Grid initialized
_this._viewInitialized = false; // View initialized
_this._lastUpdateTime = 0; // Last page refresh time
// Position and time of last scroll
_this._lastScroll = { pos: -1, renderedPos: -1, time: 0 };
_this.dataUpdating = false; // Updating data in progress
// Page processing required
_this.need_recalc_page = true;
_this._prevOffset = -1;
_this._prevLimit = -1;
/**
* Current appearance css-class
*/
_this._currentAppearance = '';
/**
* List of rows to be rendered.
*/
_this._currentRendered = [];
_this.viewPortLeft = -1;
_this.viewPortWidth = -1;
_this.RC = new RowCalculator(_this.state);
_this._inProcess = false;
// Locale was changed
_this.intl.onLocaleChanged.pipe(takeUntil(_this.destroy$)).subscribe(function (l) {
if (_this._viewInitialized) {
_this.detectChanges('locale');
}
});
// Запрос данных у слушателя события
_this.state.events.onDataQuery.pipe(takeUntil(_this.destroy$)).subscribe(function (q) {
_this.dataUpdating = true;
_this.dataQuery.emit(q);
});
// Данные получены - отображаем
_this.state.events.onDataFetch.pipe(takeUntil(_this.destroy$)).subscribe(function (q) {
if (_this._viewInitialized) {
if (q.resetData) {
_this.scrollToTop();
}
_this.renderData();
}
});
// Изменены фильтры или сортировка или набор колонок или группировка
_this.state.events.onQueryChanged.pipe(takeUntil(_this.destroy$)).subscribe(function (q) {
if (_this._initialized) {
_this.updateData();
}
/*
Мы будем запрашивать здесь запрос. Потому что может быть ленивая загрузка хочет
его подправить.
Нет, его нельзя запросить, потому что у него уже идентификатор, с которым мы запросили
и он для нас важен */
_this.queryChanged.emit(_this.state.getQuery());
});
// Следует отобразить окно фильтра для заданной колонки
_this.state.events.onFilterShow.pipe(takeUntil(_this.destroy$)).subscribe(function (e) { return _this.showFilter(e); });
// Изменение списка суммирований колонки
_this.state.events.onSummariesChanged.pipe(takeUntil(_this.destroy$)).subscribe(function (v) {
_this.state.updateSummaries();
_this.updateView();
});
_this.state.events.onHeaderContextMenu.pipe(takeUntil(_this.destroy$)).subscribe(function (e) {
// Это нужно вынести куда-нибудь подальше
if (_this.state.settings.enableHeaderContextMenu) {
var actions = _this.state.settings.headerContextMenuActions;
var sorted_1 = _this.state.dataSource.sortedByField(e.column.fieldName);
if (actions.length > 0) {
actions.forEach(function (a) {
if (a === MenuAction.SORT_ASC) {
a.disabled = sorted_1 && sorted_1.sortType === SortType.ASC;
}
if (a === MenuAction.SORT_DESC) {
a.disabled = sorted_1 && sorted_1.sortType === SortType.DESC;
}
});
_this.menuStarter.start(e.event, _this.state.settings.headerContextMenuActions, e.column);
_this.detectChanges();
e.event.preventDefault();
}
}
_this.headerContextMenu.emit(e);
});
// Custom cell event
_this.state.events.onCustomCellEvent.pipe(takeUntil(_this.destroy$)).subscribe(function (e) {
_this.customCellEvent.emit(e);
});
return _this;
}
Object.defineProperty(GridViewComponent.prototype, "data", {
/**
* Data source
* @return Array of the rows or observable object
*/
get: function () {
return this._data !== null ? this._data : this.state.model;
},
/**
* Grid data source. You can specify an observable object or array.
*/
set: function (ds) {
var _this = this;
// Unsubscribe from previous source
if (this._data !== null && ds === this._data) {
return;
}
if (this._data !== null) {
this._dataSubscription.unsubscribe();
this._data = null;
}
if (ds instanceof Observable) {
// Subscribe for new data
this._data = ds;
this._dataSubscription = this._data
.pipe(takeUntil(this.destroy$))
.subscribe(function (data) {
_this.state.model = data;
_this.updateData();
});
}
else {
// Just array
if (this.state.model !== ds) {
this.state.model = ds;
if (this._initialized) {
this.updateData();
}
}
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(GridViewComponent.prototype, "rows", {
/**
* List of the rows to display
* @return Array of the rows
*/
get: function () {
return this.state.model;
},
enumerable: true,
configurable: true
});
Object.defineProperty(GridViewComponent.prototype, "resultRows", {
/**
* Resulting list of rows after applying filters and sorting
* @return Array of the rows
*/
get: function () {
return this.state.dataSource.resultRows;
},
enumerable: true,
configurable: true
});
Object.defineProperty(GridViewComponent.prototype, "locale", {
/**
* Current locale name
*/
get: function () {
return this.intl.currentLocaleName;
},
enumerable: true,
configurable: true
});
Object.defineProperty(GridViewComponent.prototype, "columns", {
get: function () {
return this.state.columns;
},
/**
* List of the columns
*/
set: function (v) {
this.state.columns = v;
},
enumerable: true,
configurable: true
});
Object.defineProperty(GridViewComponent.prototype, "settings", {
get: function () {
return this.state.settings;
},
/**
* Grid's settings
*/
set: function (v) {
this.state.settings = v;
},
enumerable: true,
configurable: true
});
Object.defineProperty(GridViewComponent.prototype, "searchString", {
/**
* Text search data for all columns
*/
get: function () {
return this.state.dataSource.searchString;
},
set: function (v) {
var _this = this;
if (this.state.dataSource.searchString !== v) {
this.state.dataSource.searchString = v;
// A pause is necessary so that when printing quickly, do not perform
// too frequent updates.
setTimeout(function () {
if (_this.state.dataSource.searchString === v) {
_this.updateData();
}
}, this.state.settings.searchDelay);
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(GridViewComponent.prototype, "displayedRows", {
get: function () {
if (this.state.settings.customTemplate) {
return this.displayedRows_template;
}
return this.displayedRowsCenter;
},
enumerable: true,
configurable: true
});
Object.defineProperty(GridViewComponent.prototype, "selection", {
get: function () {
return this.state.selection;
},
enumerable: true,
configurable: true
});
GridViewComponent.prototype._headerParts = function () {
return [this.gridHeader];
};
Object.defineProperty(GridViewComponent.prototype, "headerParts", {
get: function () {
return this._headerParts();
},
enumerable: true,
configurable: true
});
GridViewComponent.prototype._dataParts = function () {
return [this.displayedRowsCenter];
};
Object.defineProperty(GridViewComponent.prototype, "dataParts", {
get: function () {
return this._dataParts();
},
enumerable: true,
configurable: true
});
GridViewComponent.prototype.immediateFilter = function (filter) {
this.state.dataSource.searchString = filter;
this.updateData();
};
Object.defineProperty(GridViewComponent.prototype, "visibleRows", {
/**
* List of the rows to render
* @return Array of the rows
*/
get: function () {
if (!this.state.dataSource.resultRows) {
return [];
}
if (this._prevOffset !== this.state.pageInfo.offset ||
this._prevLimit !== this.state.pageInfo.limit ||
this.need_recalc_page) {
// Page refresh
this.state.lazyLoader.query(this.state.pageInfo.offset);
var toRender = new PagePipe().transform(this.state.dataSource.resultRows, this.state.pageInfo);
this._prevOffset = this.state.pageInfo.offset;
this._prevLimit = this.state.pageInfo.limit;
this._currentRendered = toRender;
this.need_recalc_page = false;
// Important!
this.state.displayedStartIndex = this.state.pageInfo.offset;
// Если мы.. Доскроллили до предела...
// Надо бы запросить еще данные.
}
return this._currentRendered;
},
enumerable: true,
configurable: true
});
// Keeping the height of all rows
GridViewComponent.prototype.saveRowHeights = function () {
var _this = this;
this.displayedRowsCenter.forEach(function (el) {
var hh;
if (_this.state.IE) {
hh = el.elementRef.nativeElement.getBoundingClientRect().height;
}
else {
hh = el.elementRef.nativeElement.clientHeight;
}
_this.RC.setRowHeight(_this.state.dataSource.resultRowCount, el.axI + _this.state.pageInfo.offset, hh);
});
};
/**
* Page refresh
* @param forceChanges Force update even if there was no scrolling
* @param overwork The number of rows that will be rendered outside the viewport
*/
GridViewComponent.prototype.updatePage = function (log, forceChanges, overwork) {
if (log === void 0) { log = ''; }
if (forceChanges === void 0) { forceChanges = false; }
if (overwork === void 0) { overwork = null; }
// Это надо перекинуть в состояние
if (this.viewPortLeft !== this.scroller.scrollLeft || this.viewPortWidth !== this.scroller.viewPortWidth) {
this.viewPortLeft = this.scroller.scrollLeft;
this.viewPortWidth = this.scroller.viewPortWidth;
forceChanges = true;
}
if (!forceChanges) {
this.saveRowHeights();
}
var rc = this.state.dataSource.resultRowCount;
if (this.state.st.lazyLoading === LazyLoadingMode.FRAGMENTARY) {
rc = this.state.dataSource.totalRowCount;
}
var pageChanged = this.RC.updateRenderInfo(rc, this.scroller.scrollTop, this.scroller.viewPortHeight, overwork);
if (forceChanges || pageChanged) {
this.detectChanges('page');
}
};
/**
* Force view update
*/
GridViewComponent.prototype.updateView = function () {
// First we need to update all view params
this.detectChanges('view');
this.state.updateLayouts();
// Then the current page. Because its calculation depends on these parameters
this.updatePage('updateView', true);
};
GridViewComponent.prototype.cellPosition = function (row, fieldName) {
var ri = this.resultRows.indexOf(row);
return new CellPosition(row, ri, fieldName);
};
GridViewComponent.prototype.startSelect = function (cp, add) {
if (add === void 0) { add = false; }
this.state.ui.startSelect(cp, add);
};
GridViewComponent.prototype.proceedToSelect = function (cp) {
this.state.ui.proceedToSelect(cp);
};
/**
* Hide all buttons in column headers
*/
GridViewComponent.prototype.hideHeaderBtns = function () {
this.headerParts.forEach(function (p) {
p.hideHeaderBtns();
});
};
/**
* Asynchronous data update. The Observable returns, by subscribing to which
* you can find out when this update ended.
* Use this method with lazy loading settings.
* @param data The data (array of objects).
* @return Observable object whose event will occur immediately after processing.
*/
GridViewComponent.prototype.updateDataAsync = function (data) {
var _this = this;
this.data = data;
this.startProcess.emit('DataUpdate');
this.elementRef.nativeElement.classList.add('processing');
var subj = this.state.updateDataAsync();
subj.subscribe(function (e) {
_this.endProcess.emit('DataUpdate');
});
return subj;
};
/**
* Updating data with external or internal change.
*/
GridViewComponent.prototype.updateData = function (async) {
var _this = this;
if (async === void 0) { async = true; }
if (!this._initialized) {
async = false;
}
// The changes are serious. Row heights update.
this.RC.clear();
// Through a timeout, so that when the filter is applied, the button does
// not have time to turn off before it is shown with an accent color
// setTimeout(() => this.hideHeaderBtns());
if (async) {
this.startProcess.emit('DataUpdate');
this.elementRef.nativeElement.classList.add('processing');
setTimeout(function () {
_this.state.updateData();
_this.endProcess.emit('DataUpdate');
}, 1);
}
else {
this.state.updateData(async);
}
};
GridViewComponent.prototype.updateSummaries = function () {
this.state.updateSummaries();
this.updateView();
};
/**
* Received data externally (from the parent component)
* @param query The request in response to which the data received.
* @param data Data that received in response to a given request.
*/
GridViewComponent.prototype.fetchData = function (query, data, totalRowCount) {
if (totalRowCount === void 0) { totalRowCount = null; }
this.state.fetchData(query, data, totalRowCount);
};
GridViewComponent.prototype.renderData = function () {
var _this = this;
setTimeout(function () { return _this.hideHeaderBtns(); });
this.dataUpdating = false;
this.elementRef.nativeElement.classList.remove('processing');
this.need_recalc_page = true;
// Sent for further processing (aggregation, grouping)
// Refreshing page with forced detectChanges
this.updatePage('renderData', true);
// After adding data, the width of the client part of the grid may change
// due to the appearance of a scrollbar. Therefore, check the width of the grid.
if (this.state.settings.columnAutoWidth) {
this.checkSize();
}
// The height of the data changed, but the scroll position does not.
// Therefore, scroll the related parts.
this.scroller.scrollParts();
};
GridViewComponent.prototype.updatePageByScroll = function () {
this.updatePage('scroll', false);
this._lastUpdateTime = Date.now();
};
/**
* Scrolling data
* @param e Scroll event
*/
GridViewComponent.prototype.gridScroll = function (e) {
var _this = this;
var scrollPos = e.target.scrollTop;
var scrollPosH = e.target.scrollLeft;
var dscrollH = scrollPosH - this.viewPortLeft;
var dscroll = scrollPos - this._lastScroll.renderedPos;
this._lastScroll.pos = scrollPos;
this.RC.currentScrollPos = scrollPos;
var needUpdate = false;
if (Math.abs(dscroll) > this.RC.currentRH * 2) {
// We have a stock of two lines. If the scroll is larger, then update.
needUpdate = true;
}
if (Math.abs(dscrollH) > 1 && this.state.settings.renderMode === RenderMode.VISIBLE) {
// When scrolling horizontally only if RenderMode.VISIBLE
needUpdate = true;
}
if (!needUpdate) {
// заменим на явное задание margin-top у
return;
}
if (this._inProcess && this.state.iOS) {
return;
}
if (scrollPosH < 0) {
return;
}
if (scrollPos < 0 || scrollPos > (this.scroller.scrollHeight - this.scroller.viewPortHeight)) {
return;
}
this._inProcess = true;
var dt = Date.now();
this._lastScroll.renderedPos = scrollPos;
this._lastScroll.time = dt;
var ms = dt - this._lastUpdateTime;
var dms = this.state.IE ? 40 : 10;
if (ms < dms && !this.state.safari) {
// Delaying
var delay = dms * 4;
setTimeout(function () {
if (_this._lastScroll.time === dt) {
_this.updatePageByScroll();
}
}, delay);
this._inProcess = false;
return;
}
this.updatePageByScroll();
this._inProcess = false;
};
/**
* Triggered by automatic scrolling during area selection
* @param dx How many pixels are scrolled horizontally?
*/
GridViewComponent.prototype.gridAutoScrollX = function (dx) {
this.gridHeader.autoScrollX(dx);
};
/**
* We need call this method for the changes to take effect.
* If dataAffected=true then re-filter and re-sort data.
* @param log Reason for detecting changes
* @param dataAffected Is the data changed
*/
GridViewComponent.prototype.detectChanges = function (log, dataAffected) {
if (log === void 0) { log = ''; }
if (dataAffected === void 0) { dataAffected = false; }
if (dataAffected) {
this.updateData();
return;
}
if (this.state.settings.changeDetectionMode === DetectionMode.MANUAL) {
// Otherwise, it will check the changes. And we won’t pull the detector again..
this.changeDetector.detectChanges();
}
};
GridViewComponent.prototype.displayedRowsByXY = function (x, y, place) {
if (place === void 0) { place = null; }
return this.displayedRows;
};
GridViewComponent.prototype.rowLayouts = function (rows) {
var result = [];
var firstRow = rows.first;
if (!firstRow) {
return result;
}
var ri = this.state.pageInfo.offset;
rows.forEach(function (el) {
var rl = new RowLayout();
rl.index = ri;
rl.rowComponent = el;
rl.clientRect = el.elementRef.nativeElement.getBoundingClientRect();
result.push(rl);
ri++;
});
return result;
};
GridViewComponent.prototype.rowByXY = function (rows, x, y) {
return RowLayout.rowByXY(this.rowLayouts(rows), x, y);
};
GridViewComponent.prototype.cellByXY = function (x, y, place) {
if (place === void 0) { place = null; }
var rows = this.displayedRowsByXY(x, y, place);
var r = this.rowByXY(rows, x, y);
if (r && r.rowComponent) {
return this.state.ui.cellPosition(r.rowComponent.row, r.index, r.rowComponent.cellByXY(x, y));
}
return null;
};
// Перерисовка выделенного
GridViewComponent.prototype.refreshSelection = function (scrollTo) {
this.dataParts.forEach(function (p) {
return p && p.forEach(function (el) { return el.setSelection(); });
});
if (scrollTo) {
this.scrollTo(scrollTo);
}
};
/**
* Прокрутить скроллбоксы так, чтобы была видна ячейка, на которой находится
* виртуальный фокус
*/
GridViewComponent.prototype.scrollToFocused = function () {
if (this.state.selection.focusedCell) {
this.scrollTo(this.state.selection.focusedCell);
}
};
/**
* Прокрутка к указанной ячейке.
* Обычно вызываем после обработки нажатия клавиши, чтобы была видна строка
* с фокусом.
* Также вызывается после клика мышью по ячейке.. Если ячейка видна на экране
* только частично - немного скроллим.
* @param cellPos Позиция ячейки, к которой прокручиваем грид
*/
GridViewComponent.prototype.scrollTo = function (cellPos) {
var _this = this;
if (!cellPos) {
return;
}
// Будем считать, что нам нужна только ячейка с фокусом
// Сохраним высоты строк.. Т.к. они могут быть еще не сохранены,
// если не случилось прокрутки
this.saveRowHeights();
var ri = cellPos.rowIndex;
var fieldName = cellPos.fieldName;
// сначала по вертикали
var scrollTop = this.scroller.scrollTop;
var scrollLeft = this.scroller.scrollLeft;
var scrollAreaHeight = this.scroller.viewPortHeight;
var scrollAreaWidth = this.scroller.viewPortWidth;
var rowTop = this.RC.getRowTop(ri);
var rowBottom = rowTop + this.RC.getRowHeight(ri);
if (rowTop < scrollTop) {
this.scroller.scrollTo(-1, rowTop);
}
else {
if (rowBottom > (scrollTop + scrollAreaHeight)) {
this.scroller.scrollTo(-1, rowBottom - scrollAreaHeight);
}
}
// Теперь по горизонтали
// Возьмем строку
this.displayedRowsCenter.some(function (row) {
if (row.row === cellPos.row) {
var hPos = row.cellHorizontalPos(fieldName);
if (hPos && hPos.l < scrollLeft) {
_this.scroller.scrollTo(hPos.l);
}
if (hPos && hPos.r > scrollLeft + scrollAreaWidth) {
_this.scroller.scrollTo(hPos.r - scrollAreaWidth);
}
return true;
}
return false;
});
};
GridViewComponent.prototype.scrollToTop = function () {
this.scroller.scrollTo(-1, 0);
};
GridViewComponent.prototype.toggleClass = function (v, c) {
if (v) {
this.elementRef.nativeElement.classList.add(c);
}
else {
this.elementRef.nativeElement.classList.remove(c);
}
};
/**
* Проверить, нужно ли отобразить левую или правую область для зафиксированных
* колонок
* @param xx [description]
* @param scrollRect [description]
* @param target [description]
* @return [description]
*/
GridViewComponent.prototype.checkParts = function (xx, scrollRect, target) {
// Нечего проверять в этой реализации
};
/**
* Выделение заданной строки
* @param r Заданная строка
* @return Найдена ли заданная строка в списке отображаемых строк
*/
GridViewComponent.prototype.locateRow = function (r) {
return this.state.locateRow(r);
};
/**
* Выделение строки по заданному значению ключевого поля
* @param keyValue Значение ключевого поля
* @param keyField Ключевое поле. Если не задано, то поле берется из settings
* @return Найдена ли строка с заданным ключом
*/
GridViewComponent.prototype.locateByKey = function (keyValue, keyField) {
if (keyField === void 0) { keyField = ''; }
this.state.locateByKey(keyValue, keyField);
};
/**
* Очистка выделения
*/
GridViewComponent.prototype.clearSelection = function () {
this.state.clearSelection();
};
/**
* Трек отображаемых записей для более быстрого рендера ангуляром
* @param index Индекс строки
* @param data Данные строки
*/
GridViewComponent.prototype.trackRow = function (index, data) {
return data;
};
/**
* При изменении размеров окна вызывается этот метод.
* Если размер компонента изменяется какими-то действиями пользователя помимо
* изменения размеров окна браузера (вьюпорта), то необходимо вызывать этот
* метод.
* Этот метод также следует вызывать при drop колонки, т.к. размер центральной
* области меняется, если колонка переброшена в левую или правую фиксированную
* область.
* @param update_page=false [description]
* @return [description]
*/
GridViewComponent.prototype.checkSize = function (update_page) {
// Указываем ширину клиентской области.
// Если она изменена, то в сеттере будет вызван updateLayouts
if (update_page === void 0) { update_page = false; }
if (this.state.checkClientWidth(this.scroller.viewPortWidth) ||
this.state.checkClientHeight(this.scroller.viewPortHeight)) {
this.need_recalc_page = true;
if (this._initialized) {
this.updatePage('checkSize', true);
}
return true;
}
if (update_page) {
this.updatePage('checkSize2', true);
}
return false;
};
// Изменение размера окна
GridViewComponent.prototype.windowResize = function (e) {
this.checkSize(true);
};
/**
* Показать кнопку заголовка для колонки по заданному полю
* @param fieldName Заданное поле
*/
GridViewComponent.prototype.showHeaderBtn = function (fieldName) {
this.headerParts.forEach(function (p) {
p.showHeaderBtn(fieldName);
});
};
/**
* Data sorting
* @param sortings List of sortings
*/
GridViewComponent.prototype.sort = function (sortings, update) {
if (update === void 0) { update = true; }
this.state.sort(sortings, update);
};
GridViewComponent.prototype.clearSorting = function (update) {
if (update === void 0) { update = true; }
this.state.sort([], update);
};
/**
* Data filtering
*/
GridViewComponent.prototype.filter = function (filters, update) {
if (update === void 0) { update = true; }
this.state.filter(filters, update);
};
GridViewComponent.prototype.filterToString = function (filter) {
return filter.toString(this.intl, this.state.dataSource.valueFormatter);
};
GridViewComponent.prototype.filterClosed = function (result) {
if (!result) {
this.hideHeaderBtns();
}
};
GridViewComponent.prototype.setFilter = function (f) {
this.state.setFilter(f);
};
GridViewComponent.prototype.resetFilter = function (f) {
this.state.resetFilter(f);
};
GridViewComponent.prototype.getFilterComponentType = function (filter) {
var filterType = FilterTextComponent;
if (filter.type === ColumnType.NUMBER) {
filterType = FilterNumberComponent;
}
if (filter.type === ColumnType.DATETIME) {
filterType = FilterDateComponent;
}
if (filter.type === ColumnType.BOOLEAN) {
filterType = FilterBooleanComponent;
}
var col = this.state.columnByFieldName(filter.fieldName);
if (!col) {
return null;
}
if (col.filterComponentType !== null) {
filterType = col.filterComponentType;
}
return filterType;
};
GridViewComponent.prototype.showFilter = function (e) {
var _this = this;
if (this.menuStarter) {
this.menuStarter.finish();
}
var l = e.target.tagName === 'SPAN' ? e.target.parentElement : e.target;
if (this.filterPopup.visible) {
if (this.filterPopup.filter.fieldName === e.filter.fieldName) {
this.filterPopup.closePopup();
return;
}
else {
this.filterPopup.closePopup();
}
}
this.hideHeaderBtns();
this.showHeaderBtn(e.filter.fieldName);
var filterType = this.getFilterComponentType(e.filter);
if (filterType !== null) {
setTimeout(function () {
_this.filterPopup.showByTarget(l, e.filter, filterType, _this.state.model);
}, 10);
}
};
GridViewComponent.prototype.focus = function () {
this.scroller.focus();
};
Object.defineProperty(GridViewComponent.prototype, "appearanceClass", {
get: function () {
return this.state.sta.class;
},
enumerable: true,
configurable: true
});
/**
* Установка класса внешнего вида
* @param appearanceClass Класс, который будет применен к компоненту
*/
GridViewComponent.prototype.setAppearance = function () {
var appearanceClass = this.appearanceClass;
if (appearanceClass === this._currentAppearance) {
// Без изменений
return;
}
if (this._currentAppearance !== '') {
// Убираем добавленный
this.elementRef.nativeElement.classList.remove(this._currentAppearance);
}
this.elementRef.nativeElement.classList.add(appearanceClass);
this._currentAppearance = appearanceClass;
};
GridViewComponent.prototype.menuItemClick = function (e) {
var col = e.target;
var action = e.action;
if (action === MenuAction.SORT_ASC) {
this.sort([new SortInfo(col.fieldName, SortType.ASC)]);
return;
}
if (action === MenuAction.SORT_DESC) {
this.sort([new SortInfo(col.fieldName, SortType.DESC)]);
return;
}
if (action === MenuAction.HIDE) {
this.state.hideColumn(col);
return;
}
this.menuAction.emit(e);
};
GridViewComponent.prototype.ngOnInit = function () {
// Отключаем детектор по настройке.
// Без отключения всё работает, но много чего мелькает не вовремя..
if (this.state.settings.changeDetectionMode === DetectionMode.MANUAL) {
this.changeDetector.detach();
}
// Grid appearance
this.setAppearance();
if (this.state.iOS || this.state.android) {
this.elementRef.nativeElement.classList.add('true-fix-touch');
}
if (this.state.IE) {
this.elementRef.nativeElement.classList.add('true-fix-ie');
}
this._settingsDiffer = this.keyValueDiffers.find(this.state.settings).create();
this._appearanceDiffer = this.keyValueDiffers.find(this.state.settings.appearance).create();
this._settingsDiffer.diff(this.state.settings);
this._appearanceDiffer.diff(this.state.settings.appearance);
this.updateData();
this._initialized = true;
};
GridViewComponent.prototype.ngAfterContentInit = function () {
//
};
GridViewComponent.prototype.ngAfterViewInit = function () {
var _this = this;
// Сохраняем ширину
this.state.checkClientWidth(this.scroller.viewPortWidth);
// На этот момент может быть неизвестна высота грида. Которая нам очень нужна!
// Обычно это случается при сложной разметке, когда размер задан в процентах от
// родительского элемента.
var vh = this.scroller.viewPortHeight;
if (vh <= this.state.settings.rowHeight) {
// Это условие показывает, что что-то не так.
// Мы немного отложим первый рендер данных.
// Мы увидим белый экран перед тем как увидим данные.
setTimeout(function () { return _this.renderData(); });
}
else {
// Будем оптимистами. Возможно, нас спасёт ngDoCheck
this.renderData();
}
// Запоминаем текущую высоту вьюпорта
this.state.checkClientHeight(vh);
// Добавляем пассивные слушатели тач-событий
this.addTouchListeners(this.gridData.nativeElement);
// Следим за размером окна, чтобы дорендерить невидимые ранее данные
this.addWindowResizeListener();
this._viewInitialized = true;
};
GridViewComponent.prototype.doCheckParts = function () {
// Обновляем изменение
// Фильтры не виновaты, что у нас отключен детектор изменений
if (this.filterPopup && this.filterPopup.visible) {
// Поэтому даём знать об изменениях
this.filterPopup.changes();
}
};
GridViewComponent.prototype.ngDoCheck = function () {
this.doCheckParts();
// Сверяем настройки
var sChanges = this._settingsDiffer.diff(this.state.settings);
var aChanges = this._appearanceDiffer.diff(this.state.settings.appearance);
if (sChanges || aChanges) {
if (this._viewInitialized) {
this.setAppearance();
if (!this.checkSize()) {
this.updateView();
}
return;
}
}
// Сверяем высоту грида. Это немного затормаживает нас.
// Но дает гарантию, что данные будут вовремя отрисованы
if (this.state.checkClientHeight(this.scroller.viewPortHeight)
&& this._viewInitialized) {
this.updatePage();
}
};
GridViewComponent.prototype.ngOnDestroy = function () {
// Отписаться от событий
this.destroy$.next(true);
this.destroy$.unsubscribe();
_super.prototype.ngOnDestroy.call(this);
};
tslib_1.__decorate([
Input('data'),
tslib_1.__metadata("design:type", Object),
tslib_1.__metadata("design:paramtypes", [Object])
], GridViewComponent.prototype, "data", null);
tslib_1.__decorate([
Input('columns'),
tslib_1.__metadata("design:type", Array),
tslib_1.__metadata("design:paramtypes", [Array])
], GridViewComponent.prototype, "columns", null);
tslib_1.__decorate([
Input('settings'),
tslib_1.__metadata("design:type", GridSettings),
tslib_1.__metadata("design:paramtypes", [GridSettings])
], GridViewComponent.prototype, "settings", null);
tslib_1.__decorate([
Input('maxHeight'),
tslib_1.__metadata("design:type", Number)
], GridViewComponent.prototype, "maxHeight", void 0);
tslib_1.__decorate([
Input('searchString'),
tslib_1.__metadata("design:type", String),
tslib_1.__metadata("design:paramtypes", [String])
], GridViewComponent.prototype, "searchString", null);
tslib_1.__decorate([
Output('dataQuery'),
tslib_1.__metadata("design:type", EventEmitter)
], GridViewComponent.prototype, "dataQuery", void 0);
tslib_1.__decorate([
Output('queryChanged'),
tslib_1.__metadata("design:type", EventEmitter)
], GridViewComponent.prototype, "queryChanged", void 0);
tslib_1.__decorate([
Output(),
tslib_1.__metadata("design:type", EventEmitter)
], GridViewComponent.prototype, "startProcess", void 0);
tslib_1.__decorate([
Output(),
tslib_1.__metadata("design:type", EventEmitter)
], GridViewComponent.prototype, "endProcess", void 0);
tslib_1.__decorate([
Output(),
tslib_1.__metadata("design:type", EventEmitter)
], GridViewComponent.prototype, "headerContextMenu", void 0);
tslib_1.__decorate([
Output(),
tslib_1.__metadata("design:type", EventEmitter)
], GridViewComponent.prototype, "customCellEvent", void 0);
tslib_1.__decorate([
Output(),
tslib_1.__metadata("design:type", EventEmitter)
], GridViewComponent.prototype, "menuAction", void 0);
tslib_1.__decorate([
ViewChild('menuStarter', { static: true }),
tslib_1.__metadata("design:type", MenuStarterComponent)
], GridViewComponent.prototype, "menuStarter", void 0);
tslib_1.__decorate([
ViewChild('filterPopup', { static: false }),
tslib_1.__metadata("design:type", FilterPopupComponent)
], GridViewComponent.prototype, "filterPopup", void 0);
tslib_1.__decorate([
ViewChild('scroller', { static: true }),
tslib_1.__metadata("design:type", ScrollerComponent)
], GridViewComponent.prototype, "scroller", void 0);
tslib_1.__decorate([
ViewChild('grid', { static: true }),
tslib_1.__metadata("design:type", Object)
], GridViewComponent.prototype, "grid", void 0);
tslib_1.__decorate([
ViewChild('gridHeader', { static: true }),
tslib_1.__metadata("design:type", Object)
], GridViewComponent.prototype, "gridHeader", void 0);
tslib_1.__decorate([
ViewChild('gridData', { static: true }),
tslib_1.__metadata("design:type", Object)
], GridViewComponent.prototype, "gridData", void 0);
tslib_1.__decorate([
ViewChild('dragItem', { static: true }),
tslib_1.__metadata("design:type", Object)
], GridViewComponent.prototype, "dragItem", void 0);
tslib_1.__decorate([
ContentChildren(RowDirective),
tslib_1.__metadata("design:type", QueryList)
], GridViewComponent.prototype, "displayedRows_template", void 0);
tslib_1.__decorate([
ViewChildren('displayedRows', { read: RowDirective }),
tslib_1.__metadata("design:type", QueryList)
], GridViewComponent.prototype, "displayedRowsCenter", void 0);
tslib_1.__decorate([
ViewChild('customTemplate', { static: true }),
tslib_1.__metadata("design:type", Object)
], GridViewComponent.prototype, "customTemplate", void 0);
GridViewComponent = tslib_1.__decorate([
Component({
selector: 'true-grid-view',
template: "<true-menu-starter #menuStarter (itemClick)=\"menuItemClick($event)\"></true-menu-starter>\r\n<!-- Filter popup -->\r\n<true-filter-popup #filterPopup *ngIf=\"state.settings.allowFilter\"\r\n (setFilter)=\"setFilter($event)\"\r\n (resetFilter)=\"resetFilter($event)\"\r\n (closed)=\"filterClosed($event)\">\r\n</true-filter-popup>\r\n<!-- Scroll manager -->\r\n<true-scroller #scroller\r\n tabindex=\"0\"\r\n (autoscrollx)=\"gridAutoScrollX($event)\"\r\n (scroll)=\"gridScroll($event)\">\r\n <!-- \u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0438 -->\r\n <true-grid-header #gridHeader true-header\r\n [ngClass]=\"state.settings.appearance.headerAreaClass\"\r\n [layout]=\"state.layout\"\r\n [scroller]=\"scroller\">\r\n </true-grid-header>\r\n <div true-data [ngClass]=\"settings.appearance.getDataClass()\">\r\n <table #gridData class=\"true-grid-data\"\r\n [class.true-grid-data_fixed-height]=\"state.settings.fixedRowHeight\"\r\n [style.width]=\"state.layout.dataWidth\">\r\n <colgroup>\r\n <!-- Level columns -->\r\n <col *ngFor=\"let c of state.layout.levelColumns\" [style.width]=\"state.st.levelWidth\" />\r\n <!-- Data columns -->\r\n <col *ngFor=\"let c of state.layout.columns\" [style.width]=\"c.displayedWidthU\" />\r\n </colgroup>\r\n\r\n <tbody *ngIf=\"!state.st.customTemplate\">\r\n <tr *ngFor=\"let r of RC.ghostRows('start'); trackBy: RC.trackGhostRowStart\" [style.height.px]=\"r.H\" style=\"border:0\">\r\n <td [style.height.px]=\"r.H\" style=\"padding:0; border-right: 0;\"> </td>\r\n </tr>\r\n\r\n <tr *ngFor=\"let r of visibleRows; let i=index; trackBy: trackRow;\"\r\n true-row\r\n [row]=\"r\"\r\n [true-locale]=\"locale\"\r\n [true-layout]=\"state.layout\"\r\n [true-state]=\"state\"\r\n [true-i]=\"i\"\r\n [style.height.px]=\"state.settings.rowHeight\"\r\n #displayedRows>\r\n </tr>\r\n <tr *ngFor=\"let r of RC.ghostRows('end')\" [style.height.px]=\"r.H\">\r\n <td [style.height.px]=\"r.H\" style=\"padding:0; border-right: 0;\"> </td>\r\n </tr>\r\n <tr *ngIf=\"visibleRows.length === 0\"><td style=\"border: 0;\"></td></tr>\r\n </tbody>\r\n <!-- User's template -->\r\n <ng-content *ngIf=\"state.st.customTemplate\" select=\"[true-body]\"></ng-content>\r\n </table>\r\n </div>\r\n <!-- Footer -->\r\n <true-grid-footer #gridFooter true-footer [layout]=\"state.layout\"></true-grid-footer>\r\n</true-scroller>\r\n",
providers: [{ provide: 'gridState', useClass: GridStateService }],
styles: [":host{display:block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.true-grid{box-sizing:border-box;height:100%;width:100%}.true-grid-data{box-sizing:border-box;table-layout:fixed;border-spacing:0;border-collapse:collapse;outline:0}.true-grid-data td{overflow-x:hidden;overflow-y:hidden}.true-grid-drop-marker{box-sizing:border-box;visibility:hidden;position:fixed;width:3px;border:1px solid #757779;background-color:rgba(255,255,255,.05);pointer-events:none;z-index:7}.true-grid-data_fixed-height td:not(.true-cell-indent){white-space:nowrap}.true-grid-data_fixed-height td:not(.true-cell-indent):not(.true-cell-checkbox){text-overflow:ellipsis}.true-cell-checkbox.true-check-by-click{cursor:pointer}.true-grid-drag-item{visibility:hidden;position:fixed;overflow-x:hidden;text-overflow:ellipsis;pointer-events:none;z-index:8}::ng-deep .true-grid-btn{cursor:pointer;align-self:stretch;visibility:hidden;display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;-webkit-box-pack:center;justify-content:center;flex-shrink:0}::ng-deep .true-grid-btn span{display:inline-block}::ng-deep .true-align-right{text-align:right}::ng-deep .true-align-center{text-align:center}::ng-deep .true-align-left{text-align:left!important}"]
}),
tslib_1.__param(0, Inject('gridState')),
tslib_1.__metadata("design:paramtypes", [GridStateService,
InternationalizationService,
ElementRef,
ChangeDetectorRef,
KeyValueDiffers])
], GridViewComponent);
return GridViewComponent;
}(BaseComponent));
export { GridViewComponent };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZC12aWV3LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiJuZzovL0B0cnVlLWRpcmVjdGl2ZS9ncmlkLyIsInNvdXJjZXMiOlsic3JjL2dyaWQtdmlldy5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7O0VBSUU7QUFDRjs7Ozs7R0FLRztBQUNILE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLGVBQWUsRUFDbEUsVUFBVSxFQUFFLFNBQVMsRUFBRSxpQkFBaUIsRUFDeEMsZUFBZSxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQ3RDLE1BQU0sZUFBZSxDQUFDO0FBRTlCLE9BQU8sRUFBRSxPQUFPLEVBQUUsVUFBVSxFQUFnQixNQUFNLE1BQU0sQ0FBQztBQUN6RCxPQUFPLEVBQUUsU0FBUyxFQUFRLE1BQU0sZ0JBQWdCLENBQUM7QUFFakQsT0FBTyxFQUFZLFVBQVUsRUFBRSxhQUFhLEVBQUUsVUFBVSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdkYsT0FBTyxFQUFVLFlBQVksRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUM3QyxRQUFRLEVBQUUsUUFBUSxFQUFxQixVQUFVLEVBQUUsZUFBZSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDMUcsT0FBTyxFQUFFLFFBQVEsRUFBRSxhQUFhLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUUvRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUN6RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFakQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRS9DLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3hELE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxNQUFNLHFEQUFxRCxDQUFDO0FBRWxHLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDJDQUEyQyxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDJDQUEyQyxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDZDQUE2QyxDQUFDO0FBQ3BGLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDhDQUE4QyxDQUFDO0FBRXRGLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBRXhFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBUXpFO0lBQXVDLDZDQUFhO0lBaytCbEQsMkJBQzhCLEtBQXVCLEVBQ3pDLElBQWlDLEVBQ2pDLFVBQXNCLEVBQ3RCLGNBQWlDLEVBQ2pDLGVBQWdDO1FBTDVDLFlBT0UsaUJBQU8sU0F5RVI7UUEvRTZCLFdBQUssR0FBTCxLQUFLLENBQWtCO1FBQ3pDLFVBQUksR0FBSixJQUFJLENBQTZCO1FBQ2pDLGdCQUFVLEdBQVYsVUFBVSxDQUFZO1FBQ3RCLG9CQUFjLEdBQWQsY0FBYyxDQUFtQjtRQUNqQyxxQkFBZSxHQUFmLGVBQWUsQ0FBaUI7UUFyK0JsQyxjQUFRLEdBQXFCLElBQUksT0FBTyxFQUFXLENBQUM7UUFFdEQsV0FBSyxHQUFzQixJQUFJLENBQUM7UUE2RnhDOztXQUVHO1FBRUksZUFBUyxHQUFXLElBQUksQ0FBQztRQXVCaEM7Ozs7O1dBS0c7UUFFSCxlQUFTLEdBQTRCLElBQUksWUFBWSxFQUFhLENBQUM7UUFHbkUsa0JBQVksR0FBNEIsSUFBSSxZQUFZLEVBQWEsQ0FBQztRQUd0RSxrQkFBWSxHQUF5QixJQUFJLFlBQVksRUFBVSxDQUFDO1FBR2hFLGdCQUFVLEdBQXlCLElBQUksWUFBWSxFQUFVLENBQUM7UUFHOUQsdUJBQWlCLEdBQXNCLElBQUksWUFBWSxFQUFPLENBQUM7UUFHL0QscUJBQWUsR0FBc0IsSUFBSSxZQUFZLEVBQU8sQ0FBQztRQUc3RCxnQkFBVSxHQUFzQixJQUFJLFlBQVksRUFBTyxDQUFDO1FBbUI5QyxjQUFRLEdBQWEsSUFBSSxDQUFDO1FBQzFCLGtCQUFZLEdBQVksS0FBSyxDQUFDLENBQUMsbUJBQW1CO1FBQ3BELHNCQUFnQixHQUFZLEtBQUssQ0FBQyxDQUFDLG1CQUFtQjtRQUN0RCxxQkFBZSxHQUFXLENBQUMsQ0FBQyx