blob: 904708495d0ec8a7e89e463c27f656f080970a9f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014-2018 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
scout.TableHeaderMenu = function() {
scout.TableHeaderMenu.parent.call(this);
this.column = null;
this.tableHeader = null;
this.table = null;
this.filter = null;
this.filterCheckedMode = scout.TableHeaderMenu.CheckedMode.ALL;
this.filterSortMode = scout.TableHeaderMenu.SortMode.ALPHABETICALLY;
this.hasFilterTable = false;
this.hasFilterFields = false;
this.leftGroups = [];
this.moveGroup = null;
this.toBeginButton = null;
this.forwardButton = null;
this.backwardButton = null;
this.toEndButton = null;
this.sortingGroup = null;
this.sortDescButton = null;
this.sortAscAddButton = null;
this.sortDescAddButton = null;
this.columnActionsGroup = null;
this.addColumnButton = null;
this.removeColumnButton = null;
this.modifyColumnButton = null;
this.groupButton = null;
this.groupAddButton = null;
this.barChartButton = null;
this.colorGradient1Button = null;
this.colorGradient2Button = null;
this.$rightGroups = [];
this.$headerItem = null;
this.$columnActions = null;
this.$columnFilters = null;
this.$filterTableGroup = null;
this.$filterToggleChecked = null;
this.$filterTableGroupTitle = null;
this.$filterSortOrder = null;
this.$filterFieldsGroup = null;
this._onColumnMovedHandler = this._onColumnMoved.bind(this);
this._tableHeaderScrollHandler = this._onAnchorScroll.bind(this);
this.on('locationChange', this._onLocationChange.bind(this));
// Make sure the actions are not disabled even if the table is disabled
// To disable the menu use headerEnabled or headerMenusEnabled
this.inheritAccessibility = false;
};
scout.inherits(scout.TableHeaderMenu, scout.Popup);
scout.TableHeaderMenu.CheckedMode = {
ALL: {
checkAll: true,
text: 'ui.SelectAllFilter'
},
NONE: {
checkAll: false,
text: 'ui.SelectNoneFilter'
}
};
scout.TableHeaderMenu.SortMode = {
ALPHABETICALLY: {
text: 'ui.SortAlphabeticallyFilter',
cssClass: 'table-header-menu-toggle-sort-order-alphabetically'
},
AMOUNT: {
text: 'ui.SortByAmountFilter',
cssClass: 'table-header-menu-toggle-sort-order-amount'
}
};
scout.TableHeaderMenu.prototype._init = function(options) {
options.scrollType = options.scrollType || 'none';
scout.TableHeaderMenu.parent.prototype._init.call(this, options);
this.tableHeader = options.tableHeader;
this.column = options.column;
this.table = this.tableHeader.table;
this.$headerItem = this.$anchor;
this.table.on('columnMoved', this._onColumnMovedHandler);
// Filtering
this.filter = this.table.getFilter(this.column.id);
if (!this.filter) {
this.filter = this.column.createFilter();
}
// always recalculate available values to make sure new/updated/deleted rows are considered
this.filter.calculate();
this.filter.on('filterFieldsChanged', this._updateFilterTable.bind(this)); // TODO [7.0] awe: (filter) off handler?
this._updateFilterTableCheckedMode();
this.hasFilterTable = this.filter.availableValues.length > 0;
this.hasFilterFields = this.filter.hasFilterFields;
if (this.hasFilterTable) {
this._tableFilterHandler = this._onFilterTableChanged.bind(this);
this.table.on('filterAdded', this._tableFilterHandler);
this.table.on('filterRemoved', this._tableFilterHandler);
this._filterTableRowsCheckedHandler = this._onFilterTableRowsChecked.bind(this);
}
};
scout.TableHeaderMenu.prototype._createLayout = function() {
return new scout.TableHeaderMenuLayout(this);
};
scout.TableHeaderMenu.prototype._render = function() {
this.leftGroups = [];
this.$rightGroups = [];
this.$headerItem.select(true);
this.$container = this.$parent.appendDiv('table-header-menu');
this.htmlComp = scout.HtmlComponent.install(this.$container, this.session);
this.htmlComp.setLayout(this._createLayout());
this.$body = this.$container.appendDiv('table-header-menu-body');
scout.HtmlComponent.install(this.$body, this.session);
this._installScrollbars({
axis: 'y'
});
this.$columnActions = this.$body.appendDiv('table-header-menu-actions');
// only add right column if filter has a filter-table or filter-fields
if (this.hasFilterTable || this.hasFilterFields) {
this.$columnFilters = this.$body.appendDiv('table-header-menu-filters');
var htmlColumnFilters = scout.HtmlComponent.install(this.$columnFilters, this.session);
htmlColumnFilters.setLayout(new scout.RowLayout());
}
this.tableHeader.$container.on('scroll', this._tableHeaderScrollHandler);
this.$whiter = this.$container.appendDiv('table-header-menu-whiter');
if (this.withFocusContext && this.focusableContainer) {
this.$container.attr('tabindex', -1);
}
// -- Left column -- //
// Moving
var movableColumns = this.table.visibleColumns().filter(function(column) {
return !column.fixedPosition;
});
if (movableColumns.length > 1 && !this.column.fixedPosition) {
this.leftGroups.push(this._renderMovingGroup());
}
// Sorting
if (this.table.sortEnabled) {
this.leftGroups.push(this._renderSortingGroup());
}
// Add/remove/change columns
if (this._isColumnActionsGroupVisible()) {
this.leftGroups.push(this._renderColumnActionsGroup());
}
// Grouping
// column.grouped check necessary to make ungroup possible, even if grouping is not possible anymore
if (this.table.isGroupingPossible(this.column) || this.column.grouped) {
this.leftGroups.push(this._renderGroupingGroup());
}
// Expand/Collapse
this.leftGroups.push(this._renderHierarchyGruop());
// Aggregation
if (this.table.isAggregationPossible(this.column)) {
this.leftGroups.push(this._renderAggregationGroup());
}
// Coloring
if (this.column instanceof scout.NumberColumn) {
this.leftGroups.push(this._renderColoringGroup());
}
// -- Right column -- //
// Filter table
if (this.hasFilterTable) {
this.$rightGroups.push(this._renderFilterTable());
}
// Filter fields
if (this.hasFilterFields) {
this.$rightGroups.push(this._renderFilterFields());
}
this._onColumnMoved();
// Set table style to focused, so that it looks as it still has the focus.
if (this.table.enabled) {
this.table.$container.addClass('focused');
}
};
/**
* @override
*/
scout.TableHeaderMenu.prototype.get$Scrollable = function() {
return this.$body;
};
scout.TableHeaderMenu.prototype._updateFirstLast = function() {
addFirstLastClass(this.leftGroups.filter(function(group) {
return group.isVisible();
}));
addFirstLastClass(this.$rightGroups);
function addFirstLastClass(groups) {
groups.forEach(function(group, index, arr) {
toggleCssClass(group, 'first', index === 0);
toggleCssClass(group, 'last', index === arr.length - 1);
}, this);
}
// Note: we should refactor code for filter-fields and filter-table so they could also
// work with a model-class (like the button menu groups). Currently this would cause
// to much work.
function toggleCssClass(group, cssClass, condition) {
var $container = group instanceof scout.TableHeaderMenuGroup ? group.$container : group;
$container.toggleClass(cssClass, condition);
}
};
scout.TableHeaderMenu.prototype._remove = function() {
if (this.filterTable) {
this.filterTable.off('rowsChecked', this._filterTableRowsCheckedHandler);
}
this.tableHeader.$container.off('scroll', this._tableHeaderScrollHandler);
this.$headerItem.select(false);
this.table.off('columnMoved', this._onColumnMovedHandler);
this.table.off('filterAdded', this._tableFilterHandler);
this.table.off('filterRemoved', this._tableFilterHandler);
scout.TableHeaderMenu.parent.prototype._remove.call(this);
// table may have been removed in the meantime
if (this.table.rendered) {
this.table.$container.removeClass('focused');
}
};
scout.TableHeaderMenu.prototype._renderMovingGroup = function() {
var table = this.table,
column = this.column,
pos = table.visibleColumns().indexOf(column);
this.moveGroup = scout.create('TableHeaderMenuGroup', {
parent: this,
textKey: 'ui.Move',
cssClass: 'first'
});
this.toBeginButton = scout.create('TableHeaderMenuButton', {
parent: this.moveGroup,
text: '${textKey:ui.toBegin}',
cssClass: 'move move-top'
});
this.toBeginButton.on('action', function() {
table.moveColumn(column, pos, 0);
pos = table.visibleColumns().indexOf(column);
});
this.forwardButton = scout.create('TableHeaderMenuButton', {
parent: this.moveGroup,
text: '${textKey:ui.forward}',
cssClass: 'move move-up'
});
this.forwardButton.on('action', function() {
table.moveColumn(column, pos, Math.max(pos - 1, 0));
pos = table.visibleColumns().indexOf(column);
});
this.backwardButton = scout.create('TableHeaderMenuButton', {
parent: this.moveGroup,
text: '${textKey:ui.backward}',
cssClass: 'move move-down'
});
this.backwardButton.on('action', function() {
table.moveColumn(column, pos, Math.min(pos + 1, table.header.findHeaderItems().length - 1));
pos = table.visibleColumns().indexOf(column);
});
this.toEndButton = scout.create('TableHeaderMenuButton', {
parent: this.moveGroup,
text: '${textKey:ui.toEnd}',
cssClass: 'move move-bottom'
});
this.toEndButton.on('action', function() {
table.moveColumn(column, pos, table.header.findHeaderItems().length - 1);
pos = table.visibleColumns().indexOf(column);
});
this.moveGroup.render(this.$columnActions);
return this.moveGroup;
};
scout.TableHeaderMenu.prototype._onColumnMoved = function() {
var table = this.table,
column = this.column;
if (this.moveGroup) {
var visibleColumns = table.visibleColumns();
var columnIndex = table.visibleColumns().indexOf(column);
var forwardEnabled = visibleColumns[columnIndex - 1] && !visibleColumns[columnIndex - 1].fixedPosition;
var backwardEnabled = visibleColumns[columnIndex + 1] && !visibleColumns[columnIndex + 1].fixedPosition;
this.toBeginButton.setEnabled(forwardEnabled);
this.forwardButton.setEnabled(forwardEnabled);
this.backwardButton.setEnabled(backwardEnabled);
this.toEndButton.setEnabled(backwardEnabled);
}
this.hierarchyGroup.setVisible(this.table.isTableNodeColumn(column));
this._updateFirstLast();
};
scout.TableHeaderMenu.prototype._isColumnActionsGroupVisible = function() {
return this.table.columnAddable || this.column.removable || this.column.modifiable;
};
scout.TableHeaderMenu.prototype._renderColumnActionsGroup = function() {
var column = this.column,
menuPopup = this;
this.columnActionsGroup = scout.create('TableHeaderMenuGroup', {
parent: this,
textKey: 'ui.Column'
});
this.addColumnButton = scout.create('TableHeaderMenuButton', {
parent: this.columnActionsGroup,
text: '${textKey:ui.addColumn}',
cssClass: 'add-column',
visible: this.table.columnAddable
});
this.addColumnButton.on('action', onClick.bind(this, 'add'));
this.removeColumnButton = scout.create('TableHeaderMenuButton', {
parent: this.columnActionsGroup,
text: '${textKey:ui.removeColumn}',
cssClass: 'remove-column',
visible: this.column.removable
});
this.removeColumnButton.on('action', onClick.bind(this, 'remove'));
this.modifyColumnButton = scout.create('TableHeaderMenuButton', {
parent: this.columnActionsGroup,
text: '${textKey:ui.changeColumn}',
cssClass: 'change-column',
visible: this.column.modifiable
});
this.modifyColumnButton.on('action', onClick.bind(this, 'modify'));
this.columnActionsGroup.render(this.$columnActions);
return this.columnActionsGroup;
function onClick(action) {
menuPopup.close();
this.table.trigger('columnOrganizeAction', {
action: action,
column: column
});
}
};
scout.TableHeaderMenu.prototype.onColumnActionsChanged = function(event) {
this.addColumnButton.setVisible(event.addVisible);
this.removeColumnButton.setVisible(event.removeVisible);
this.modifyColumnButton.setVisible(event.modifyVisible);
var groupVisible = this.columnActionsGroup.children.some(function(button) {
return button.visible;
});
this.columnActionsGroup.setVisible(groupVisible);
};
scout.TableHeaderMenu.prototype._renderSortingGroup = function() {
var table = this.table,
column = this.column,
menuPopup = this;
this.sortingGroup = scout.create('TableHeaderMenuGroup', {
parent: this,
textKey: 'ColumnSorting'
});
if (!table.hasPermanentHeadOrTailSortColumns()) {
this.sortAscButton = scout.create('TableHeaderMenuButton', {
parent: this.sortingGroup,
text: '${textKey:ui.ascending}',
cssClass: 'sort sort-asc',
direction: 'asc',
toggleAction: true
});
this.sortAscButton.on('action', onSortClick.bind(this.sortAscButton));
this.sortDescButton = scout.create('TableHeaderMenuButton', {
parent: this.sortingGroup,
text: '${textKey:ui.descending}',
cssClass: 'sort sort-desc',
direction: 'desc',
toggleAction: true
});
this.sortDescButton.on('action', onSortClick.bind(this.sortDescButton));
}
this.sortAscAddButton = scout.create('TableHeaderMenuButton', {
parent: this.sortingGroup,
text: '${textKey:ui.ascendingAdditionally}',
cssClass: 'sort sort-asc-add',
direction: 'asc',
toggleAction: true
});
this.sortAscAddButton.on('action', onSortAdditionalClick.bind(this.sortAscAddButton));
this.sortDescAddButton = scout.create('TableHeaderMenuButton', {
parent: this.sortingGroup,
text: '${textKey:ui.descendingAdditionally}',
cssClass: 'sort sort-desc-add',
direction: 'desc',
toggleAction: true
});
this.sortDescAddButton.on('action', onSortAdditionalClick.bind(this.sortDescAddButton));
this._updateSortingSelectedState();
this.sortingGroup.render(this.$columnActions);
return this.sortingGroup;
function onSortClick() {
menuPopup.close();
sort(this.direction, false, !this.selected);
}
function onSortAdditionalClick() {
menuPopup.close();
sort(this.direction, true, !this.selected);
}
function sort(direction, multiSort, remove) {
table.sort(column, direction, multiSort, remove);
menuPopup._updateSortingSelectedState();
}
};
scout.TableHeaderMenu.prototype._updateSortingSelectedState = function() {
if (!this.table.sortEnabled) {
return;
}
var addIcon,
showAddCommands = false,
sortCount = this._sortColumnCount();
this.sortingGroup.children.forEach(function(button) {
button.setSelected(false);
});
if (sortCount === 1 && !this.table.hasPermanentHeadOrTailSortColumns()) {
if (this.column.sortActive) {
if (this.column.sortAscending) {
this.sortAscButton.setSelected(true);
} else {
this.sortDescButton.setSelected(true);
}
} else {
showAddCommands = true;
}
} else if (sortCount > 1 || this.table.hasPermanentHeadOrTailSortColumns()) {
showAddCommands = true;
if (this.column.sortActive) {
if (this.column.sortAscending) {
this.sortAscAddButton.setSelected(true);
} else {
this.sortDescAddButton.setSelected(true);
}
addIcon = this.column.sortIndex + 1;
this.sortAscAddButton.setIconId(addIcon);
this.sortDescAddButton.setIconId(addIcon);
}
}
this.sortAscAddButton.setVisible(showAddCommands);
this.sortDescAddButton.setVisible(showAddCommands);
};
scout.TableHeaderMenu.prototype._renderGroupingGroup = function() {
var menuPopup = this,
table = this.table,
column = this.column,
groupCount = this._groupColumnCount();
var group = scout.create('TableHeaderMenuGroup', {
parent: this,
textKey: 'ui.Grouping'
});
this.groupButton = scout.create('TableHeaderMenuButton', {
parent: group,
text: '${textKey:ui.groupingApply}',
cssClass: 'group',
additional: false,
toggleAction: true
});
this.groupButton.on('action', groupColumn.bind(this.groupButton));
this.groupAddButton = scout.create('TableHeaderMenuButton', {
parent: group,
text: '${textKey:ui.additionally}',
cssClass: 'group-add',
additional: true,
toggleAction: true
});
this.groupAddButton.on('action', groupColumn.bind(this.groupAddButton));
if (groupCount === 0) {
this.groupAddButton.setVisible(false);
} else if (groupCount === 1 && this.column.grouped) {
this.groupButton.setSelected(true);
this.groupAddButton.setVisible(false);
} else if (groupCount > 1) {
this.groupAddButton.setVisible(true);
}
if (table.hasPermanentHeadOrTailSortColumns() && groupCount > 0) {
// If table has permanent head columns, other columns may not be grouped exclusively -> only enable add button (equally done for sort buttons)
this.groupButton.setVisible(false);
this.groupAddButton.setVisible(true);
}
if (this.column.grouped) {
if (groupCount === 1) {
this.groupAddButton.setSelected(true);
} else if (groupCount > 1) {
this.groupAddButton.setSelected(true);
this.groupAddButton.setIconId(this.column.sortIndex + 1);
}
}
group.render(this.$columnActions);
return group;
function groupColumn() {
var direction = (column.sortIndex >= 0 && !column.sortAscending) ? 'desc' : 'asc';
menuPopup.close();
table.groupColumn(column, this.additional, direction, !this.selected);
}
};
scout.TableHeaderMenu.prototype._renderHierarchyGruop = function() {
var table = this.table,
menuPopup = this;
this.hierarchyGroup = scout.create('TableHeaderMenuGroup', {
parent: this,
textKey: 'ui.Hierarchy',
visible: this.table.isTableNodeColumn(this.column)
});
var collapseAllButton = scout.create('TableHeaderMenuButton', {
parent: this.hierarchyGroup,
text: '${textKey:ui.CollapseAll}',
cssClass: 'hierarchy-collapse-all',
enabled: !!scout.arrays.find(table.rows, function(row) {
return row.expanded && !scout.arrays.empty(row.childRows);
})
});
collapseAllButton.on('action', function() {
menuPopup.close();
table.collapseAll();
});
var expandAllButton = scout.create('TableHeaderMenuButton', {
parent: this.hierarchyGroup,
text: '${textKey:ui.ExpandAll}',
cssClass: 'hierarchy-expand-all',
enabled: !!scout.arrays.find(table.rows, function(row) {
return !row.expanded && !scout.arrays.empty(row.childRows);
})
});
expandAllButton.on('action', function() {
menuPopup.close();
table.expandAll();
});
this.hierarchyGroup.render(this.$columnActions);
return this.hierarchyGroup;
};
scout.TableHeaderMenu.prototype._renderAggregationGroup = function() {
var table = this.table,
column = this.column,
aggregation = column.aggregationFunction,
menuPopup = this,
group = scout.create('TableHeaderMenuGroup', {
parent: this,
textKey: 'ui.Aggregation'
}),
allowedAggregationFunctions = scout.arrays.ensure(column.allowedAggregationFunctions),
isAggregationNoneAllowed = allowedAggregationFunctions.indexOf('none') !== -1;
createHeaderMenuButtonForAggregationFunction('${textKey:ui.Sum}', 'sum');
createHeaderMenuButtonForAggregationFunction('${textKey:ui.Average}', 'avg');
createHeaderMenuButtonForAggregationFunction('${textKey:ui.Minimum}', 'min');
createHeaderMenuButtonForAggregationFunction('${textKey:ui.Maximum}', 'max');
group.children.forEach(function(button) {
button.setSelected(button.aggregation === aggregation);
});
group.render(this.$columnActions);
return group;
function createHeaderMenuButtonForAggregationFunction(text, aggregation) {
if (allowedAggregationFunctions.indexOf(aggregation) !== -1) {
var aggrButton = scout.create('TableHeaderMenuButton', {
parent: group,
text: text,
cssClass: 'aggregation-function ' + aggregation,
aggregation: aggregation,
toggleAction: isAggregationNoneAllowed
});
aggrButton.on('action', onClick.bind(aggrButton));
}
}
function onClick() {
menuPopup.close();
table.changeAggregation(column, this.aggregation === aggregation ? 'none' : this.aggregation);
}
};
scout.TableHeaderMenu.prototype._renderColoringGroup = function() {
var table = this.table,
column = this.column,
menuPopup = this,
backgroundEffect = column.backgroundEffect,
group = scout.create('TableHeaderMenuGroup', {
parent: this,
textKey: 'ui.Coloring'
});
this.colorGradient1Button = scout.create('TableHeaderMenuButton', {
parent: group,
text: '${textKey:ui.fromRedToGreen}',
cssClass: 'color color-gradient1',
backgroundEffect: 'colorGradient1',
toggleAction: true
});
this.colorGradient1Button.on('action', onClick.bind(this.colorGradient1Button));
this.colorGradient2Button = scout.create('TableHeaderMenuButton', {
parent: group,
text: '${textKey:ui.fromGreenToRed}',
cssClass: 'color color-gradient2',
backgroundEffect: 'colorGradient2',
toggleAction: true
});
this.colorGradient2Button.on('action', onClick.bind(this.colorGradient2Button));
if (scout.device.supportsCssGradient()) {
this.barChartButton = scout.create('TableHeaderMenuButton', {
parent: group,
text: '${textKey:ui.withBarChart}',
cssClass: 'color color-bar-chart',
backgroundEffect: 'barChart',
toggleAction: true
});
this.barChartButton.on('action', onClick.bind(this.barChartButton));
}
group.children.forEach(function(button) {
button.setSelected(button.backgroundEffect === backgroundEffect);
});
group.render(this.$columnActions);
return group;
function onClick() {
menuPopup.close();
table.setColumnBackgroundEffect(column, !this.selected ? null : this.backgroundEffect);
}
};
scout.TableHeaderMenu.prototype._renderFilterTable = function() {
var $filterActions;
this.$filterTableGroup = this.$columnFilters
.appendDiv('table-header-menu-group first');
var htmlComp = scout.HtmlComponent.install(this.$filterTableGroup, this.session);
htmlComp.setLayout(new scout.RowLayout());
$filterActions = this.$filterTableGroup
.appendDiv('table-header-menu-filter-actions');
this.$filterSortOrder = $filterActions
.appendDiv('table-header-menu-toggle-sort-order')
.on('click', this._onSortModeClick.bind(this))
.addClass(this.filterSortMode.cssClass);
this.$filterToggleChecked = $filterActions
.appendDiv('table-header-menu-filter-toggle-checked')
.text(this.session.text(this.filterCheckedMode.text))
.on('click', this._onFilterCheckedModeClick.bind(this));
this.$filterTableGroupTitle = this.$filterTableGroup
.appendDiv('table-header-menu-group-text')
.text(this._filterByText());
scout.HtmlComponent.install(this.$filterTableGroupTitle, this.session);
this.filterTable = this._createFilterTable();
this.filterTable.on('rowsChecked', this._filterTableRowsCheckedHandler);
var tableRow, tableRows = [];
this.filter.availableValues.forEach(function(filterValue) {
tableRow = {
cells: [
scout.create('Cell', {
text: (this.filter.column.objectType === 'NumberColumn') ? filterValue.text : null,
value: (this.filter.column.objectType === 'NumberColumn') ? filterValue.key : filterValue.text,
iconId: filterValue.iconId,
htmlEnabled: filterValue.htmlEnabled,
cssClass: filterValue.cssClass
}),
filterValue.count,
filterValue.key === null ? 1 : 0 // empty cell should always be at the bottom
],
checked: this.filter.selectedValues.indexOf(filterValue.key) > -1,
dataMap: {
filterValue: filterValue
}
};
tableRows.push(tableRow);
}, this);
this.filterTable.insertRows(tableRows);
this.filterTable.render(this.$filterTableGroup);
this.filterTable.htmlComp.pixelBasedSizing = true;
// must do this in a setTimeout, since table/popup is not visible yet (same as Table#revealSelection).
setTimeout(this.filterTable.revealChecked.bind(this.filterTable));
return this.$filterTableGroup;
};
scout.TableHeaderMenu.prototype._createFilterTable = function() {
var objectType = 'Column';
if (this.column.objectType === 'NumberColumn') {
objectType = this.column.objectType;
}
return scout.create('Table', {
parent: this,
headerVisible: false,
multiSelect: false,
autoResizeColumns: true,
checkable: true,
checkableStyle: scout.Table.CheckableStyle.TABLE_ROW,
// column-texts are not visible since header is not visible
columns: [{
objectType: objectType,
text: 'filter-value',
width: 160,
sortActive: true,
sortIndex: 1,
horizontalAlignment: -1
}, {
objectType: 'NumberColumn',
text: 'aggregate-count',
width: 40
}, {
objectType: 'NumberColumn',
displayable: false,
sortActive: true,
sortIndex: 0
}]
});
};
/**
* @returns the title-text used for the filter-table
*/
scout.TableHeaderMenu.prototype._filterByText = function() {
var text = this.session.text('ui.Filter'),
numSelected = this.filter.selectedValues.length,
numFilters = this.filter.availableValues.length;
if (numSelected && numFilters) {
text += ' ' + this.session.text('ui.FilterInfoXOfY', numSelected, numFilters);
} else if (numFilters) {
text += ' ' + this.session.text('ui.FilterInfoCount', numFilters);
}
return text;
};
scout.TableHeaderMenu.prototype._onFilterCheckedModeClick = function() {
var checkedMode = scout.TableHeaderMenu.CheckedMode;
var checkAll = this.filterCheckedMode.checkAll;
this.filter.selectedValues = [];
if (this.filterCheckedMode === checkedMode.ALL) {
this.filterCheckedMode = checkedMode.NONE;
this.filter.availableValues.forEach(function(filterValue) {
this.filter.selectedValues.push(filterValue.key);
}, this);
} else {
this.filterCheckedMode = checkedMode.ALL;
}
this.filterTable.checkAll(checkAll);
this._updateFilterTableActions();
};
scout.TableHeaderMenu.prototype._onSortModeClick = function() {
var sortMode = scout.TableHeaderMenu.SortMode;
if (this.filterSortMode === sortMode.ALPHABETICALLY) {
// sort by amount
this.filterTable.sort(this.filterTable.columns[1], 'desc');
this.filterSortMode = sortMode.AMOUNT;
} else {
// sort alphabetically (first by invisible column to make sure empty cells are always at the bottom)
this.filterTable.sort(this.filterTable.columns[2], 'asc');
this.filterTable.sort(this.filterTable.columns[0], 'asc', true);
this.filterSortMode = sortMode.ALPHABETICALLY;
}
this._updateFilterTableActions();
};
scout.TableHeaderMenu.prototype._updateFilterTable = function() {
if (this.filter.filterActive()) {
this.table.addFilter(this.filter);
} else {
this.table.removeFilterByKey(this.column.id);
}
// callback to table
this.table.filter();
};
scout.TableHeaderMenu.prototype._updateFilterTableActions = function() {
// checked mode
this.$filterToggleChecked.text(this.session.text(this.filterCheckedMode.text));
// sort mode
var sortMode = scout.TableHeaderMenu.SortMode;
var sortAlphabetically = this.filterSortMode === scout.TableHeaderMenu.SortMode.ALPHABETICALLY;
this.$filterSortOrder.toggleClass(sortMode.ALPHABETICALLY.cssClass, sortAlphabetically);
this.$filterSortOrder.toggleClass(sortMode.AMOUNT.cssClass, !sortAlphabetically);
};
scout.TableHeaderMenu.prototype._renderFilterFields = function() {
this.filterFieldsGroupBox = scout.create('GroupBox:FilterFields', {
parent: this,
column: this.column,
filter: this.filter
});
this.$filterFieldsGroup = this.$columnFilters.appendDiv('table-header-menu-group');
var htmlComp = scout.HtmlComponent.install(this.$filterFieldsGroup, this.session);
htmlComp.setLayout(new scout.RowLayout());
var $filterFieldsText = this.$filterFieldsGroup
.appendDiv('table-header-menu-group-text')
.text(this.filter.filterFieldsTitle());
htmlComp = scout.HtmlComponent.install($filterFieldsText, this.session);
this.filterFieldsGroupBox.render(this.$filterFieldsGroup);
return this.$filterFieldsGroup;
};
scout.TableHeaderMenu.prototype.isOpenFor = function($headerItem) {
return this.rendered && this.belongsTo($headerItem);
};
scout.TableHeaderMenu.prototype._countColumns = function(propertyName) {
return this.table.visibleColumns().reduce(function(sum, column) {
return sum + (column[propertyName] ? 1 : 0);
}, 0);
};
scout.TableHeaderMenu.prototype._sortColumnCount = function() {
return this._countColumns('sortActive');
};
scout.TableHeaderMenu.prototype._groupColumnCount = function() {
return this._countColumns('grouped');
};
scout.TableHeaderMenu.prototype._computeWhitherWidth = function() {
var $tableHeaderContainer = this.tableHeader.$container,
headerItemWidth = this.$headerItem.outerWidth() - this.$headerItem.cssBorderWidthX(),
containerWidth = this.$container.outerWidth() - this.$container.cssBorderWidthX(),
tableHeaderWidth = $tableHeaderContainer.outerWidth() - this.tableHeader.menuBar.$container.outerWidth();
// if container is wider than header item -> use header item width, otherwise use container width
var whitherWidth = Math.min(headerItemWidth, containerWidth);
// if container is positioned at the right side, header item may not be fully visible (under the menubar or partly invisible due to scrolling)
whitherWidth = Math.min(whitherWidth, tableHeaderWidth - this.$headerItem.position().left);
var clipLeft = $tableHeaderContainer.offset().left - this.$headerItem.offset().left - this.tableHeader.table.$container.cssBorderLeftWidth();
if (clipLeft > 0) {
whitherWidth -= clipLeft;
}
return whitherWidth;
};
scout.TableHeaderMenu.prototype._renderCompact = function() {
this.$body.toggleClass('compact', this.compact);
this.invalidateLayoutTree();
};
scout.TableHeaderMenu.prototype.setCompact = function(compact) {
this.setProperty('compact', compact);
};
scout.TableHeaderMenu.prototype._onLocationChange = function(event) {
var inView, containerBounds,
isLocationInView = scout.scrollbars.isLocationInView,
headerItemBounds = scout.graphics.offsetBounds(this.$headerItem),
$tableHeaderContainer = this.tableHeader.$container;
this.$container.setVisible(true);
containerBounds = scout.graphics.offsetBounds(this.$container);
// menu must only be visible if the header item is in view (menu gets repositioned when the table gets scrolled -> make sure it won't be displayed outside of the table)
// check left side of the header item (necessary if header item is moved outside on the left side of the table)
inView = isLocationInView(new scout.Point(headerItemBounds.x, headerItemBounds.y), $tableHeaderContainer);
if (!inView) {
// if left side of the header is not in view, check if right side of the header and the menu, both must be visible)
// check right side of the header item (necessary if header item is moved outside on the right side of the table)
inView = isLocationInView(new scout.Point(headerItemBounds.x + headerItemBounds.width, headerItemBounds.y + headerItemBounds.height), $tableHeaderContainer);
// check right side of the menu (necessary if header item is larger than menu, and if header item is moved outside on the left side of the table)
inView = inView && isLocationInView(new scout.Point(containerBounds.x + containerBounds.width, containerBounds.y), $tableHeaderContainer);
}
this.$container.setVisible(inView);
// make sure whither is correctly positioned and sized
// (bounds must be computed after setVisible, if it was hidden before bounds are not correct)
containerBounds = scout.graphics.offsetBounds(this.$container);
this.$whiter
// if header is clipped on the left side, position whither at the left of the visible part of the header (same applies for width, see _computeWhitherWidth)
.cssLeft(Math.max(headerItemBounds.x - containerBounds.x, $tableHeaderContainer.offset().left - containerBounds.x - this.tableHeader.table.$container.cssBorderLeftWidth()))
.width(this._computeWhitherWidth());
};
scout.TableHeaderMenu.prototype._onAnchorScroll = function(event) {
this.position();
};
scout.TableHeaderMenu.prototype._onFilterTableRowsChecked = function(event) {
this.filter.selectedValues = [];
this.filterTable.rows.forEach(function(row) {
if (row.checked) {
this.filter.selectedValues.push(row.dataMap.filterValue.key);
}
}, this);
this._updateFilterTable();
};
scout.TableHeaderMenu.prototype._onFilterTableChanged = function() {
this.$filterTableGroupTitle.text(this._filterByText());
this._updateFilterTableCheckedMode();
this._updateFilterTableActions();
};
// When no filter value is selected, we change the selection mode to ALL
// since it makes no sense to choose NONE when no value is currently selected
scout.TableHeaderMenu.prototype._updateFilterTableCheckedMode = function() {
if (this.filter.selectedValues.length === 0) {
this.filterCheckedMode = scout.TableHeaderMenu.CheckedMode.ALL;
} else {
this.filterCheckedMode = scout.TableHeaderMenu.CheckedMode.NONE;
}
};
scout.TableHeaderMenu.prototype._onMouseDownOutside = function(event) {
// close popup only if source of event is not $headerItem or one of it's children.
if (this.$headerItem.isOrHas(event.target)) {
return;
}
this.close();
};
/**
* Called by table header
*/
scout.TableHeaderMenu.prototype.onColumnResized = function() {
// Adjust whiter with if size gets changed while menu is open (may caused by TableHeader._adjustColumnMinWidth)
this.$whiter.width(this._computeWhitherWidth());
};