UNPKG

@true-directive/grid

Version:

Angular Data Grid from Yopsilon.

1,081 lines 137 kB
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;\">&nbsp;</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;\">&nbsp;</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