UNPKG

@true-directive/base

Version:

The set of base classes for the TrueDirective Grid

530 lines (529 loc) 22.8 kB
/** * Copyright (c) 2018-2019 Aleksey Melnikov, True Directive Company. * @link https://truedirective.com/ * @license MIT */ import { GridPart } from './enums'; import { Column } from './column.class'; import { ColumnBand } from './column-band.class'; import { GridLayoutSelection } from './grid-layout-selection.class'; /** * Разметка секции грида. * Содержит колонки только своей секции * Пересчитывает их ширину * Содержит информацию о выделенных областях своей секции * @return [description] */ var GridLayout = /** @class */ (function () { function GridLayout(place) { this.place = place; // Уровни группировки генерируются вместе с первой видимой колонкой, но не // считая чекбокса. this.groupLevels = false; this.columns = []; this.bands = []; this.selection = new GridLayoutSelection(); // Вспомогательное поле для идентификации лэйаута this.tag = ''; this._levelColumns = []; this._autoWidth = false; } Object.defineProperty(GridLayout.prototype, "levelColumns", { // Список виртуальных колонок для отступов get: function () { return this._levelColumns; }, enumerable: true, configurable: true }); Object.defineProperty(GridLayout.prototype, "isLeft", { // Находится ли эта разметка слева get: function () { return this.place === GridPart.LEFT; }, enumerable: true, configurable: true }); Object.defineProperty(GridLayout.prototype, "isCenter", { // Находится ли эта разметка в основной части get: function () { return this.place === GridPart.CENTER; }, enumerable: true, configurable: true }); Object.defineProperty(GridLayout.prototype, "isRight", { // Находится ли эта разметка справа get: function () { return this.place === GridPart.RIGHT; }, enumerable: true, configurable: true }); // Обновить список полей группировки GridLayout.prototype.updateGroupedColumns = function (groupedColumns) { var _this = this; if (groupedColumns === null) { this._levelColumns = []; return; } var lc = []; if (groupedColumns.length > 0) { lc.push(new Column('_', '', this._levelIndent)); // Для стрелки } groupedColumns.forEach(function (col, index) { lc.push(new Column('__group_' + index, '', _this._levelIndent)); }); this._levelColumns = lc; }; // Обновление отступов для дерева GridLayout.prototype.updateTreeColumns = function (maxLevel) { var lc = []; if (maxLevel > 0) { // Для стрелки lc.push(new Column('_', '', this._levelIndent)); } for (var i = 0; i < maxLevel; i++) { lc.push(new Column('__group_' + i, '', this._levelIndent)); } this._levelColumns = lc; }; Object.defineProperty(GridLayout.prototype, "levelsWidth", { // Суммарная ширина отступов get: function () { return this._levelColumns.length * this._levelIndent; }, enumerable: true, configurable: true }); // Ширина колонки GridLayout.prototype.columnDataWidth = function (col) { return col.width; }; // Ширина колонки в заголовке GridLayout.prototype.columnHeaderWidth = function (col) { // Отступы уровней добавляем к первой не чекбоксовой колонке for (var i = 0; i < this.columns.length; i++) { var c = this.columns[i]; if (c.isCheckbox) { continue; } // Если это убрать, то нужно сделать синхронно с row.directive if (c === col) { return (col.displayedWidth + this.levelsWidth); } break; } return col.displayedWidth; }; GridLayout.prototype.displayedHeaderWidth = function (col) { // Если нет группировок или уровней, то легко if (this._levelColumns.length === 0) { return col.displayedWidthU; } return this.columnHeaderWidth(col) + this._widthUnit; }; Object.defineProperty(GridLayout.prototype, "headerWidth", { // Суммарная ширина всех заголовков get: function () { if (this.place === GridPart.CENTER) { var ww = this.totalWidth + this.levelsWidth; if (this._widthUnit === 'px') { ww += 96; } else { ww += 10; } return ww + this._widthUnit; } return this.totalWidth + this.levelsWidth + this._widthUnit; }, enumerable: true, configurable: true }); Object.defineProperty(GridLayout.prototype, "dataWidth", { // Суммарная ширина данных get: function () { var ww = this.totalWidth + this.levelsWidth; if (this._autoWidth) { return ww + 'px'; } return ww + this._widthUnit; }, enumerable: true, configurable: true }); // Количество видимых колонок с учетом временно вынесенной в панель группировки GridLayout.prototype.visibleColumnCount = function (groupedTemp) { var res = 0; for (var i = 0; i < this.columns.length; i++) { var col = this.columns[i]; if (groupedTemp && groupedTemp.fieldName === col.fieldName) { continue; } if (col.visible) { res++; } } return res; }; // Обновление разметки GridLayout.prototype.update = function (columns, widthUnit, levelIndent, clientWidth, autoWidth) { if (widthUnit === void 0) { widthUnit = 'px'; } if (levelIndent === void 0) { levelIndent = 0; } if (clientWidth === void 0) { clientWidth = 0; } if (autoWidth === void 0) { autoWidth = false; } this._levelIndent = levelIndent; this._widthUnit = widthUnit; this.bands = []; this.columns = []; var gLevels = false; if (columns !== undefined) { var currentBand = ''; var currentBandColumns = []; var hasBands = false; var bandWidth = 0; var isFirstColumn = true; for (var _i = 0, columns_1 = columns; _i < columns_1.length; _i++) { var column = columns_1[_i]; if (!column.visible) { continue; } if (!column.isCheckbox && isFirstColumn) { if (column.fixed === this.place) { gLevels = true; } isFirstColumn = false; } if (column.fixed !== this.place && this.place !== GridPart.GROUPED_COLUMN) { // в сгруппированном всегда показываем continue; } var ww = column.width; if (column.isCheckbox) { ww = this._levelIndent; } this.columns.push(column); if (column.band !== currentBand) { if (currentBand) { var band = new ColumnBand(currentBand, currentBandColumns, bandWidth); this.bands.push(band); } hasBands = true; currentBandColumns = []; currentBand = column.band; bandWidth = 0; } currentBandColumns.push(column); bandWidth += ww; } if (hasBands) { this.bands.push(new ColumnBand(currentBand, currentBandColumns, bandWidth)); } } this.resize(widthUnit, levelIndent, clientWidth, autoWidth); this.groupLevels = gLevels; }; // Минимальная ширина с заданным набором колонок GridLayout.prototype.minWidth = function () { var res = this.levelsWidth; this.columns.forEach(function (c) { return res += c.autoWidthFixed ? c.width : (c.autoWidthMin ? c.autoWidthMin : 0); }); return res; }; // Убираем одну колонку с конца с наименьшим приоритетом GridLayout.prototype.removeColumnWithLowPriority = function () { var min = undefined; this.columns.forEach(function (c) { if (min === undefined || c.autoWidthPriority < min) { min = c.autoWidthPriority; } }); if (min === undefined) { return false; } var _loop_1 = function (i) { var c = this_1.columns[i]; if (c.autoWidthPriority === min) { this_1.columns.splice(i, 1); // Убираем колонку из бэнда и сам бэнд, если он опустел var band = this_1.bands.find(function (b) { return b.columns.indexOf(c) >= 0; }); if (band) { band.removeColumn(c); if (band.columns.length === 0) { this_1.bands.splice(this_1.bands.indexOf(band), 1); } } return { value: true }; } }; var this_1 = this; for (var i = this.columns.length - 1; i >= 0; i--) { var state_1 = _loop_1(i); if (typeof state_1 === "object") return state_1.value; } return false; }; GridLayout.prototype.fixedSize = function (c) { // autoWidthMin может быть не задана для чекбокса, например if (c.autoWidthFixed) { return c.width; } return isNaN(c.autoWidthMin) ? c.width : c.autoWidthMin; }; // GridLayout.prototype.calcWidth = function (fixedSizeColumns, clientWidth) { var _this = this; var res = []; var fixedSize = 0; fixedSizeColumns.forEach(function (c) { return fixedSize += _this.fixedSize(c); }); var remains = clientWidth - fixedSize - this.levelsWidth; var totalWidth = 0; this.columns.forEach(function (c) { if (fixedSizeColumns.indexOf(c) < 0) { totalWidth += c.width; } }); this.columns.forEach(function (c) { if (fixedSizeColumns.indexOf(c) >= 0) { res.push({ c: c, width: _this.fixedSize(c) }); } else { res.push({ c: c, width: Math.floor(c.width * remains / totalWidth) }); } }); return res; }; // Автоматический пересчет ширины колонок GridLayout.prototype.resize = function (widthUnit, levelIndent, clientWidth, autoWidth) { this._autoWidth = autoWidth; this.totalWidth = 0; if (this.columns.length === 0) { return; } var sizes = []; if (autoWidth) { // Удалим колонки, которые никак не влезут var res = true; while (autoWidth && this.minWidth() > clientWidth && res) { res = this.removeColumnWithLowPriority(); } // Последовательно фиксируем ширину колонок, если расчетная меньше минимальной var fixedCols_1 = []; var recalc_1 = true; while (autoWidth && recalc_1) { recalc_1 = false; sizes = this.calcWidth(fixedCols_1, clientWidth); sizes.some(function (s) { // Проверяем, устроит ли нас длина if (fixedCols_1.indexOf(s.c) < 0 && (s.width < s.c.autoWidthMin || s.c.autoWidthFixed || s.c.isCheckbox)) { fixedCols_1.push(s.c); // Не устроила recalc_1 = true; return true; } return false; }); } } // Фиксируем var newTotalWidth = 0; var firstCol = true; var _loop_2 = function (i) { var c = this_2.columns[i]; c.displayedWidth = c.width; c.displayedWidthU = c.width + this_2._widthUnit; if (autoWidth) { c.displayedWidth = sizes.find(function (s) { return s.c === c; }).width; c.displayedWidthU = c.displayedWidth + 'px'; } if (firstCol && !c.isCheckbox) { c.headerWidth = c.displayedWidth + this_2.levelsWidth; firstCol = false; } else { c.headerWidth = c.displayedWidth; } newTotalWidth += c.displayedWidth; }; var this_2 = this; for (var i = 0; i < this.columns.length; i++) { _loop_2(i); } // Отброшенные дробные части плюсуем к первой колонке if (autoWidth && this.columns.length > 0) { this.columns[0].displayedWidth += clientWidth - newTotalWidth - this.levelsWidth; this.columns[0].displayedWidthU = this.columns[0].displayedWidth + 'px'; } this.totalWidth = newTotalWidth; }; /** * Is it possible to reorder the column at the specified coordinates? * @param mouseAction User action info with mouse coordinates * @param items List of column headers or bands * @param r0 Header bounding rectangle * @param hasL We have part positioned to left of this part * @param hasR We have part positioned to right of this part * @param columns Columns collection of the grid * @return Possibility of reordering, position */ GridLayout.prototype.canDrop = function (mouseAction, items, r0, hasL, hasR, columns) { var result = null; var tg = mouseAction.target; if (mouseAction.y < r0.top) { return result; } var isColumn = tg instanceof Column; var isBand = tg instanceof ColumnBand; if (mouseAction.y < r0.top) { return result; } if (items.length === 0 && mouseAction.x >= r0.left && mouseAction.x < r0.right) { return { inColumns: isColumn, item: null, pos: 'left', place: this.place }; } var mrX = 0; var cbCol = columns.prevCheckbox(tg); // Необходимо перебрать колонки и понять, сможем ли мы бросить сюда наш заголовок. for (var i = 0; i < items.length; i++) { var isFirst = i === 0; var isLast = i === items.length - 1; var rr = items[i].boundingRect; var item = items[i].item; if (mouseAction.inItemRect(r0, rr)) { // проверяем, можно ли вставить колонку сюда.. var canDropLeft = true; var canDropRight = true; if (isColumn && tg.fieldName === item.fieldName) { // Навели на себя же canDropLeft = false; canDropRight = false; } if (cbCol && cbCol.fieldName === item.fieldName) { // Навели на чекбокс, который прилеплен к перетаскиваемой колонке canDropLeft = false; canDropRight = false; } if (item.isCheckbox && i < items.length - 1) { canDropRight = false; // между чекбоксом и норм столбцом не вклиниваемся } // Не самый первый элемент if (!isFirst) { var prevItem = items[i - 1].item; // Простая проверка - для колонки if (isColumn && prevItem.fieldName === tg.fieldName) { canDropLeft = false; } if (isColumn && prevItem.isCheckbox) { if (cbCol && cbCol.fieldName === prevItem.fieldName) { canDropLeft = false; } } if (isBand && tg.columns[tg.columns.length - 1].fieldName === prevItem.columns[prevItem.columns.length - 1].fieldName) { canDropLeft = false; } } // Бэнд if (isBand) { // Нельзя бросить бэнд слева от себя if (tg.columns[0].fieldName === item.columns[0].fieldName) { canDropLeft = false; } // Нельзя бросить справа от себя if (tg.columns[tg.columns.length - 1].fieldName === item.columns[item.columns.length - 1].fieldName) { canDropRight = false; } // if (tg.columns[0].fieldName === item.columns[item.columns.length - 1].fieldName) { canDropRight = false; } if (tg.columns[tg.columns.length - 1].fieldName === item.columns[0].fieldName) { canDropLeft = false; } } // Не последний элемент if (!isLast) { var nextItem = items[i + 1].item; if (isColumn && nextItem.fieldName === tg.fieldName) { canDropRight = false; } if (isBand && tg.columns[0].fieldName === nextItem.columns[0].fieldName) { canDropRight = false; } if (isColumn && nextItem.isCheckbox) { if (cbCol && cbCol.fieldName === nextItem.fieldName) { canDropRight = false; } } } // Если мы вписываемся в наш компонент, то показываем сразу.. // Иначе нам нужно скрыть и немного проскроллить.. var showMarker = false; if ((mouseAction.x - rr.left < rr.width / 2 || item.isCheckbox) && canDropLeft) { if (i > 0 && items[i - 1].item.isCheckbox) { // Колонка с чекбоксом неразделимы item = items[i - 1].item; rr = items[i - 1].boundingRect; } mrX = rr.left - 1; showMarker = true; result = { inColumns: isColumn, item: item, pos: 'left' }; } else if (mouseAction.x - rr.left >= rr.width / 2 && canDropRight) { mrX = rr.right - 1; showMarker = true; result = { inColumns: isColumn, item: item, pos: 'right' }; } return result; } } return result; }; /** * Returns the column index in the column list by field name * @param fieldName Name of the field to be searched * @return Column index */ GridLayout.columnIndex = function (layouts, fieldName) { var i = 0; var res = -1; layouts.forEach(function (l) { for (var j = 0; j < l.columns.length && res < 0; j++) { if (l.columns[j].fieldName === fieldName) { res = i; break; } i++; } }); return res; }; GridLayout.columnByIndex = function (layouts, index) { var i = 0; var res = null; layouts.forEach(function (l) { for (var j = 0; j < l.columns.length; j++) { if (i === index) { res = l.columns[j]; break; } i++; } }); return res; }; GridLayout.columnCount = function (layouts) { var res = 0; layouts.forEach(function (l) { res += l.columns.length; }); return res; }; GridLayout.firstColumn = function (layouts) { return GridLayout.columnByIndex(layouts, 0); }; GridLayout.lastColumn = function (layouts) { return GridLayout.columnByIndex(layouts, GridLayout.columnCount(layouts) - 1); }; return GridLayout; }()); export { GridLayout };