UNPKG

@true-directive/grid

Version:

Angular Data Grid from Yopsilon.

1,030 lines 147 kB
import * as tslib_1 from "tslib"; /** * Copyright (c) 2018-2019 Aleksey Melnikov, True Directive Company. * @link https://truedirective.com/ * @license MIT */ import { Directive, ElementRef, Input, EventEmitter, Output, Renderer2, Injector, ComponentFactoryResolver, ApplicationRef } from '@angular/core'; // Base import { GridLayout } from '@true-directive/base'; import { ColumnType, RenderMode } from '@true-directive/base'; import { CellHighlighter } from '@true-directive/base'; // Classes import { RowCell } from './row-cell.class'; // Services import { GridStateService } from './grid-state.service'; // Editor components import { EditorTextComponent } from './editors/editor-text.component'; import { EditorSelectTrueComponent } from './editors/editor-select-true.component'; import { EditorNumberComponent } from './editors/editor-number.component'; import { EditorDateComponent } from './editors/editor-date.component'; // Cell components import { CellHtmlComponent } from './cells/cell-html.component'; import { CellRefComponent } from './cells/cell-ref.component'; /** * Grid row directive. It's all about speed. */ let RowDirective = class RowDirective { constructor(_renderer, _cfResolver, _appRef, _injector, elementRef) { this._renderer = _renderer; this._cfResolver = _cfResolver; this._appRef = _appRef; this._injector = _injector; this.elementRef = elementRef; /** * Indicates we're an empty line and we don't need to do anything. */ this.empty = false; /** * Horizontal scroll position */ this.viewPortLeft = -1; /** * Scrollbox width */ this.viewPortWidth = -1; /** * Locale */ this.locale = ''; /** * Event - clicking on the checkbox - will cause the value to change */ this.toggleCheckbox = new EventEmitter(); this._left_rendered = -1; this._right_rendered = -1; /** * Cell list */ this.cells = []; /** * Cell selection levels */ this.selectionMap = []; /** * View was initialized */ this._viewInitialized = false; /** * One of the cells was in edit mode before destroying the row */ this._wasEditor = null; /** * Row height in previous render */ this._height0 = null; /** * Locale in previous render */ this._locale0 = ''; // Применение стилей помеченных записей this._checkedAppearance = false; // Подписки на изменения в редакторе this._subscribes = []; /** * Предыдущее значение общего текстового фильтра */ this._filter0 = ''; this._editorRef = null; this._customCellRefs = []; this._skips = []; } /** * Data of the displayed row */ get rowData() { return this.state.dataSource.rowData(this.row); } get st() { return this.state.settings; } get sta() { return this.state.settings.appearance; } get currentRowIndex() { return this.axI + this.layout.selection.displayedStartIndex; } get children() { return this.elementRef.nativeElement.children; } firstCellRect() { for (let i = 0; i < this.children.length; i++) { const el = this.children[i]; // Эти вообще не считаем. if (el.classList.contains('true-cell-indent')) { continue; } return el.getBoundingClientRect(); } return null; } firstCellClientHeight() { for (let i = 0; i < this.children.length; i++) { const el = this.children[i]; if (el.classList.contains('true-cell-indent')) { continue; // Эти могут быть без бордеров, а остальные с бордерами } return el.clientHeight; } return null; } /** * Получение поля по координатам * @param x Координата поля * @return [description] */ cellByXY(x, y) { let xx = this.cells[0].element.getBoundingClientRect().left; for (let i = 0; i < this.cells.length; i++) { const cell = this.cells[i]; const col = this.state.columnByFieldName(cell.fieldName); const ww = !cell.skipped ? cell.element.offsetWidth : col.displayedWidth; if (x >= xx && x < xx + ww) { return cell.fieldName; } xx += ww; } return ''; } /** * Вызывается для получения Х-координаты ячейки с целью прокрутки * компонента к заданной ячейке * @param fieldName Наименование поля ячейки * @return Координаты левого и правого края */ cellHorizontalPos(fieldName) { const x0 = this.cells[0].element.offsetLeft; let xx = x0; for (let i = 0; i < this.cells.length; i++) { const cell = this.cells[i]; const col = this.state.columnByFieldName(cell.fieldName); // Получить clientWidth const ww = !cell.skipped ? cell.element.offsetWidth : col.displayedWidth; if (cell.fieldName === fieldName) { return { // Для первой колонки указыаем нулевой offset, чтобы при наличии indents // прокрутка осуществлялась к самому началу l: i === 0 ? 0 : xx, r: xx + ww }; } xx += ww; } return null; } // Очистка выделения clearSelection() { this.cells.forEach((cell) => { cell.setSelected(0); cell.setCellFocused(false); }); } // Генерируем план выделения getSelectionMap(sel, rowChecked = false) { const sels = []; let sel0 = 0; for (let i = 0; i < this.cells.length; i++) { sels.push(sel0); } for (let j = 0; j < sel.ranges.length; j++) { const selection = sel.ranges[j]; // По вертикали let minI = selection.rowIndex; let maxI = selection.rowIndex + selection.rangeY; // Меняем местами 'to' and 'from' if (minI > maxI) { const t = maxI; maxI = minI; minI = t; } if (this.currentRowIndex < minI || this.currentRowIndex > maxI) { continue; // В этой строке ничего не выделено } // По горизонтали const fromIndex = selection.columnIndex; const toIndex = fromIndex + selection.rangeX; const selectedColumnIndex = fromIndex; for (let i = 0; i < this.cells.length; i++) { if (i >= fromIndex && i <= toIndex || i <= fromIndex && i >= toIndex) { sels[i]++; } } } return sels; } setCellSelection(cell, idx) { const sel = this.layout.selection; const selected = this.selectionMap[idx]; const cellFocused = this.currentRowIndex === sel.focusedRowIndex && idx === sel.focusedColumnIndex; cell.setSelected(selected); cell.setCellFocused(cellFocused); return selected; } // Устанавливаем выделение setSelection() { const sel = this.layout.selection; const rowChecked = this.state.check.isRowChecked(this.row); if (this.state.sta.checkedRowClass != '' && (this.state.sta.enableCheckedAppearance || this._checkedAppearance)) { if (rowChecked && this.state.sta.enableCheckedAppearance) { this.elementRef.nativeElement.classList.add(this.sta.checkedRowClass); } else { this.elementRef.nativeElement.classList.remove(this.sta.checkedRowClass); } } this._checkedAppearance = this.sta.enableCheckedAppearance; if (sel.ranges.length < 1 && !rowChecked) { this.clearSelection(); return; } this.selectionMap = this.getSelectionMap(sel, rowChecked); this.cells.forEach((cell, i) => { this.setCellSelection(cell, i); }); } // Полная очистка строки clear(saveH = false) { if (saveH) { // Сохраняем высоту строки для редактора this._height0 = this.firstCellClientHeight(); } else { // Сбрасываем высоту строки, чтобы не было недостоверной информации this._height0 = null; } this._subscribes.forEach(s => s.unsubscribe()); this._subscribes = []; this.cells.splice(0, this.cells.length); let itemsToRemove = []; // Fastest way this.elementRef.nativeElement.innerHTML = ''; if (this._customCellRefs.length > 0) { this._customCellRefs.forEach(r => r.destroy()); this._customCellRefs.splice(0, this._customCellRefs.length); } this.clearEditor(); } clearEditor() { this._height0 = this.firstCellClientHeight(); if (this._editorRef && this._wasEditor) { const cp = this._wasEditor; this.cells.forEach(cell => { if (cell.fieldName === cp.fieldName && this.rowData === cp.row) { cell.element.style.height = 'unset'; this._renderer.removeClass(cell.element, 'true-cell-input'); let itemsToRemove = []; let i = 0; while (i < cell.element.children.length) { itemsToRemove.push(cell.element.children[i]); i++; } itemsToRemove.forEach(item => this._renderer.removeChild(cell.element, item)); const col = this.state.columnByFieldName(cp.fieldName); const rowData = this.rowData; const v = this.rowData[col.fieldName]; const v_displayed = this.getDisplayedValue(col, rowData, v); this.fillCell(cell, col, rowData, false, v, v_displayed); cell.value = v; } /* this._editorRef.destroy(); this._editorRef = null; */ this._wasEditor = null; }); } } renderStub(rowData, cell, col) { if (col.isBoolean) { return; } const divEl = this._renderer.createElement('span'); const w = 20 + Math.random() * 40; divEl.style.width = w + '%'; this._renderer.appendChild(cell.element, divEl); this._renderer.addClass(divEl, 'true-stub'); } // Чекбокс создается прямо в td. Для того, чтобы его ширина не зависела // от наличия бордеров и ширины ячейки, было бы неплохо создать // промежуточный div с display: flex. Но вызов еще одного createElement // сильно замедляет процесс создания строки. renderBoolean(rowData, cell, col, v) { const divEl = this._renderer.createElement('div'); if (this.st.canEditColumnCell(col)) { this._renderer.addClass(divEl, this.sta.checkboxClass); this._renderer.listen(divEl, 'mousedown', (e) => e.stopPropagation()); this._renderer.listen(divEl, 'dblclick', (e) => e.stopPropagation()); this._renderer.listen(divEl, 'click', (e) => { this.toggleCheckbox.emit({ row: rowData, fieldName: col.fieldName }); e.stopPropagation(); }); this._renderer.appendChild(cell.element, divEl); } else { const classes = this.sta.booleanClass.trim().split(' '); classes.forEach(s => this._renderer.addClass(divEl, s)); this._renderer.appendChild(cell.element, divEl); } cell.cbElement = divEl; cell.setChecked(v); } renderCheckbox(cell, col, v) { const divEl = this._renderer.createElement('div'); this._renderer.addClass(divEl, this.sta.checkboxClass); if (!this.st.checkByCellClick) { this._renderer.listen(divEl, 'mousedown', (e) => e.stopPropagation()); this._renderer.listen(divEl, 'dblclick', (e) => e.stopPropagation()); this._renderer.listen(divEl, 'click', (e) => { this.toggleCheckbox.emit({ row: this.row, fieldName: col.fieldName }); e.stopPropagation(); }); } cell.cbElement = divEl; this._renderer.appendChild(cell.element, divEl); cell.setChecked(v); } getEditorComponentType(col) { let editorType = EditorTextComponent; if (col.type === ColumnType.DATETIME) { editorType = EditorDateComponent; } if (col.type === ColumnType.NUMBER) { editorType = EditorNumberComponent; } if (col.type === ColumnType.UNSAFE_HTML) { editorType = EditorSelectTrueComponent; } if (col.editorComponentType !== null) { editorType = col.editorComponentType; } return editorType; } getDh() { // Один пиксел добавим, если видны горизонтальные линии let dh = 0; if (this.axI !== 0 && !this.state.IE && this.sta.horizontalLines) { dh = 1; } return dh; } setEditorHeight(cell) { // Тест, когда всё редакторы if (this._height0 === null && this.state.ui.editorHeight !== null) { // Если в состоянии сохранена высота, то используем её this._height0 = this.state.ui.editorHeight; } else { // Иначе наоборот - сохраняем высоту this.state.ui.editorHeight = this._height0; } if (this._height0 >= this.state.settings.rowHeight) { cell.element.style.height = (this._height0 - 1) + 'px'; } } renderEditor(rowData, cell, col, v) { this._renderer.addClass(cell.element, 'true-cell-input'); const editorType = this.getEditorComponentType(col); const componentFactory = this._cfResolver.resolveComponentFactory(editorType); this._editorRef = componentFactory.create(this._injector, [], cell.element); this._appRef.attachView(this._editorRef.hostView); // Initialization this._editorRef.instance.state = this.state; this._editorRef.instance.column = col; this._editorRef.instance.row = rowData; this.setEditorHeight(cell); const dh = this.getDh(); // Изменение значения. Сохраняем измененное. const s_change = this._editorRef.instance.change.subscribe((v) => { this.state.ui.editorValue = v; }); // Подтверждение редактирования. Отправляем в данные const s_commit = this._editorRef.instance.commit.subscribe((e) => { this.state.ui.commitEditor(this.row, col.fieldName, e); }); // Отмена редактирования const s_cancel = this._editorRef.instance.cancel.subscribe(() => { let cp = this._wasEditor.clone(); this.state.ui.stopEditing(cp, true, true); }); this._editorRef.instance.init(this.state.ui.editorValue, this.state.ui.editorValueChanged, this._height0 + dh, this.state.IE, this.state.ui.editorWasShown); this.state.ui.editorWasShown = true; // Persisting last editor state if (this.state.ui.editor) { this._wasEditor = this.state.ui.editor.clone(); } // this._subscribes.push(s_change); this._subscribes.push(s_commit); this._subscribes.push(s_cancel); } // Пока так renderCustomCell(rowData, cell, col, v, componentType = CellHtmlComponent) { this._renderer.addClass(cell.element, 'true-cell-custom'); const componentFactory = this._cfResolver.resolveComponentFactory(componentType); const cr = componentFactory.create(this._injector, [], cell.element); // Initialization cr.instance.state = this.state; cr.instance.column = col; cr.instance.row = rowData; this._appRef.attachView(cr.hostView); cr.instance.init(this.getDisplayedValue(col, rowData, v)); // Подтверждение редактирования. Отправляем в данные const s = cr.instance.event.subscribe((e) => { // вызываем в стэйте this.state.emitCustomCellEvent(e); }); this._customCellRefs.push(cr); this._subscribes.push(s); } getDisplayedValue(col, rowData, v) { return this.state.dataSource.displayedValue(col, v, rowData); } fillCell(cell, col, rowData, isFirst, v, v_displayed) { const classes = this.state.st.cellClass(col).trim().split(' '); classes.forEach(s => { if (s !== '') { this._renderer.addClass(cell.element, s); } }); if (col.classField !== '' && rowData[col.classField]) { this._renderer.addClass(cell.element, rowData[col.classField]); } cell.rendered = true; cell.fieldName = col.fieldName; if (rowData.__ax === 'empty') { this.renderStub(rowData, cell, col); return cell; } const tdEl = cell.element; if (col.isCheckbox) { // Помечаются не только данные, но и заголовки групп. Поэтому // апдейтится строка из результирующего набора данных this.renderCheckbox(cell, col, v); return cell; } if (col.isBoolean) { // Может быть изменено только значение исходных данных, но не // заголовков групп. Поэтому отправляется исходная строка this.renderBoolean(rowData, cell, col, v); return cell; } if (col.type === ColumnType.REFERENCE) { this.renderCustomCell(rowData, cell, col, v, CellRefComponent); return cell; } if (this.state.ui.editor !== null && this.state.ui.editor.fieldName === col.fieldName && this.state.ui.editor.row === rowData) { // Заголовок группы не может быть отредактирован, отправляются исходные // данные this.renderEditor(rowData, cell, col, v); return cell; } if (col.cellComponentType !== null) { // Чтобы кастомная ячейка не путалась, отправляются исходные данные. // Если ячейка будет создана в заголовке группы - мы это не предусмотрели. this.renderCustomCell(rowData, cell, col, v, col.cellComponentType); return cell; } if (col.type === ColumnType.HTML) { // Чтобы кастомная ячейка не путалась, отправляются исходные данные. // Если ячейка будет создана в заголовке группы - мы это не предусмотрели. this.renderCustomCell(rowData, cell, col, v); return cell; } let html = col.type === ColumnType.UNSAFE_HTML; if (this.state.dataSource.searchString !== '' && this.state.st.searchHighlight) { html = true; } if (html) { const s = this.state.dataSource.searchString; tdEl.innerHTML = CellHighlighter.highlight(s, col, v, v_displayed); } else { tdEl.innerText = v_displayed; } return cell; } needRender(col, xPos, isFirst) { let render = true; // Не все ячейки рендерятся только при настройке RenderMode.VISIBLE if (this.st.renderMode !== RenderMode.VISIBLE) { return render; } if (this.viewPortLeft >= 0 && !isFirst) { // Учитываем вьюпорт if (xPos > (this.viewPortLeft + this.viewPortWidth)) { // Мы находимся правее render = false; } if ((xPos + col.headerWidth) < this.viewPortLeft) { // Мы находимся левее render = false; } } return render; } createTd(span) { const tdEl = this._renderer.createElement('td'); if (span > 1) { this._renderer.setAttribute(tdEl, 'colspan', span + ''); } return tdEl; } /** * Создание ячейки * @param col Колонка * @param span Количество объединенных ячеек * @return Объект RowCell */ createCell(col, span = 1, isFirst = false, xPos = -1, render = true) { const rowData = this.rowData; const v = rowData !== null ? rowData[col.fieldName] : null; const cell = new RowCell(col.fieldName, v, null); cell.element = this.createTd(span); if (render) { const v_displayed = this.getDisplayedValue(col, rowData, v); this.fillCell(cell, col, rowData, isFirst, v, v_displayed); } return cell; } // Для дерева - проверка необходимости объединения ячеек needApplySpan() { return 1; } createSkipCell(skipCount) { const tdEl = this._renderer.createElement('td'); this._renderer.setAttribute(tdEl, 'colspan', skipCount + ''); return tdEl; } renderRowEditor() { for (let i = 0; i < this.layout.columns.length; i++) { const col = this.layout.columns[i]; if (col.isBoolean) { continue; } const cell = this.cells[i]; if (this.state.ui.editor !== null && this.state.ui.editor.fieldName === col.fieldName && this.state.ui.editor.row === this.rowData) { // Заголовок группы не может быть отредактирован, отправляются исходные // данные const v = this.rowData[col.fieldName]; this.renderEditor(this.rowData, cell, col, v); return cell; } } } // Заполнение строки (создание разметки и вставка данных) renderRow() { this._locale0 = this.locale; this._skips.splice(0, this._skips.length); this._wasEditor = null; if (!this.empty) { this._filter0 = this.state.dataSource.searchString; } // Удаление старых ячеек this.cells.splice(0, this.cells.length); let firstCell = true; if (this.empty) { const tdEl = this._renderer.createElement('td'); this._renderer.setAttribute(tdEl, 'colspan', this.layout.columns.length + ''); this._renderer.appendChild(this.elementRef.nativeElement, tdEl); return; } // const dt = Date.now(); let xPos = 0; let skipCount = 0; let skipStart = -1; let skipEnd = -1; for (let i = 0; i < this.layout.columns.length; i++) { const col = this.layout.columns[i]; // Для дерева.. Нам нужен уровень.. И растягивание первой не чекбоксовой колонки // Но только если мы находимся в самой левой части let span = 1; const f = firstCell; if (firstCell && !col.isCheckbox) { span = this.needApplySpan(); firstCell = false; } // Done const needRender = this.needRender(col, xPos, f); const cell = this.createCell(col, span, f, xPos, needRender); this.cells.push(cell); if (needRender) { if (skipCount > 0) { // Was skips const skipTd = this.createSkipCell(skipCount); this._renderer.appendChild(this.elementRef.nativeElement, skipTd); skipCount = 0; this._skips.push({ element: skipTd, fromIndex: skipStart, toIndex: skipEnd }); } // Very resource intensive operation this._renderer.appendChild(this.elementRef.nativeElement, cell.element); } else { if (skipCount === 0) { skipStart = i; } skipEnd = i; cell.skipped = true; skipCount++; } xPos += col.headerWidth; } if (skipCount > 0) { const skipTd = this.createSkipCell(skipCount); this._renderer.appendChild(this.elementRef.nativeElement, skipTd); this._skips.push({ element: skipTd, fromIndex: skipStart, toIndex: skipEnd }); } this.setSelection(); this.setDisabledFields(); this._left_rendered = this.viewPortLeft; this._right_rendered = this.viewPortLeft + this.viewPortWidth; } nextEl(el) { let found = false; for (let i = 0; i < this.children.length; i++) { if (this.children[i] === el) { found = true; continue; } if (found) { return this.children[i]; } } return null; } unskip(cell, i) { for (let j = 0; j < this._skips.length; j++) { const skip = this._skips[j]; if (i >= skip.fromIndex && i <= skip.toIndex) { let insertBeforeEl = null; if (i === skip.fromIndex) { // Добавляем слева skip.fromIndex++; this._renderer.insertBefore(this.elementRef.nativeElement, cell.element, skip.element); } else { if (i === skip.toIndex) { // Добавляем справа skip.toIndex--; const nextEl = this.nextEl(skip.element); if (nextEl !== null) { this._renderer.insertBefore(this.elementRef.nativeElement, cell.element, nextEl); } else { this._renderer.appendChild(this.elementRef.nativeElement, cell.element); } } else { // Добавляем посередине // Создаем новый скип... let newEl = this.createSkipCell(i - skip.fromIndex); this._renderer.insertBefore(this.elementRef.nativeElement, cell.element, skip.element); this._renderer.insertBefore(this.elementRef.nativeElement, newEl, cell.element); this._skips.push({ element: newEl, fromIndex: skip.fromIndex, toIndex: i - 1 }); skip.fromIndex = i + 1; } } if (skip.fromIndex > skip.toIndex) { // Удаляем этот пропуск this._skips.splice(j, 1); this._renderer.removeChild(this.elementRef.nativeElement, skip.element); } else { // Меняем его спан this._renderer.setAttribute(skip.element, 'colspan', (skip.toIndex - skip.fromIndex + 1) + ''); } cell.skipped = false; break; } } } renderByViewPort() { const rowData = this.rowData; const right = this.viewPortLeft + this.viewPortWidth; const left = this.viewPortLeft; let xPos = 0; // Первая ячейка всегда отрендерена for (let i = 0; i < this.layout.columns.length; i++) { // Подготавливаем данные const col = this.layout.columns[i]; const cell = this.cells[i]; if (!cell) { continue; } // Решаем, рендерить или нет let needRender = this.needRender(col, xPos, false); // Нужно последовательно добавлять... if (needRender && i > 0) { if (!cell.rendered) { const v = cell.value; const v_displayed = this.getDisplayedValue(col, rowData, v); this.fillCell(cell, col, rowData, i === 0, v, v_displayed); } if (cell.skipped) { this.unskip(cell, i); } } xPos += col.headerWidth; } if (this._right_rendered < (this.viewPortLeft + this.viewPortWidth)) { this._right_rendered = this.viewPortLeft + this.viewPortWidth; } if (this._left_rendered > this.viewPortLeft) { this._left_rendered = this.viewPortLeft; } } // Проверяем, нужно ли пересоздать по причине включения или выключения редактора checkEditor() { if (this._wasEditor === null && this.state.ui.editor === null) { // Всё ок. // Редактора и не было и не намечается. return true; } if (this._wasEditor === null && this.state.ui.editor !== null) { // Не было, но появился if (this.state.ui.editor.row !== this.rowData) { // Но строка не наша return true; } } if (this._wasEditor !== null && this.state.ui.editor !== null) { // Был и не стало if (this._wasEditor.row === this.state.ui.editor.row) { // В этой строке if (this._wasEditor.fieldName === this.state.ui.editor.fieldName) { return true; // Не изменилось, оставляем } } } return false; } /** * We check whether it is necessary to recreate the string when changing the * column. If there were simple changes (two columns swapped or one of them * just disappeared), we try to apply these changes without a full rerendering. * @return True if we don't need a rerender. */ checkColumns() { if (!this.layout) { return false; } if (this.cells.length === 0 && !this.layout.columns) { return true; } // The current number of cells is more than 1 higher than the number // of columns. if (this.cells.length > (this.layout.columns.length + 1)) { return false; } if (this.cells.length < (this.layout.columns.length - 1)) { return false; } let i = 0; let changed = false; while (i < this.cells.length) { if (!this.layout || !this.layout.columns[i]) { return false; } if (this.state.isTree && this.layout.columns[i].fieldName !== this.cells[i].fieldName) { // For a tree, it wouldn't help if we just moved the columns around. return false; } if (this.layout.columns[i].fieldName !== this.cells[i].fieldName) { // Let's consider three scenarios so that we don't recreate the entire line... if (i < this.cells.length - 1 && this.layout.columns[i].fieldName === this.cells[i + 1].fieldName) { // 1. The column is missing? // Removing the cell. const ww = this.cells[i].element.offsetWidth; this._renderer.removeChild(this.elementRef.nativeElement, this.cells[i].element); this.cells.splice(i, 1); this._skips.forEach(s => { if (s.toIndex > i) { s.toIndex--; } if (s.fromIndex > i) { s.fromIndex--; } }); continue; } else { if (i < this.cells.length - 2 && this.layout.columns[i].fieldName === this.cells[i + 2].fieldName) { // 1.1. Two columns are missing? // Remove one cell. The second one will be removed by the next iteration. this._renderer.removeChild(this.elementRef.nativeElement, this.cells[i].element); this.cells.splice(i, 1); this._skips.forEach(s => { if (s.toIndex > i) { s.toIndex--; } if (s.fromIndex > i) { s.fromIndex--; } }); continue; } else { if (i < this.layout.columns.length - 1 && this.cells[i].fieldName === this.layout.columns[i + 1].fieldName) { // 2. New column? if (this.cells[i].skipped) { return false; } this.viewPortLeft = -1; const cell = this.createCell(this.layout.columns[i]); cell.setDisabled(this.state.dragDrop.disabledFields.indexOf(cell.fieldName) >= 0); this._renderer.insertBefore(this.elementRef.nativeElement, cell.element, this.cells[i].element); this.cells.splice(i, 0, cell); this._skips.forEach(s => { if (s.toIndex > i) { s.toIndex++; } if (s.fromIndex > i) { s.fromIndex++; } }); // Don't forget to reset selection changed = true; i++; } else { // Didn't help, we need to rerender! return false; } } } } i++; } // Render what fit on the screen after changing the columns this.renderByViewPort(); if (changed) { this.setSelection(); } return true; } checkValues() { if (!this.layout) { return false; } if (this.cells.length === 0 && !this.layout.columns) { return true; } // Другое количество ячеек if (this.cells.length !== this.layout.columns.length) { return false; } // Нужно обновить подсветку if (this._filter0 !== this.state.dataSource.searchString) { return false; } let i = 0; while (i < this.cells.length) { const cell = this.cells[i]; const rowData = this.rowData; const v = rowData !== null ? rowData[cell.fieldName] : null; if (v !== cell.value) { if (cell.cbElement) { cell.setChecked(this.row[cell.fieldName]); } else { // Даём шанс редактору не инициировать перерисовку всей строки if (this._wasEditor && this._wasEditor.fieldName === cell.fieldName && this._wasEditor.row === this.rowData) { i++; continue; } return false; } } i++; } return true; } setDisabledFields() { this.cells.forEach(cell => { cell.setDisabled(this.state.dragDrop.disabledFields.indexOf(cell.fieldName) >= 0); }); } setParams(byInit = false) { if (byInit) { this.renderRow(); this._viewInitialized = true; return; } if (!this._viewInitialized) { return; } } checkViewPort() { return this._left_rendered <= this.viewPortLeft && this._right_rendered >= (this.viewPortLeft + this.viewPortWidth); } check() { const localeChanged = this.locale !== this._locale0; let settingsChanged = false; if (this._checkedAppearance !== this.sta.enableCheckedAppearance) { settingsChanged = true; } let valuesChanged = false; //let editorChanged = false; const columnsChanged = !this.checkColumns(); if (!columnsChanged) { valuesChanged = !this.checkValues(); //if (!valuesChanged) { //editorChanged = !this.checkEditor(); //} } return !(columnsChanged || valuesChanged || //editorChanged || settingsChanged || localeChanged); } ngOnChanges(changes) { this.setParams(); } ngDoCheck() { if (!this._viewInitialized) { return; } if (!this.check()) { // Изменились основные параметры строки (колонки, значения, редактор) const hasEditor = this.state.ui.editor && this.state.ui.editor.row === this.rowData; // Очищаем this.clear(hasEditor); // И заново формируем строку this.renderRow(); return; } // if (!this.checkEditor()) { this.clearEditor(); this.renderRowEditor(); } // Изменились параметры отображения if (!this.checkViewPort()) { // Формируем только те ячейки, которые добавлены к уже отрендеренным this.renderByViewPort(); } this.setSelection(); this.setDisabledFields(); } ngAfterContentInit() { this.setParams(true); } ngOnDestroy() { this.clear(); } }; tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Object) ], RowDirective.prototype, "row", void 0); tslib_1.__decorate([ Input('true-state'), tslib_1.__metadata("design:type", GridStateService) ], RowDirective.prototype, "state", void 0); tslib_1.__decorate([ Input('true-i'), tslib_1.__metadata("design:type", Number) ], RowDirective.prototype, "axI", void 0); tslib_1.__decorate([ Input('true-layout'), tslib_1.__metadata("design:type", GridLayout) ], RowDirective.prototype, "layout", void 0); tslib_1.__decorate([ Input('true-empty'), tslib_1.__metadata("design:type", Object) ], RowDirective.prototype, "empty", void 0); tslib_1.__decorate([ Input('view-port-left'), tslib_1.__metadata("design:type", Object) ], RowDirective.prototype, "viewPortLeft", void 0); tslib_1.__decorate([ Input('view-port-width'), tslib_1.__metadata("design:type", Object) ], RowDirective.prototype, "viewPortWidth", void 0); tslib_1.__decorate([ Input('true-locale'), tslib_1.__metadata("design:type", String) ], RowDirective.prototype, "locale", void 0); tslib_1.__decorate([ Output('toggleCheckbox'), tslib_1.__metadata("design:type", Object) ], RowDirective.prototype, "toggleCheckbox", void 0); RowDirective = tslib_1.__decorate([ Directive({ selector: '[true-row]' }), tslib_1.__metadata("design:paramtypes", [Renderer2, ComponentFactoryResolver, ApplicationRef, Injector, ElementRef]) ], RowDirective); export { RowDirective }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm93LmRpcmVjdGl2ZS5qcyIsInNvdXJjZVJvb3QiOiJuZzovL0B0cnVlLWRpcmVjdGl2ZS9ncmlkLyIsInNvdXJjZXMiOlsic3JjL3Jvdy5kaXJlY3RpdmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7O0VBSUU7QUFDRixPQUFPLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQWEsWUFBWSxFQUFFLE1BQU0sRUFDbEUsU0FBUyxFQUFFLFFBQVEsRUFFbkIsd0JBQXdCLEVBQUUsY0FBYyxFQUMxQixNQUFNLGVBQWUsQ0FBQztBQUV4QyxPQUFPO0FBQ1AsT0FBTyxFQUFFLFVBQVUsRUFBdUIsTUFBTSxzQkFBc0IsQ0FBQztBQUN2RSxPQUFPLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBVSxNQUFNLHNCQUFzQixDQUFDO0FBQ3RFLE9BQU8sRUFBZ0IsZUFBZSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFHckUsVUFBVTtBQUNWLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUUzQyxXQUFXO0FBQ1gsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFFeEQsb0JBQW9CO0FBQ3BCLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3RFLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLHdDQUF3QyxDQUFDO0FBQ25GLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQzFFLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRXRFLGtCQUFrQjtBQUNsQixPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUU5RDs7R0FFRztBQUlILElBQWEsWUFBWSxHQUF6QixNQUFhLFlBQVk7SUFnb0NyQixZQUNZLFNBQW9CLEVBQ3BC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