devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
694 lines (589 loc) • 25.3 kB
JavaScript
"use strict";
var $ = require("../../core/renderer"),
modules = require("./ui.grid_core.modules"),
commonUtils = require("../../core/utils/common"),
windowUtils = require("../../core/utils/window"),
each = require("../../core/utils/iterator").each,
typeUtils = require("../../core/utils/type"),
messageLocalization = require("../../localization/message"),
when = require("../../core/utils/deferred").when;
var TABLE_CLASS = "table",
BORDERS_CLASS = "borders",
TABLE_FIXED_CLASS = "table-fixed",
IMPORTANT_MARGIN_CLASS = "important-margin",
TEXT_CONTENT_CLASS = "text-content",
HIDDEN_CLASS = "dx-hidden",
GRIDBASE_CONTAINER_CLASS = "dx-gridbase-container",
HIDDEN_COLUMNS_WIDTH = "adaptiveHidden",
EDITORS_INPUT_SELECTOR = "input:not([type='hidden'])",
VIEW_NAMES = ["columnsSeparatorView", "blockSeparatorView", "trackerView", "headerPanel", "columnHeadersView", "rowsView", "footerView", "columnChooserView", "filterPanelView", "pagerView", "draggingHeaderView", "contextMenuView", "errorView", "headerFilterView", "filterBuilderView"];
var isPercentWidth = function isPercentWidth(width) {
return typeUtils.isString(width) && width.slice(-1) === "%";
};
var mergeArraysByMaxValue = function mergeArraysByMaxValue(values1, values2) {
var result = [],
i;
if (values1 && values2 && values1.length && values1.length === values2.length) {
for (i = 0; i < values1.length; i++) {
result.push(values1[i] > values2[i] ? values1[i] : values2[i]);
}
} else if (values1 && values1.length) {
result = values1;
} else if (values2) {
result = values2;
}
return result;
};
var getContainerHeight = function getContainerHeight($container) {
var clientHeight = $container.get(0).clientHeight,
paddingTop = parseFloat($container.css("paddingTop")),
paddingBottom = parseFloat($container.css("paddingBottom"));
return clientHeight - paddingTop - paddingBottom;
};
var ResizingController = modules.ViewController.inherit({
_initPostRenderHandlers: function _initPostRenderHandlers() {
var that = this,
dataController = that._dataController;
if (!that._refreshSizesHandler) {
that._refreshSizesHandler = function (e) {
dataController.changed.remove(that._refreshSizesHandler);
var resizeDeferred,
changeType = e && e.changeType,
isDelayed = e && e.isDelayed,
items = dataController.items();
if (!e || changeType === "refresh" || changeType === "prepend" || changeType === "append") {
if (!isDelayed) {
resizeDeferred = that.resize();
}
} else if (changeType === "update") {
if ((items.length > 1 || e.changeTypes[0] !== "insert") && !(items.length === 0 && e.changeTypes[0] === "remove")) {
that._rowsView.resize();
} else {
resizeDeferred = that.resize();
}
}
if (changeType && changeType !== "updateSelection" && !isDelayed) {
when(resizeDeferred).done(function () {
that._setAriaRowColCount();
that.component._fireContentReadyAction();
});
}
};
// TODO remove resubscribing
that._dataController.changed.add(function () {
that._dataController.changed.add(that._refreshSizesHandler);
});
}
},
_setAriaRowColCount: function _setAriaRowColCount() {
var component = this.component;
component.setAria({
"rowCount": this._dataController.totalItemsCount(),
"colCount": component.columnCount()
}, component.$element().children("." + GRIDBASE_CONTAINER_CLASS));
},
_getBestFitWidths: function _getBestFitWidths() {
if (!this.option("legacyRendering")) {
return this._rowsView.getColumnWidths();
}
var that = this,
rowsColumnWidths,
headerColumnWidths,
footerColumnWidths,
resultWidths;
rowsColumnWidths = that._rowsView.getColumnWidths();
headerColumnWidths = that._columnHeadersView && that._columnHeadersView.getColumnWidths();
footerColumnWidths = that._footerView && that._footerView.getColumnWidths();
resultWidths = mergeArraysByMaxValue(rowsColumnWidths, headerColumnWidths);
resultWidths = mergeArraysByMaxValue(resultWidths, footerColumnWidths);
return resultWidths;
},
_setVisibleWidths: function _setVisibleWidths(visibleColumns, widths) {
var columnsController = this._columnsController;
columnsController.beginUpdate();
each(visibleColumns, function (index, column) {
var columnId = column.command ? "command:" + column.command : column.index;
columnsController.columnOption(columnId, "visibleWidth", widths[index]);
});
columnsController.endUpdate();
},
_toggleBestFitModeForView: function _toggleBestFitModeForView(view, className, isBestFit) {
if (!view) return;
var $rowsTable = this._rowsView._getTableElement(),
$viewTable = view._getTableElement(),
$tableBody;
if ($viewTable) {
if (isBestFit) {
$tableBody = $viewTable.children("tbody").appendTo($rowsTable);
} else {
$tableBody = $rowsTable.children("." + className).appendTo($viewTable);
}
$tableBody.toggleClass(className, isBestFit);
$tableBody.toggleClass(this.addWidgetPrefix("best-fit"), isBestFit);
}
},
_toggleBestFitMode: function _toggleBestFitMode(isBestFit) {
var $element = this.component.$element(),
that = this;
if (!this.option("legacyRendering")) {
var $rowsTable = that._rowsView._getTableElement(),
$rowsFixedTable = that._rowsView.getTableElements().eq(1);
$rowsTable.css("tableLayout", isBestFit ? "auto" : "fixed");
$rowsTable.children("colgroup").css("display", isBestFit ? "none" : "");
$rowsFixedTable.toggleClass(this.addWidgetPrefix(TABLE_FIXED_CLASS), !isBestFit);
that._toggleBestFitModeForView(that._columnHeadersView, "dx-header", isBestFit);
that._toggleBestFitModeForView(that._footerView, "dx-footer", isBestFit);
} else {
$element.find("." + this.addWidgetPrefix(TABLE_CLASS)).toggleClass(this.addWidgetPrefix(TABLE_FIXED_CLASS), !isBestFit);
// B253906
$element.find(EDITORS_INPUT_SELECTOR).toggleClass(HIDDEN_CLASS, isBestFit);
$element.find(".dx-group-cell").toggleClass(HIDDEN_CLASS, isBestFit);
$element.find(".dx-header-row ." + this.addWidgetPrefix(TEXT_CONTENT_CLASS)).css("maxWidth", "");
}
},
_synchronizeColumns: function _synchronizeColumns() {
var that = this,
columnsController = that._columnsController,
visibleColumns = columnsController.getVisibleColumns(),
columnAutoWidth = that.option("columnAutoWidth"),
legacyRendering = that.option("legacyRendering"),
needBestFit = that._needBestFit(),
hasMinWidth = false,
resetBestFitMode,
isColumnWidthsCorrected = false,
resultWidths = [],
normalizeWidthsByExpandColumns = function normalizeWidthsByExpandColumns() {
var expandColumnWidth;
each(visibleColumns, function (index, column) {
if (column.command === "expand") {
expandColumnWidth = resultWidths[index];
}
});
each(visibleColumns, function (index, column) {
if (column.command === "expand" && expandColumnWidth) {
resultWidths[index] = expandColumnWidth;
}
});
};
!needBestFit && each(visibleColumns, function (index, column) {
if (column.width === "auto" || legacyRendering && column.fixed) {
needBestFit = true;
return false;
}
});
each(visibleColumns, function (index, column) {
if (column.minWidth) {
hasMinWidth = true;
return false;
}
});
that._setVisibleWidths(visibleColumns, []);
if (needBestFit) {
that._toggleBestFitMode(true);
resetBestFitMode = true;
}
commonUtils.deferUpdate(function () {
if (needBestFit) {
resultWidths = that._getBestFitWidths();
each(visibleColumns, function (index, column) {
var columnId = column.command ? "command:" + column.command : column.index;
columnsController.columnOption(columnId, "bestFitWidth", resultWidths[index], true);
});
} else if (hasMinWidth) {
resultWidths = that._getBestFitWidths();
}
each(visibleColumns, function (index) {
if (this.width !== "auto") {
if (this.width) {
resultWidths[index] = this.width;
} else if (!columnAutoWidth) {
resultWidths[index] = undefined;
}
}
});
if (resetBestFitMode) {
that._toggleBestFitMode(false);
resetBestFitMode = false;
}
isColumnWidthsCorrected = that._correctColumnWidths(resultWidths, visibleColumns);
if (columnAutoWidth) {
normalizeWidthsByExpandColumns();
if (that._needStretch()) {
that._processStretch(resultWidths, visibleColumns);
}
}
commonUtils.deferRender(function () {
if (needBestFit || isColumnWidthsCorrected) {
that._setVisibleWidths(visibleColumns, resultWidths);
}
});
});
},
_needBestFit: function _needBestFit() {
return this.option("columnAutoWidth");
},
_needStretch: function _needStretch() {
return this.option("legacyRendering");
},
_getAverageColumnsWidth: function _getAverageColumnsWidth(resultWidths) {
var contentWidth = this._rowsView.contentWidth(),
totalWidth = this._getTotalWidth(resultWidths, contentWidth),
columnCountWithoutWidth = resultWidths.filter(function (width) {
return width === undefined;
}).length;
return (contentWidth - totalWidth) / columnCountWithoutWidth;
},
_correctColumnWidths: function _correctColumnWidths(resultWidths, visibleColumns) {
var that = this,
i,
hasPercentWidth = false,
hasAutoWidth = false,
isColumnWidthsCorrected = false,
$element = that.component.$element(),
hasWidth = that._hasWidth,
averageColumnsWidth,
lastColumnIndex;
for (i = 0; i < visibleColumns.length; i++) {
var index = i,
column = visibleColumns[index],
isHiddenColumn = resultWidths[index] === HIDDEN_COLUMNS_WIDTH,
width = resultWidths[index];
if (width === undefined && column.minWidth) {
averageColumnsWidth = that._getAverageColumnsWidth(resultWidths);
width = averageColumnsWidth;
}
if (width < column.minWidth && !isHiddenColumn) {
resultWidths[index] = column.minWidth;
isColumnWidthsCorrected = true;
i = -1;
}
if (!column.width) {
hasAutoWidth = true;
}
if (isPercentWidth(column.width)) {
hasPercentWidth = true;
}
}
if ($element && that._maxWidth) {
delete that._maxWidth;
$element.css("maxWidth", "");
}
if (!hasAutoWidth && resultWidths.length) {
var contentWidth = that._rowsView.contentWidth(),
scrollbarWidth = that._rowsView.getScrollbarWidth(),
totalWidth = that._getTotalWidth(resultWidths, contentWidth);
if (totalWidth <= contentWidth) {
lastColumnIndex = resultWidths.length - 1;
while (lastColumnIndex >= 0 && visibleColumns[lastColumnIndex] && (visibleColumns[lastColumnIndex].command || resultWidths[lastColumnIndex] === HIDDEN_COLUMNS_WIDTH || visibleColumns[lastColumnIndex].fixed || visibleColumns[lastColumnIndex].allowResizing === false)) {
lastColumnIndex--;
}
if (lastColumnIndex >= 0) {
resultWidths[lastColumnIndex] = "auto";
isColumnWidthsCorrected = true;
if (!hasWidth && !hasPercentWidth) {
that._maxWidth = totalWidth + scrollbarWidth + (that.option("showBorders") ? 2 : 0);
$element.css("maxWidth", that._maxWidth);
}
}
}
}
return isColumnWidthsCorrected;
},
_processStretch: function _processStretch(resultSizes, visibleColumns) {
var groupSize = this._rowsView.contentWidth(),
tableSize = this._getTotalWidth(resultSizes, groupSize),
unusedIndexes = { length: 0 },
diff,
diffElement,
onePixelElementsCount,
i;
if (!resultSizes.length) return;
each(visibleColumns, function (index) {
if (this.width || resultSizes[index] === HIDDEN_COLUMNS_WIDTH) {
unusedIndexes[index] = true;
unusedIndexes.length++;
}
});
diff = groupSize - tableSize;
diffElement = Math.floor(diff / (resultSizes.length - unusedIndexes.length));
onePixelElementsCount = diff - diffElement * (resultSizes.length - unusedIndexes.length);
if (diff >= 0) {
for (i = 0; i < resultSizes.length; i++) {
if (unusedIndexes[i]) {
continue;
}
resultSizes[i] += diffElement;
if (onePixelElementsCount > 0) {
resultSizes[i]++;
onePixelElementsCount--;
}
}
}
},
_getTotalWidth: function _getTotalWidth(widths, groupWidth) {
var result = 0,
width,
i;
for (i = 0; i < widths.length; i++) {
width = widths[i];
if (width && width !== HIDDEN_COLUMNS_WIDTH) {
result += isPercentWidth(width) ? parseInt(width) * groupWidth / 100 : parseInt(width);
}
}
return Math.round(result);
},
updateSize: function updateSize($rootElement) {
var that = this,
$groupElement,
width,
importantMarginClass = that.addWidgetPrefix(IMPORTANT_MARGIN_CLASS);
if (that._hasHeight === undefined && $rootElement && $rootElement.is(":visible")) {
$groupElement = $rootElement.children("." + that.getWidgetContainerClass());
if ($groupElement.length) {
$groupElement.detach();
}
that._hasHeight = !!getContainerHeight($rootElement);
width = $rootElement.width();
$rootElement.addClass(importantMarginClass);
that._hasWidth = $rootElement.width() === width;
$rootElement.removeClass(importantMarginClass);
if ($groupElement.length) {
$groupElement.appendTo($rootElement);
}
}
},
publicMethods: function publicMethods() {
return ["resize", "updateDimensions"];
},
resize: function resize() {
return !this.component._requireResize && this.updateDimensions();
},
/**
* @name GridBaseMethods.updateDimensions
* @publicName updateDimensions()
*/
updateDimensions: function updateDimensions(checkSize) {
var that = this;
that._initPostRenderHandlers();
// T335767
if (!that._checkSize(checkSize)) {
return;
}
return commonUtils.deferRender(function () {
that._rowsView.height("");
if (that._dataController.isLoaded()) {
that._synchronizeColumns();
}
commonUtils.deferUpdate(function () {
commonUtils.deferRender(function () {
commonUtils.deferUpdate(function () {
that._updateDimensionsCore();
});
});
});
});
},
_checkSize: function _checkSize(checkSize) {
var $rootElement = this.component.$element();
if (checkSize && (this._lastWidth === $rootElement.width() && this._lastHeight === $rootElement.height() || !$rootElement.is(":visible"))) {
return false;
}
return true;
},
_updateDimensionsCore: function _updateDimensionsCore() {
var that = this,
dataController = that._dataController,
rowsView = that._rowsView,
columnHeadersView = that._columnHeadersView,
footerView = that._footerView,
$rootElement = that.component.$element(),
groupElement = $rootElement.children().get(0),
rootElementHeight = $rootElement && ($rootElement.get(0).clientHeight || $rootElement.height()),
maxHeight = parseFloat($rootElement.css("maxHeight")),
maxHeightHappened = maxHeight && rootElementHeight >= maxHeight,
hasHeight = that._hasHeight || maxHeightHappened,
height = that.option("height") || $rootElement.get(0).style.height,
editorFactory = that.getController("editorFactory"),
rowsViewHeight = "",
isMaxHeightApplied = maxHeightHappened && groupElement.scrollHeight === groupElement.offsetHeight,
$testDiv;
that.updateSize($rootElement);
if (height && that._hasHeight ^ height !== "auto") {
$testDiv = $("<div>").height(height).appendTo($rootElement);
that._hasHeight = !!$testDiv.height();
$testDiv.remove();
}
if (isMaxHeightApplied) {
rowsViewHeight = rowsView.height();
}
commonUtils.deferRender(function () {
rowsView.height(rowsViewHeight, hasHeight);
// IE11
if (maxHeightHappened && !isMaxHeightApplied) {
$(groupElement).css("height", maxHeight);
}
if (!dataController.isLoaded()) {
rowsView.setLoading(true);
return;
}
commonUtils.deferUpdate(function () {
that._updateLastSizes($rootElement);
var vScrollbarWidth = hasHeight ? rowsView.getScrollbarWidth() : 0;
var hScrollbarWidth = rowsView.getScrollbarWidth(true);
commonUtils.deferRender(function () {
columnHeadersView && columnHeadersView.setScrollerSpacing(vScrollbarWidth);
footerView && footerView.setScrollerSpacing(vScrollbarWidth);
rowsView.setScrollerSpacing(vScrollbarWidth, hScrollbarWidth);
});
each(VIEW_NAMES, function (index, viewName) {
var view = that.getView(viewName);
if (view) {
view.resize();
}
});
editorFactory && editorFactory.resize();
});
});
},
_updateLastSizes: function _updateLastSizes($rootElement) {
this._lastWidth = $rootElement.width();
this._lastHeight = $rootElement.height();
},
optionChanged: function optionChanged(args) {
switch (args.name) {
case "width":
case "height":
this.component._renderDimensions();
this.resize();
/* falls through */
case "legacyRendering":
args.handled = true;
return;
default:
this.callBase(args);
}
},
init: function init() {
var that = this;
that._dataController = that.getController("data");
that._columnsController = that.getController("columns");
that._columnHeadersView = that.getView("columnHeadersView");
that._footerView = that.getView("footerView");
that._rowsView = that.getView("rowsView");
}
});
var SynchronizeScrollingController = modules.ViewController.inherit({
_scrollChangedHandler: function _scrollChangedHandler(views, pos, viewName) {
for (var j = 0; j < views.length; j++) {
if (views[j] && views[j].name !== viewName) {
views[j].scrollTo({ left: pos.left, top: pos.top });
}
}
},
init: function init() {
var view,
views = [this.getView("columnHeadersView"), this.getView("footerView"), this.getView("rowsView")],
i;
for (i = 0; i < views.length; i++) {
view = views[i];
if (view) {
view.scrollChanged.add(this._scrollChangedHandler.bind(this, views));
}
}
}
});
var GridView = modules.View.inherit({
_endUpdateCore: function _endUpdateCore() {
if (this.component._requireResize) {
this.component._requireResize = false;
this._resizingController.resize();
}
},
_getWidgetAriaLabel: function _getWidgetAriaLabel() {
return "dxDataGrid-ariaDataGrid";
},
init: function init() {
var that = this;
that._resizingController = that.getController("resizing");
that._dataController = that.getController("data");
},
getView: function getView(name) {
return this.component._views[name];
},
element: function element() {
return this._groupElement;
},
optionChanged: function optionChanged(args) {
var that = this;
if (typeUtils.isDefined(that._groupElement) && args.name === "showBorders") {
that._groupElement.toggleClass(that.addWidgetPrefix(BORDERS_CLASS), !!args.value);
args.handled = true;
} else {
that.callBase(args);
}
},
_renderViews: function _renderViews($groupElement) {
var that = this;
each(VIEW_NAMES, function (index, viewName) {
var view = that.getView(viewName);
if (view) {
view.render($groupElement);
}
});
},
_getTableRoleName: function _getTableRoleName() {
return "grid";
},
render: function render($rootElement) {
var that = this,
isFirstRender = !that._groupElement,
$groupElement = that._groupElement || $("<div>").addClass(that.getWidgetContainerClass());
$groupElement.addClass(GRIDBASE_CONTAINER_CLASS);
$groupElement.toggleClass(that.addWidgetPrefix(BORDERS_CLASS), !!that.option("showBorders"));
that.setAria("role", "presentation", $rootElement);
that.component.setAria({
"role": this._getTableRoleName(),
"label": messageLocalization.format(that._getWidgetAriaLabel())
}, $groupElement);
that._rootElement = $rootElement || that._rootElement;
if (isFirstRender) {
that._groupElement = $groupElement;
windowUtils.hasWindow() && that.getController("resizing").updateSize($rootElement);
$groupElement.appendTo($rootElement);
}
that._renderViews($groupElement);
},
update: function update() {
var that = this,
$rootElement = that._rootElement,
$groupElement = that._groupElement,
resizingController = that.getController("resizing");
if ($rootElement && $groupElement) {
resizingController.resize();
if (that._dataController.isLoaded()) {
that.component._fireContentReadyAction();
}
}
}
});
module.exports = {
defaultOptions: function defaultOptions() {
return {
/**
* @name GridBaseOptions.showBorders
* @publicName showBorders
* @type boolean
* @default false
*/
showBorders: false,
legacyRendering: false
};
},
controllers: {
resizing: ResizingController,
synchronizeScrolling: SynchronizeScrollingController
},
views: {
gridView: GridView
}
};