comindware.core.ui
Version:
Comindware Core UI provides the basic components like editors, lists, dropdowns, popups that we so desperately need while creating Marionette-based single-page applications.
825 lines (726 loc) • 30.8 kB
text/typescript
import { transliterator } from 'utils';
import Marionette from 'backbone.marionette';
import Backbone from 'backbone';
import _ from 'underscore';
import { validationSeverityTypes, validationSeverityClasses } from 'Meta';
import { classes } from '../meta';
import dropdown from 'dropdown';
import MobileService from '../../services/MobileService';
import CellViewFactory from '../CellViewFactory';
import { Column, GridItemModel } from '../types/types';
import { objectPropertyTypes, complexValueTypes, DOUBLECLICK_DELAY } from '../../Meta';
import ErrorsPanelView from '../../views/ErrorsPanelView';
const config = {
TRANSITION_DELAY: 400
};
const defaultOptions = {
levelMargin: 10,
contextLevelMargin: 30,
subGroupMargin: 20
};
/**
* Some description for initializer
* @name RowView
* @memberof module:core.list.views
* @class RowView
* @extends Marionette.View
* @constructor
* @description View используемый по умолчанию для отображения строки списка
* @param {Object} options Constructor options
* @param {Array} options.columns Массив колонк
* @param {Object} options.gridEventAggregator ?
* @param {Number} [options.paddingLeft=20] Левый отступ
* @param {Number} [options.paddingRight=10] Правый отступ
* */
export default Marionette.View.extend({
tagName: 'tr',
ui: {
cells: '.js-grid-cell',
collapsibleButton: '.js-collapsible-button',
checkbox: '.js-checkbox',
dots: '.js-dots',
index: '.js-index'
},
events: {
'click @ui.collapsibleButton': '__toggleCollapse',
'pointerdown @ui.checkbox': '__handleCheckboxClick',
click: '__handleClick',
dblclick: '__handleDblClick',
dragstart: '__handleDragStart',
dragenter: '__handleDragEnter',
dragleave: '__handleDragLeave',
drop: '__handleDrop',
contextmenu: '__handleContextMenu',
touchend: '__handleTouchEnd',
touchmove: '__handleTouchMove',
touchcancel: '__handleTouchCancel'
},
modelEvents: {
selected: '__handleSelection',
deselected: '__handleDeselection',
'select:pointed': '__selectPointed',
'selected:enter': '__handleEnter',
'selected:exit': '__handleExit',
highlighted: '__handleHighlight',
unhighlighted: '__handleUnhighlight',
change: '__handleChange',
blink: '__blink',
'toggle:collapse': 'updateCollapsed',
checked: '__updateState',
unchecked: '__updateState',
'checked:some': '__updateState',
validated: '__onValidated',
'set:draggable': '__setDraggable',
'dragleave': '__onModelDragLeave'
},
initialize() {
_.defaults(this.options, defaultOptions);
this.gridEventAggregator = this.options.gridEventAggregator;
this.listenTo(this.gridEventAggregator, 'set:draggable', this.__setDraggable);
this.collection = this.model.collection;
this.cellConfigs = {};
this.cellViewsByKey = {};
this.options.columns.forEach((column: Column, index: number) => {
this.cellConfigs[column.key] = { isHidden: column.getHidden?.(this.model) };
if (typeof column.getHidden === 'function') {
this.listenTo(this.model, 'change', () => this.__setCellHidden({ column, index, isHidden: Boolean(column.getHidden?.(this.model)) }));
}
});
this.__debounceSelectPointedOnClick = _.debounce((...args) => this.__selectPointedOnClick(...args), DOUBLECLICK_DELAY);
},
getValue(id: string) {
this.model.get(id);
},
onRender() {
const model = this.model;
if (model.selected) {
this.__handleSelection();
}
if (this.model.checked !== undefined) {
this.__updateState();
}
if (this.getOption('isTree')) {
this.insertFirstCellHtml(true);
}
this.__updateValidationErrors();
},
onDestroy() {
if (this.cellViewsByKey) {
Object.values(this.cellViewsByKey).forEach(x => x.destroy());
this.cellViewsByKey = {};
}
this.errorPopout?.destroy();
this.multiValuePopout?.destroy();
},
updateCollapsed(model: ListItemModel) {
const collaspibleButtons = this.el.getElementsByClassName(classes.collapsibleIcon);
if (!model.collapsed) {
if (collaspibleButtons.length) {
collaspibleButtons[0].classList.add(classes.expanded);
}
} else if (collaspibleButtons.length) {
collaspibleButtons[0].classList.remove(classes.expanded);
}
},
_renderTemplate() {
if (typeof this.options.transliteratedFields === 'object') {
transliterator.initializeTransliteration({
model: this.model,
transliteratedFields: this.options.transliteratedFields,
schemaForExtendComputed: this.options.columns
});
}
this.onDestroy();
const itemsHTML: Array<string> = this.itemsHTML = [];
if (this.getOption('showCheckbox')) {
this.__insertCellChechbox();
}
const customCells: Array<{ index: number, CellView: Marionette.View<Backbone.Model> }> = [];
this.options.columns.forEach((column: Column, index: number) => {
if (column.cellView) {
customCells.push({ index, CellView: column.cellView })
} else {
const cellHTML = CellViewFactory.getCell(column, this.model);
itemsHTML.push(cellHTML);
}
});
this.el.innerHTML = this.itemsHTML.join('');
customCells.forEach(({ index, CellView }) => {
const cellView = this.__renderCell({ column: this.options.columns[index], index, CellView });
if (index === 0) {
if (this.getOption('showCheckbox')) {
const firstChildEl = this.el.firstChild;
firstChildEl.insertAdjacentElement('afterend', cellView.el);
} else {
this.el.insertAdjacentElement('afterbegin', cellView.el);
}
} else {
const childElBefore = this.__getCellByColumnIndex(index - 1);
childElBefore.insertAdjacentElement('afterend', cellView.el);
}
cellView.triggerMethod('before:attach');
cellView.triggerMethod('attach');
})
},
__handleChange() {
const changed = this.model.changedAttributes();
if (!changed) {
return;
}
this.getOption('columns').forEach((column, index) => {
if (!this.__isNeedToReplaceCell({ changed, column, index })) {
return;
}
this.__insertReadonlyCell({ column, index, isReplace: true });
});
},
__isNeedToReplaceCell({ changed, column, index }) {
if (column.cellView || column.getHidden?.(this.model)) {
return false;
}
const isCellValueChanged = Object.prototype.hasOwnProperty.call(changed, column.key);
if (!isCellValueChanged) {
return false;
}
return this.lastPointedIndex !== index || !this.__isColumnEditable(index);
},
__hasCellErrors(column: Column) {
return column.required && _.isEmpty(this.model.get(column.key));
},
__isDropAllowed(): boolean {
const draggingModels = this.collection.draggingModels;
if (!draggingModels) {
return false;
}
if (draggingModels.some(draggingModel => this.collection.indexOf(this.model) + 1 === this.collection.indexOf(draggingModel) && this.model.level <= draggingModel.level)) {
return false;
}
if (draggingModels.some(draggingModel => this.__findInParents(draggingModel, this.model))) {
return false;
}
return true;
},
__findInParents(draggingModel: GridItemModel, model: GridItemModel): boolean {
if (model === draggingModel) {
return true;
}
if (model.parentModel) {
return this.__findInParents(draggingModel, model.parentModel);
}
return false;
},
__handleDragStart(event: { originalEvent: DragEvent }) {
const checkedModels = this.model.collection.getCheckedModels();
if (checkedModels.length) {
return;
}
this.model.collection.draggingModels = [this.model];
const originalEvent = event.originalEvent;
if (!originalEvent.dataTransfer) {
return;
}
originalEvent.dataTransfer.setData('Text', this.cid); // fix for FireFox
},
__handleDragEnter(event: DragEvent) {
this.__setDragEnterModel(this.model);
},
__setDragEnterModel(model: Backbone.Model) {
const previousDragEnterModel = this.model.collection.dragoverModel;
if (previousDragEnterModel === model) {
return;
}
previousDragEnterModel?.trigger('dragleave');
this.model.collection.dragoverModel = model;
if (this.__isDropAllowed()) {
this.el.classList.add(classes.dragover);
}
},
__handleDragLeave() {
this.el.classList.remove(classes.dragover);
},
__handleDrop(event: DragEvent) {
event.preventDefault();
this.el.classList.remove(classes.dragover);
if (this.__isDropAllowed()) {
this.gridEventAggregator.trigger('drag:drop', this.model.collection.draggingModels, this.model);
}
delete this.collection.draggingModels;
},
__handleContextMenu(event: MouseEvent) {
if (this.__isNeedToShowMenu(event)) {
this.model.trigger('contextmenu', this.model, event);
}
},
__isNeedToShowMenu(e: MouseEvent) {
const target = <Element>e.target;
const selection = document.getSelection();
return e.button === 0 || e.ctrlKey || !((selection?.toString().length && selection.focusNode?.contains(target)) || target.tagName === 'A');
},
__handleHighlight(fragment: string) {
this.render();
},
__handleUnhighlight() {
this.render();
},
updateIndex(index: number) {
if (index !== this.model.currentIndex) {
this.el.querySelector('.js-index').innerHTML = index;
this.model.currentIndex = index;
}
},
insertFirstCellHtml(force: boolean) {
const elements = this.el.children;
if (!elements.length) {
return;
}
const level = this.model.level || 0;
let margin = level * this.options.levelMargin;
const hasChildren = Boolean(this.model.children?.length);
if (!force && this.lastHasChildren === hasChildren && this.lastMargin === margin) {
return;
}
const el = this.__getCellByColumnIndex(0);
const treeFirstCell = el.getElementsByClassName('js-tree-first-cell')[0];
if (treeFirstCell?.parentElement === el) {
el.removeChild(treeFirstCell);
}
const isContext = el.getElementsByClassName('context-icon')[0];
if (isContext) {
margin = level * this.options.contextLevelMargin;
if (hasChildren) {
el.insertAdjacentHTML(
'beforeend',
`<i class="${classes.collapsible} js-tree-first-cell context-collapsible-btn ${Handlebars.helpers.iconPrefixer('angle-down')} ${this.model.collapsed === false ? classes.expanded : ''}"></i>`
);
}
isContext.style.marginLeft = `${margin + defaultOptions.subGroupMargin}px`;
} else if (hasChildren) {
el.insertAdjacentHTML(
'afterbegin',
`<i class="js-tree-first-cell collapsible-btn ${classes.collapsible}
${Handlebars.helpers.iconPrefixer('angle-down')} ${this.model.collapsed === false ? classes.expanded : ''}" style="margin-left:${margin}px;"></i/`
);
} else {
el.insertAdjacentHTML('afterbegin', `<span class="js-tree-first-cell" style="margin-left:${margin + defaultOptions.subGroupMargin}px;"></span>`);
}
this.lastHasChildren = hasChildren;
this.lastMargin = margin;
},
__insertCellChechbox() {
if (typeof this.model.__draggable !== 'boolean') {
this.model.__draggable = this.model.collection.__allDraggable;
}
const isDraggable = this.options.draggable && this.model.__draggable;
if (this.options.showRowIndex) {
this.model.currentIndex = this.model.collection.indexOf(this.model) + 1;
}
const cellHTML =
`<td class="${classes.checkboxCell} ${this.options.showRowIndex ? 'cell_selection-index' : 'cell_selection'}"
${isDraggable ? 'draggable="true"' : ''}>
${this.options.showRowIndex
? `<span class="js-index cell__index">
${this.model.currentIndex}
</span>`
: ''
}
<div class="checkbox js-checkbox"></div>
</td>`
this.itemsHTML.push(cellHTML);
},
__setDraggable(draggable: boolean): void {
this.model.__draggable = draggable;
const checkboxCellEl = this.el.querySelector(`.${classes.checkboxCell}`);
if (!checkboxCellEl) {
return;
}
const hasDraggableAttribute = checkboxCellEl.hasAttribute('draggable');
const needSet = draggable && !hasDraggableAttribute;
const needRemove = !draggable && hasDraggableAttribute;
if (needSet) {
checkboxCellEl.setAttribute('draggable', true);
} else if (needRemove) {
checkboxCellEl.removeAttribute('draggable');
}
},
__handleCheckboxClick(e: PointerEvent) {
const isShiftKeyPressed = e.shiftKey;
if (isShiftKeyPressed) {
e.preventDefault(); //remove text highlighting from table
}
this.model.toggleChecked(isShiftKeyPressed);
if (this.getOption('bindSelection')) {
this.model.collection.updateTreeNodesCheck(this.model, undefined, e.shiftKey);
}
},
__handleClick(e: MouseEvent) {
const model = this.model;
const selectFn = model.collection.selectSmart || model.collection.select;
const columnIndex = this.__getFocusedColumnIndex(e);
const column = this.options.columns[columnIndex];
if (selectFn) {
selectFn.call(model.collection, model, e.ctrlKey, e.shiftKey, undefined, {
isModelClick: true
});
}
if (column) {
// todo: find more clear way to handle this case
const target = <Element>e.target;
const isErrorButtonClicked = target && target.classList.contains('js-error-button');
const isShowEditor = this.__isColumnEditable(columnIndex) || column.isShowEditor;
if (isShowEditor
// temporary desicion for complex cells
|| (column.type === 'Complex' && [complexValueTypes.expression, complexValueTypes.script].includes(this.model.get(column.key)?.type))) {
// change boolean value immediatly
if (column.type === objectPropertyTypes.BOOLEAN && this.lastPointedIndex !== columnIndex) {
const newValue = column.storeArray ? [!this.model.get(column.key)?.[0]] : !this.model.get(column.key);
this.model.set(column.key, newValue);
}
} else {
const values = this.model.get(column.key);
if (values?.length > 1 && this.multiValueShownIndex !== columnIndex) {
this.__showDropDown(columnIndex);
}
}
this.__debounceSelectPointedOnClick({ isErrorButtonClicked, column, columnIndex });
}
this.gridEventAggregator.trigger('click', this.model);
},
__selectPointedOnClick({ isErrorButtonClicked, column, columnIndex }: { isErrorButtonClicked: boolean, column: Column, columnIndex: number }) {
if (!this.model.selected) {
return;
}
if (this.__isDoubleClicked) {
if (this.lastPointedIndex > -1 && this.lastPointedIndex === columnIndex) {
this.__deselectPointed();
}
this.__isDoubleClicked = false;
return;
}
this.__selectPointed(columnIndex, true);
if (isErrorButtonClicked) {
this.__showErrorsForColumn({ column, index: columnIndex });
}
},
__showDropDown(index: number) {
const column = this.options.columns[index];
this.lastShowDropodwnIndex =
this.multiValuePopout = CellViewFactory.tryGetMultiValueCellPanel(column, this.model, this.__getCellByColumnIndex(index));
if (this.multiValuePopout) {
this.multiValuePopout.open();
this.multiValuePopout.on('close', () => delete this.multiValueShownIndex);
this.multiValueShownIndex = index;
}
},
__getCellByColumnIndex(columnIndex: number): Element {
const index = this.getOption('showCheckbox') ? columnIndex + 1 : columnIndex;
const cellElement = this.el.children[index];
return cellElement;
},
__getEditableCell(column: Column): Marionette.View<Backbone.Model> {
return CellViewFactory.getCellViewForColumn(column, this.model)
},
__isColumnEditable(columnIndex: number): boolean {
if (columnIndex < 0 || !this.gridEventAggregator.isEditable) {
return false;
}
const column = this.getOption('columns')[columnIndex];
return column.editable && !column.cellView && (!column.getReadonly || !column.getReadonly(this.model)) && (!column.getHidden || !column.getHidden(this.model));
},
__insertEditableCell({ column, index, CellView }: { column: Column, index: number, CellView: Marionette.View<Backbone.Model> }) {
const cellView = this.__renderCell({ column, index, CellView });
const cellEl = this.__getCellByColumnIndex(index);
this.el.replaceChild(cellView.el, cellEl);
cellView.triggerMethod('before:attach');
cellView.triggerMethod('attach');
if (this.getOption('isTree') && index === 0) {
this.insertFirstCellHtml(true);
}
this.__updateValidationErrorForColumn({ column, index });
},
__getReadonlyCell(column: Column): string{
return CellViewFactory.getCell(column, this.model);
},
__insertReadonlyCell({ column, index }: { column: Column, index: number }) {
const cell = this.__getReadonlyCell(column);
const cellEl = this.__getCellByColumnIndex(index);
cellEl.outerHTML = cell;
this.cellViewsByKey[column.key]?.destroy();
delete this.cellViewsByKey[column.key];
if (this.getOption('isTree') && index === 0) {
this.insertFirstCellHtml(true);
}
this.__updateValidationErrorForColumn({ column, index });
},
__handleDblClick() {
if (!MobileService.isMobile) {
this.__isDoubleClicked = true;
this.gridEventAggregator.trigger('row:pointer:down', this.model);
this.gridEventAggregator.trigger('dblclick', this.model);
}
},
__handleTouchEnd() {
if (MobileService.isMobile) {
if (this.isTouchMoveEvent) {
this.isTouchMoveEvent = false;
return;
}
this.gridEventAggregator.trigger('row:pointer:down', this.model);
}
},
__handleTouchCancel() {
this.isTouchMoveEvent = false;
},
__handleTouchMove() {
this.isTouchMoveEvent = true;
},
__handleSelection() {
this.el.classList.add(classes.selected);
if (this.gridEventAggregator.pointedCell !== undefined) {
this.__selectPointed(this.gridEventAggregator.pointedCell);
}
},
__handleDeselection() {
this.el.classList.remove(classes.selected);
this.__deselectPointed();
},
__toggleCollapse(event: MouseEvent) {
this.updateCollapsed(this.model);
if (this.model.collapsed === undefined ? false : !this.model.collapsed) {
this.model.collapse();
} else {
this.model.expand();
}
this.trigger('toggle:collapse', this.model);
event.stopPropagation();
},
__onModelChecked() {
this.internalCheck = true;
if (this.model.children?.length) {
this.model.children.forEach((model: GridItemModel) => model.check());
}
this.internalCheck = false;
this.__updateParentChecked();
},
__onModelUnchecked() {
this.internalCheck = true;
if (this.model.children?.length) {
this.model.children.forEach((model: GridItemModel) => model.uncheck());
}
this.internalCheck = false;
this.__updateParentChecked();
},
__updateParentChecked() {
if (this.internalCheck) {
return;
}
const parentModel = this.model.parentModel;
if (parentModel) {
let checkedChildren = 0;
parentModel.children.forEach((child: GridItemModel) => {
if (child.checked) {
checkedChildren++;
}
});
if (checkedChildren === 0) {
parentModel.uncheck();
} else if (parentModel.children.length === checkedChildren) {
parentModel.check();
} else {
parentModel.checkSome();
}
}
},
__deselectPointed() {
if (this.lastPointedIndex > -1) {
const isColumnEditable = this.__isColumnEditable(this.lastPointedIndex);
if (isColumnEditable) {
const column = this.getOption('columns')[this.lastPointedIndex];
this.cellViewsByKey[column.key]?.blur?.();
this.__insertReadonlyCell({ column, index: this.lastPointedIndex });
}
const lastPointedEl = this.__getCellByColumnIndex(this.lastPointedIndex);
lastPointedEl?.classList.remove(classes.cellFocused);
delete this.lastPointedIndex;
delete this.lastFocusEditor;
}
},
__selectPointed(columnIndex: number, focusEditor: boolean = this.lastFocusEditor) {
if (this.lastPointedIndex === columnIndex && this.lastFocusEditor === focusEditor) {
return;
}
if (this.lastPointedIndex > -1 && this.lastPointedIndex !== columnIndex) {
this.__deselectPointed();
}
const isColumnEditable = this.__isColumnEditable(columnIndex);
if (isColumnEditable) {
const column = this.getOption('columns')[columnIndex];
if (focusEditor) {
const cell = this.__getEditableCell(column);
this.__insertEditableCell({ column, index: columnIndex, CellView: cell });
this.cellViewsByKey[column.key]?.focus?.();
} else {
this.__insertReadonlyCell({ column, index: columnIndex })
}
}
this.gridEventAggregator.pointedCell = columnIndex;
const pointedEl = this.__getCellByColumnIndex(columnIndex);
if (!focusEditor || !isColumnEditable) {
pointedEl?.focus();
}
if (this.gridEventAggregator.isEditable) {
pointedEl?.classList.add(classes.cellFocused);
}
this.lastPointedIndex = columnIndex;
this.lastFocusEditor = focusEditor;
},
__someFocused(nodeList: NodeList) {
const someFunction = (node: Node) => document.activeElement === node || node.contains(document.activeElement);
return Array.prototype.some.call(nodeList, someFunction);
},
__handleEnter(e: KeyboardEvent) {
this.__selectPointed(this.gridEventAggregator.pointedCell, true);
},
__handleExit(e: KeyboardEvent) {
this.__selectPointed(this.gridEventAggregator.pointedCell, false);
},
__getFocusedColumnIndex(e: MouseEvent): number {
const elIndex = [...this.el.children].findIndex((cell: Element) => cell.contains(<Node>e.target));
return this.options.showCheckbox ? elIndex - 1 : elIndex;
},
__blink() {
this.el.classList.add(classes.hover__transition);
this.el.classList.add(classes.hover);
setTimeout(() => this.el.classList.remove(classes.hover), config.TRANSITION_DELAY);
setTimeout(() => this.el.classList.remove(classes.hover__transition), config.TRANSITION_DELAY * 2);
// TODO: scrollIntoViewIfNeeded, IE, top?
if (this.el.getBoundingClientRect().bottom > window.innerHeight) {
this.el.scrollIntoView(false);
}
},
__updateState(model: GridItemModel, checkedState: 'checked' | 'unchecked' | 'checkedSome' | null) {
let state = checkedState;
if (!state) {
if (this.model.checked) {
state = 'checked';
} else if (this.model.checked === null) {
state = 'checkedSome';
}
}
let innerHTML
const checkbox = this.ui.checkbox.get(0);
switch (state) {
case 'checked':
innerHTML = '<i class="fas fa-check"></i>';
this.el.classList.add(classes.rowChecked);
this.el.classList.remove(classes.rowCheckedSome);
break;
case 'checkedSome':
innerHTML = '<i class="fas fa-square"></i>';
this.el.classList.add(classes.rowChecked, classes.rowCheckedSome);
break;
case 'unchecked':
default:
innerHTML = '';
this.el.classList.remove(classes.rowChecked, classes.rowCheckedSome);
break;
}
if (checkbox) {
checkbox.innerHTML = innerHTML;
}
},
__setCellHidden({ column, index, isHidden } : { column: Column, index: number, isHidden: boolean }) {
if (this.cellConfigs[column.key].isHidden === isHidden) {
return;
}
const isTree = this.getOption('isTree');
this.cellConfigs[column.key].isHidden = isHidden;
const oldCellView = this.cellViewsByKey[column.key];
this.__insertReadonlyCell({ column, index });
if (oldCellView) {
oldCellView.destroy();
}
},
__renderCell({ column, index, CellView }: { column: Column, index: number, CellView: Marionette.View<any> }) {
const cellView = new CellView({
class: `${classes.cell} ${column.customClass || ''} ${column.columnClass || ''} ${this.__getDropdownClass(column)}`,
tagName: 'td',
schema: column,
model: this.model,
key: column.key
});
cellView.el.setAttribute('tabindex', -1);
cellView.render();
this.cellViewsByKey[column.key] = cellView;
return cellView;
},
__getDropdownClass(column: Column): string {
switch (column.dataType || column.type) {
case objectPropertyTypes.INSTANCE:
case objectPropertyTypes.DOCUMENT:
case objectPropertyTypes.ACCOUNT:
case objectPropertyTypes.ENUM:
case objectPropertyTypes.IMAGE:
return classes.dropdownRoot;
default:
return '';
}
},
__onValidated() {
this.__updateValidationErrors({ onValidated: true});
},
__updateValidationErrors({ onValidated } : { onValidated?: boolean } = {}) {
if (!onValidated && !this.model.validationError) {
return;
}
this.options.columns.forEach((column: Column, index: number) => this.__updateValidationErrorForColumn({ column, index }));
},
__updateValidationErrorForColumn({ column, index }: { column: Column, index: number }) {
const cellEl = this.__getCellByColumnIndex(index);
const columnError = this.model.validationError?.[column.key];
if (columnError) {
const oldErrorButton = cellEl.querySelector(`.${classes.errorButton}`);
if (oldErrorButton) {
cellEl.removeChild(oldErrorButton);
}
let severityPart;
if (Array.isArray(columnError) && columnError.every(error => error.severity?.toLowerCase() === validationSeverityTypes.WARNING)) {
cellEl.classList.add(validationSeverityClasses.WARNING);
severityPart = validationSeverityClasses.WARNING;
} else {
cellEl.classList.add(validationSeverityClasses.ERROR);
severityPart = validationSeverityClasses.ERROR;
}
const errorButton = `<i class="${classes.errorButton} form-label__${severityPart}-button popout__action-btn"></i>`;
cellEl.insertAdjacentHTML('beforeend', errorButton);
} else {
cellEl.classList.remove(validationSeverityClasses.ERROR);
cellEl.classList.remove(validationSeverityClasses.WARNING);
const errorEl = cellEl.querySelector(`.${classes.errorButton}`);
if (errorEl) {
errorEl.parentElement?.removeChild(errorEl);
}
}
},
__showErrorsForColumn({ column, index } : { column: Column, index: number }) {
if (this.errorShownIndex === index) {
return;
}
const element = this.__getCellByColumnIndex(index);
const errors = this.model.validationError[column.key];
this.errorCollection ? this.errorCollection.reset(errors) : (this.errorCollection = new Backbone.Collection(errors));
this.errorPopout = dropdown.factory.createPopout({
buttonView: Marionette.View,
buttonModel: new Backbone.Model({ errorCollection: this.errorCollection }),
panelView: ErrorsPanelView,
panelViewOptions: {
collection: this.errorCollection
},
popoutFlow: 'right',
element,
autoOpen: false
});
this.errorPopout.open();
this.errorPopout.on('close', () => delete this.errorShownIndex);
this.errorShownIndex = index;
}
});