handsontable
Version:
Handsontable is a JavaScript Data Grid available for React, Angular and Vue.
801 lines (763 loc) • 32.5 kB
JavaScript
"use strict";
exports.__esModule = true;
require("core-js/modules/es.error.cause.js");
require("core-js/modules/es.array.push.js");
require("core-js/modules/esnext.iterator.constructor.js");
require("core-js/modules/esnext.iterator.every.js");
var _element = require("../../helpers/dom/element");
var _mixed = require("../../helpers/mixed");
var _object = require("../../helpers/object");
var _function = require("../../helpers/function");
var _array = require("../../helpers/array");
var _base = require("../base");
var _translations = require("../../translations");
var _hooks = require("../../core/hooks");
var _columnStatesManager = require("./columnStatesManager");
var _shortcutContexts = require("../../shortcutContexts");
var _utils = require("./utils");
var _domHelpers = require("./domHelpers");
var _rootComparator = require("./rootComparator");
var _sortService = require("./sortService");
var _a11y = require("../../helpers/a11y");
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
const PLUGIN_KEY = exports.PLUGIN_KEY = 'columnSorting';
const PLUGIN_PRIORITY = exports.PLUGIN_PRIORITY = 50;
const APPEND_COLUMN_CONFIG_STRATEGY = exports.APPEND_COLUMN_CONFIG_STRATEGY = 'append';
const REPLACE_COLUMN_CONFIG_STRATEGY = exports.REPLACE_COLUMN_CONFIG_STRATEGY = 'replace';
const SHORTCUTS_GROUP = PLUGIN_KEY;
(0, _sortService.registerRootComparator)(PLUGIN_KEY, _rootComparator.rootComparator);
_hooks.Hooks.getSingleton().register('beforeColumnSort');
_hooks.Hooks.getSingleton().register('afterColumnSort');
/**
* Tracks the conflicts between `columnSorting` and `multiColumnSorting` options.
* Only one plugin can be enabled for Handsontable instance. Once one of them is enabled,
* the other should remain disabled even if it's set to `true`.
*/
const pluginConflictsState = new WeakMap();
// DIFF - MultiColumnSorting & ColumnSorting: changed configuration documentation.
/**
* @plugin ColumnSorting
* @class ColumnSorting
*
* @description
* This plugin sorts the view by columns (but does not sort the data source!). To enable the plugin, set the
* {@link Options#columnSorting} property to the correct value (see the examples below).
*
* @example
* ```js
* // as boolean
* columnSorting: true
*
* // as an object with initial sort config (sort ascending for column at index 1)
* columnSorting: {
* initialConfig: {
* column: 1,
* sortOrder: 'asc'
* }
* }
*
* // as an object which define specific sorting options for all columns
* columnSorting: {
* sortEmptyCells: true, // true = the table sorts empty cells, false = the table moves all empty cells to the end of the table (by default)
* indicator: true, // true = shows indicator for all columns (by default), false = don't show indicator for columns
* headerAction: true, // true = allow to click on the headers to sort (by default), false = turn off possibility to click on the headers to sort
* compareFunctionFactory: function(sortOrder, columnMeta) {
* return function(value, nextValue) {
* // Some value comparisons which will return -1, 0 or 1...
* }
* }
* }
*
* // as an object passed to the `column` property, allows specifying a custom options for the desired column.
* // please take a look at documentation of `column` property: https://handsontable.com/docs/Options.html#columns
* columns: [{
* columnSorting: {
* indicator: false, // disable indicator for the first column,
* sortEmptyCells: true,
* headerAction: false, // clicks on the first column won't sort
* compareFunctionFactory: function(sortOrder, columnMeta) {
* return function(value, nextValue) {
* return 0; // Custom compare function for the first column (don't sort)
* }
* }
* }
* }]
* ```
*/
var _ColumnSorting_brand = /*#__PURE__*/new WeakSet();
class ColumnSorting extends _base.BasePlugin {
constructor() {
super(...arguments);
/**
* Load saved settings or sort by predefined plugin configuration.
*/
_classPrivateMethodInitSpec(this, _ColumnSorting_brand);
/**
* Instance of column state manager.
*
* @private
* @type {null|ColumnStatesManager}
*/
_defineProperty(this, "columnStatesManager", null);
/**
* Cached column properties from plugin like i.e. `indicator`, `headerAction`.
*
* @private
* @type {null|PhysicalIndexToValueMap}
*/
_defineProperty(this, "columnMetaCache", null);
/**
* Main settings key designed for the plugin.
*
* @private
* @type {string}
*/
_defineProperty(this, "pluginKey", PLUGIN_KEY);
/**
* Plugin indexes cache.
*
* @private
* @type {null|IndexesSequence}
*/
_defineProperty(this, "indexesSequenceCache", null);
}
static get PLUGIN_KEY() {
return PLUGIN_KEY;
}
static get PLUGIN_PRIORITY() {
return PLUGIN_PRIORITY;
}
/**
* Checks if the plugin is enabled in the Handsontable settings. This method is executed in {@link Hooks#beforeInit}
* hook and if it returns `true` then the {@link ColumnSorting#enablePlugin} method is called.
*
* @returns {boolean}
*/
isEnabled() {
return !!this.hot.getSettings()[this.pluginKey];
}
/**
* Enables the plugin functionality for this Handsontable instance.
*/
enablePlugin() {
var _this = this;
if (pluginConflictsState.has(this.hot) && pluginConflictsState.get(this.hot) !== this.pluginKey) {
this.hot.updateSettings({
[this.pluginKey]: false
});
(0, _utils.warnAboutPluginsConflict)(pluginConflictsState.get(this.hot), this.pluginKey);
return;
}
if (this.enabled) {
return;
}
pluginConflictsState.set(this.hot, this.pluginKey);
this.columnStatesManager = new _columnStatesManager.ColumnStatesManager(this.hot, `${this.pluginKey}.sortingStates`);
this.columnMetaCache = new _translations.PhysicalIndexToValueMap(physicalIndex => {
let visualIndex = this.hot.toVisualColumn(physicalIndex);
if (visualIndex === null) {
visualIndex = physicalIndex;
}
return this.getMergedPluginSettings(visualIndex);
});
this.hot.columnIndexMapper.registerMap(`${this.pluginKey}.columnMeta`, this.columnMetaCache);
this.addHook('afterGetColHeader', (column, TH) => _assertClassBrand(_ColumnSorting_brand, this, _onAfterGetColHeader).call(this, column, TH));
this.addHook('beforeOnCellMouseDown', function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _assertClassBrand(_ColumnSorting_brand, _this, _onBeforeOnCellMouseDown).call(_this, ...args);
});
this.addHook('afterOnCellMouseDown', (event, target) => this.onAfterOnCellMouseDown(event, target));
this.addHook('afterInit', () => _assertClassBrand(_ColumnSorting_brand, this, _loadOrSortBySettings).call(this));
this.addHook('afterLoadData', function () {
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return _assertClassBrand(_ColumnSorting_brand, _this, _onAfterLoadData).call(_this, ...args);
});
// TODO: Workaround? It should be refactored / described.
if (this.hot.view) {
_assertClassBrand(_ColumnSorting_brand, this, _loadOrSortBySettings).call(this);
}
this.registerShortcuts();
super.enablePlugin();
}
/**
* Disables the plugin functionality for this Handsontable instance.
*/
disablePlugin() {
const clearColHeader = (column, TH) => {
const headerSpanElement = (0, _utils.getHeaderSpanElement)(TH);
if ((0, _utils.isFirstLevelColumnHeader)(column, TH) === false || headerSpanElement === null) {
return;
}
this.updateHeaderClasses(headerSpanElement);
};
pluginConflictsState.delete(this.hot);
// Changing header width and removing indicator.
this.hot.addHook('afterGetColHeader', clearColHeader);
this.hot.addHookOnce('afterViewRender', () => {
this.hot.removeHook('afterGetColHeader', clearColHeader);
});
this.hot.batchExecution(() => {
if (this.indexesSequenceCache !== null) {
this.hot.rowIndexMapper.setIndexesSequence(this.indexesSequenceCache.getValues());
this.hot.rowIndexMapper.unregisterMap(this.pluginKey);
this.indexesSequenceCache = null;
}
}, true);
this.hot.columnIndexMapper.unregisterMap(`${this.pluginKey}.columnMeta`);
this.columnStatesManager.destroy();
this.columnMetaCache = null;
this.columnStatesManager = null;
this.unregisterShortcuts();
super.disablePlugin();
}
/**
* Register shortcuts responsible for toggling column sorting functionality.
*
* @private
*/
registerShortcuts() {
this.hot.getShortcutManager().getContext('grid').addShortcut({
keys: [['Enter']],
callback: () => {
const {
highlight
} = this.hot.getSelectedRangeActive();
this.sort(this.getColumnNextConfig(highlight.col));
// prevent default Enter behavior (move to the next row within a selection range)
return false;
},
runOnlyIf: () => {
var _this$hot$getSelected, _this$hot$getSelected2;
const highlight = (_this$hot$getSelected = this.hot.getSelectedRangeActive()) === null || _this$hot$getSelected === void 0 ? void 0 : _this$hot$getSelected.highlight;
return highlight && ((_this$hot$getSelected2 = this.hot.getSelectedRangeActive()) === null || _this$hot$getSelected2 === void 0 ? void 0 : _this$hot$getSelected2.isSingle()) && this.hot.selection.isCellVisible(highlight) && highlight.row === -1 && highlight.col >= 0;
},
relativeToGroup: _shortcutContexts.EDITOR_EDIT_GROUP,
position: 'before',
group: SHORTCUTS_GROUP
});
}
/**
* Unregister shortcuts responsible for toggling column sorting functionality.
*
* @private
*/
unregisterShortcuts() {
this.hot.getShortcutManager().getContext('grid').removeShortcutsByGroup(SHORTCUTS_GROUP);
}
// DIFF - MultiColumnSorting & ColumnSorting: changed function documentation.
/**
* Sorts the table by chosen columns and orders.
*
* @param {undefined|object} sortConfig Single column sort configuration. The configuration object contains `column` and `sortOrder` properties.
* First of them contains visual column index, the second one contains sort order (`asc` for ascending, `desc` for descending).
*
* **Note**: Please keep in mind that every call of `sort` function set an entirely new sort order. Previous sort configs aren't preserved.
*
* @example
* ```js
* // sort ascending first visual column
* hot.getPlugin('columnSorting').sort({ column: 0, sortOrder: 'asc' });
* ```
*
* @fires Hooks#beforeColumnSort
* @fires Hooks#afterColumnSort
*/
sort(sortConfig) {
const currentSortConfig = this.getSortConfig();
// We always pass configs defined as an array to `beforeColumnSort` and `afterColumnSort` hooks.
const destinationSortConfigs = this.getNormalizedSortConfigs(sortConfig);
const sortPossible = this.areValidSortConfigs(destinationSortConfigs);
const allowSort = this.hot.runHooks('beforeColumnSort', currentSortConfig, destinationSortConfigs, sortPossible);
if (allowSort === false) {
return;
}
if (currentSortConfig.length === 0 && this.indexesSequenceCache === null) {
this.indexesSequenceCache = this.hot.rowIndexMapper.registerMap(this.pluginKey, new _translations.IndexesSequence());
this.indexesSequenceCache.setValues(this.hot.rowIndexMapper.getIndexesSequence());
}
if (sortPossible) {
this.columnStatesManager.setSortStates(destinationSortConfigs);
this.sortByPresetSortStates(destinationSortConfigs);
this.saveAllSortSettings(destinationSortConfigs);
}
this.hot.runHooks('afterColumnSort', currentSortConfig, sortPossible ? destinationSortConfigs : currentSortConfig, sortPossible);
if (sortPossible) {
this.hot.render();
}
}
/**
* Clear the sort performed on the table.
*/
clearSort() {
this.sort([]);
}
/**
* Checks if the table is sorted (any column have to be sorted).
*
* @returns {boolean}
*/
isSorted() {
return this.enabled && !this.columnStatesManager.isListOfSortedColumnsEmpty();
}
/**
* Get sort configuration for particular column or for all sorted columns. Objects contain `column` and `sortOrder` properties.
*
* **Note**: Please keep in mind that returned objects expose **visual** column index under the `column` key. They are handled by the `sort` function.
*
* @param {number} [column] Visual column index.
* @returns {undefined|object|Array}
*/
getSortConfig(column) {
if ((0, _mixed.isDefined)(column)) {
return this.columnStatesManager.getColumnSortState(column);
}
return this.columnStatesManager.getSortStates();
}
/**
* @description
* Warn: Useful mainly for providing server side sort implementation (see in the example below). It doesn't sort the data set. It just sets sort configuration for all sorted columns.
* Note: Please keep in mind that this method doesn't re-render the table.
*
* @example
* ```js
* beforeColumnSort: function(currentSortConfig, destinationSortConfigs) {
* const columnSortPlugin = this.getPlugin('columnSorting');
*
* columnSortPlugin.setSortConfig(destinationSortConfigs);
*
* // const newData = ... // Calculated data set, ie. from an AJAX call.
*
* this.loadData(newData); // Load new data set and re-render the table.
*
* return false; // The blockade for the default sort action.
* }
* ```
*
* @param {undefined|object|Array} sortConfig Single column sort configuration or full sort configuration (for all sorted columns).
* The configuration object contains `column` and `sortOrder` properties. First of them contains visual column index, the second one contains
* sort order (`asc` for ascending, `desc` for descending).
*/
setSortConfig(sortConfig) {
// We always set configs defined as an array.
const destinationSortConfigs = this.getNormalizedSortConfigs(sortConfig);
if (this.areValidSortConfigs(destinationSortConfigs)) {
this.columnStatesManager.setSortStates(destinationSortConfigs);
}
}
/**
* Get normalized sort configs.
*
* @private
* @param {object|Array} [sortConfig=[]] Single column sort configuration or full sort configuration (for all sorted columns).
* The configuration object contains `column` and `sortOrder` properties. First of them contains visual column index, the second one contains
* sort order (`asc` for ascending, `desc` for descending).
* @returns {Array}
*/
getNormalizedSortConfigs() {
let sortConfig = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
if (Array.isArray(sortConfig)) {
return sortConfig.slice(0, 1);
}
return [sortConfig];
}
/**
* Get if sort configs are valid.
*
* @private
* @param {Array} sortConfigs Sort configuration for all sorted columns. Objects contain `column` and `sortOrder` properties.
* @returns {boolean}
*/
areValidSortConfigs(sortConfigs) {
const numberOfColumns = this.hot.countCols();
// We don't translate visual indexes to physical indexes.
return (0, _utils.areValidSortStates)(sortConfigs) && sortConfigs.every(_ref => {
let {
column
} = _ref;
return column <= numberOfColumns && column >= 0;
});
}
/**
* Saves all sorting settings. Saving works only when {@link Options#persistentState} option is enabled.
*
* @param {Array} sortConfigs Sort configuration for all sorted columns. Objects contain `column` and `sortOrder` properties.
*
* @private
* @fires Hooks#persistentStateSave
*/
saveAllSortSettings(sortConfigs) {
const allSortSettings = this.columnStatesManager.getAllColumnsProperties();
const translateColumnToPhysical = _ref2 => {
let {
column: visualColumn,
...restOfProperties
} = _ref2;
return {
column: this.hot.toPhysicalColumn(visualColumn),
...restOfProperties
};
};
allSortSettings.initialConfig = (0, _array.arrayMap)(sortConfigs, translateColumnToPhysical);
this.hot.runHooks('persistentStateSave', 'columnSorting', allSortSettings);
}
/**
* Get all saved sorting settings. Loading works only when {@link Options#persistentState} option is enabled.
*
* @private
* @returns {object} Previously saved sort settings.
*
* @fires Hooks#persistentStateLoad
*/
getAllSavedSortSettings() {
const storedAllSortSettings = {};
this.hot.runHooks('persistentStateLoad', 'columnSorting', storedAllSortSettings);
const allSortSettings = storedAllSortSettings.value;
const translateColumnToVisual = _ref3 => {
let {
column: physicalColumn,
...restOfProperties
} = _ref3;
return {
column: this.hot.toVisualColumn(physicalColumn),
...restOfProperties
};
};
if ((0, _mixed.isDefined)(allSortSettings) && Array.isArray(allSortSettings.initialConfig)) {
allSortSettings.initialConfig = (0, _array.arrayMap)(allSortSettings.initialConfig, translateColumnToVisual);
}
return allSortSettings;
}
/**
* Get next sort configuration for particular column. Object contain `column` and `sortOrder` properties.
*
* **Note**: Please keep in mind that returned object expose **visual** column index under the `column` key.
*
* @private
* @param {number} column Visual column index.
* @returns {undefined|object}
*/
getColumnNextConfig(column) {
const sortOrder = this.columnStatesManager.getSortOrderOfColumn(column);
if ((0, _mixed.isDefined)(sortOrder)) {
const nextSortOrder = (0, _utils.getNextSortOrder)(sortOrder);
if ((0, _mixed.isDefined)(nextSortOrder)) {
return {
column,
sortOrder: nextSortOrder
};
}
return;
}
const nrOfColumns = this.hot.countCols();
if (Number.isInteger(column) && column >= 0 && column < nrOfColumns) {
return {
column,
sortOrder: (0, _utils.getNextSortOrder)()
};
}
}
/**
* Get sort configuration with "next order" for particular column.
*
* @private
* @param {number} columnToChange Visual column index of column which order will be changed.
* @param {string} strategyId ID of strategy. Possible values: 'append' and 'replace'. The first one
* change order of particular column and change it's position in the sort queue to the last one. The second one
* just change order of particular column.
*
* **Note**: Please keep in mind that returned objects expose **visual** column index under the `column` key.
*
* @returns {Array}
*/
getNextSortConfig(columnToChange) {
let strategyId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : APPEND_COLUMN_CONFIG_STRATEGY;
const indexOfColumnToChange = this.columnStatesManager.getIndexOfColumnInSortQueue(columnToChange);
const isColumnSorted = indexOfColumnToChange !== -1;
const currentSortConfig = this.getSortConfig();
const nextColumnConfig = this.getColumnNextConfig(columnToChange);
if (isColumnSorted) {
if ((0, _mixed.isUndefined)(nextColumnConfig)) {
return [...currentSortConfig.slice(0, indexOfColumnToChange), ...currentSortConfig.slice(indexOfColumnToChange + 1)];
}
if (strategyId === APPEND_COLUMN_CONFIG_STRATEGY) {
return [...currentSortConfig.slice(0, indexOfColumnToChange), ...currentSortConfig.slice(indexOfColumnToChange + 1), nextColumnConfig];
} else if (strategyId === REPLACE_COLUMN_CONFIG_STRATEGY) {
return [...currentSortConfig.slice(0, indexOfColumnToChange), nextColumnConfig, ...currentSortConfig.slice(indexOfColumnToChange + 1)];
}
}
if ((0, _mixed.isDefined)(nextColumnConfig)) {
return currentSortConfig.concat(nextColumnConfig);
}
return currentSortConfig;
}
/**
* Get plugin's column config for the specified column index.
*
* @private
* @param {object} columnConfig Configuration inside `columns` property for the specified column index.
* @returns {object}
*/
getPluginColumnConfig(columnConfig) {
if ((0, _object.isObject)(columnConfig)) {
const pluginColumnConfig = columnConfig[this.pluginKey];
if ((0, _object.isObject)(pluginColumnConfig)) {
return pluginColumnConfig;
}
}
return {};
}
/**
* Get plugin settings related properties, properly merged from cascade settings.
*
* @private
* @param {number} column Visual column index.
* @returns {object}
*/
getMergedPluginSettings(column) {
const pluginMainSettings = this.hot.getSettings()[this.pluginKey];
const storedColumnProperties = this.columnStatesManager.getAllColumnsProperties();
const cellMeta = this.hot.getCellMeta(0, column);
const columnMeta = Object.getPrototypeOf(cellMeta);
if (Array.isArray(columnMeta.columns)) {
return Object.assign(storedColumnProperties, pluginMainSettings, this.getPluginColumnConfig(columnMeta.columns[column]));
} else if ((0, _function.isFunction)(columnMeta.columns)) {
return Object.assign(storedColumnProperties, pluginMainSettings, this.getPluginColumnConfig(columnMeta.columns(column)));
}
return Object.assign(storedColumnProperties, pluginMainSettings);
}
/**
* Get copy of settings for first cell in the column.
*
* @private
* @param {number} column Visual column index.
* @returns {object}
*/
// TODO: Workaround. Inheriting of non-primitive cell meta values doesn't work. Instead of getting properties from column meta we call this function.
// TODO: Remove test named: "should not break the dataset when inserted new row" (#5431).
getFirstCellSettings(column) {
const cellMeta = this.hot.getCellMeta(0, column);
const cellMetaCopy = Object.create(cellMeta);
cellMetaCopy[this.pluginKey] = this.columnMetaCache.getValueAtIndex(this.hot.toPhysicalColumn(column));
return cellMetaCopy;
}
/**
* Get number of rows which should be sorted.
*
* @private
* @param {number} numberOfRows Total number of displayed rows.
* @returns {number}
*/
getNumberOfRowsToSort(numberOfRows) {
const settings = this.hot.getSettings();
// `maxRows` option doesn't take into account `minSpareRows` option in this case.
if (settings.maxRows <= numberOfRows) {
return settings.maxRows;
}
return numberOfRows - settings.minSpareRows;
}
/**
* Performs the sorting using a stable sort function basing on internal state of sorting.
*
* @param {Array} sortConfigs Sort configuration for all sorted columns. Objects contain `column` and `sortOrder` properties.
* @private
*/
sortByPresetSortStates(sortConfigs) {
this.hot.rowIndexMapper.setIndexesSequence(this.indexesSequenceCache.getValues());
if (sortConfigs.length === 0) {
return;
}
const indexesWithData = [];
const numberOfRows = this.hot.countRows();
const getDataForSortedColumns = visualRowIndex => (0, _array.arrayMap)(sortConfigs, sortConfig => this.hot.getDataAtCell(visualRowIndex, sortConfig.column));
for (let visualRowIndex = 0; visualRowIndex < this.getNumberOfRowsToSort(numberOfRows); visualRowIndex += 1) {
indexesWithData.push([this.hot.toPhysicalRow(visualRowIndex)].concat(getDataForSortedColumns(visualRowIndex)));
}
const indexesBefore = (0, _array.arrayMap)(indexesWithData, indexWithData => indexWithData[0]);
(0, _sortService.sort)(indexesWithData, this.pluginKey, (0, _array.arrayMap)(sortConfigs, sortConfig => sortConfig.sortOrder), (0, _array.arrayMap)(sortConfigs, sortConfig => this.getFirstCellSettings(sortConfig.column)));
// Append spareRows
for (let visualRowIndex = indexesWithData.length; visualRowIndex < numberOfRows; visualRowIndex += 1) {
indexesWithData.push([visualRowIndex].concat(getDataForSortedColumns(visualRowIndex)));
}
const indexesAfter = (0, _array.arrayMap)(indexesWithData, indexWithData => indexWithData[0]);
const indexMapping = new Map((0, _array.arrayMap)(indexesBefore, (indexBefore, indexInsideArray) => [indexBefore, indexesAfter[indexInsideArray]]));
const newIndexesSequence = (0, _array.arrayMap)(this.hot.rowIndexMapper.getIndexesSequence(), physicalIndex => {
if (indexMapping.has(physicalIndex)) {
return indexMapping.get(physicalIndex);
}
return physicalIndex;
});
this.hot.rowIndexMapper.setIndexesSequence(newIndexesSequence);
}
/**
* Sort the table by provided configuration.
*
* @private
* @param {object} allSortSettings All sort config settings. Object may contain `initialConfig`, `indicator`,
* `sortEmptyCells`, `headerAction` and `compareFunctionFactory` properties.
*/
sortBySettings(allSortSettings) {
if ((0, _object.isObject)(allSortSettings)) {
this.columnStatesManager.updateAllColumnsProperties(allSortSettings);
const initialConfig = allSortSettings.initialConfig;
if (Array.isArray(initialConfig) || (0, _object.isObject)(initialConfig)) {
this.sort(initialConfig);
}
} else {
// Extra render for headers. Their width may change.
this.hot.render();
}
}
/**
* Callback for the `onAfterGetColHeader` hook. Adds column sorting CSS classes.
*
* @param {number} column Visual column index.
* @param {Element} TH TH HTML element.
*/
/**
* Update header classes.
*
* @private
* @param {HTMLElement} headerSpanElement Header span element.
* @param {...*} args Extra arguments for helpers.
*/
updateHeaderClasses(headerSpanElement) {
(0, _element.removeClass)(headerSpanElement, (0, _domHelpers.getClassesToRemove)(headerSpanElement));
if (this.enabled !== false) {
for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
args[_key3 - 1] = arguments[_key3];
}
(0, _element.addClass)(headerSpanElement, (0, _domHelpers.getClassesToAdd)(...args));
}
}
/**
* Overwriting base plugin's `onUpdateSettings` method. Please keep in mind that `onAfterUpdateSettings` isn't called
* for `updateSettings` in specific situations.
*
* @private
* @param {object} newSettings New settings object.
*/
onUpdateSettings(newSettings) {
super.onUpdateSettings(newSettings);
if (this.columnMetaCache !== null) {
// Column meta cache base on settings, thus we should re-init the map.
this.columnMetaCache.init(this.hot.columnIndexMapper.getNumberOfIndexes());
}
if ((0, _mixed.isDefined)(newSettings[this.pluginKey])) {
this.sortBySettings(newSettings[this.pluginKey]);
}
}
/**
* Callback for the `afterLoadData` hook.
*
* @param {boolean} initialLoad Flag that determines whether the data has been loaded during the initialization.
*/
/**
* Indicates if clickable header was clicked.
*
* @private
* @param {MouseEvent} event The `mousedown` event.
* @param {number} column Visual column index.
* @returns {boolean}
*/
wasClickableHeaderClicked(event, column) {
const pluginSettingsForColumn = this.getFirstCellSettings(column)[this.pluginKey];
const headerActionEnabled = pluginSettingsForColumn.headerAction;
return headerActionEnabled && (0, _element.hasClass)(event.target, _utils.HEADER_SPAN_CLASS);
}
/**
* Changes the behavior of selection / dragging.
*
* @param {MouseEvent} event The `mousedown` event.
* @param {CellCoords} coords Visual coordinates.
* @param {HTMLElement} TD The cell element.
* @param {object} controller An object with properties `row`, `column` and `cell`. Each property contains
* a boolean value that allows or disallows changing the selection for that particular area.
*/
/**
* Callback for the `onAfterOnCellMouseDown` hook.
*
* @private
* @param {Event} event Event which are provided by hook.
* @param {CellCoords} coords Visual coords of the selected cell.
*/
onAfterOnCellMouseDown(event, coords) {
if ((0, _utils.wasHeaderClickedProperly)(coords.row, coords.col, event) === false) {
return;
}
if (this.wasClickableHeaderClicked(event, coords.col)) {
if (this.hot.getShortcutManager().isCtrlPressed()) {
this.hot.deselectCell();
this.hot.selectColumns(coords.col);
}
const activeEditor = this.hot.getActiveEditor();
const nextConfig = this.getColumnNextConfig(coords.col);
if (activeEditor !== null && activeEditor !== void 0 && activeEditor.isOpened() && this.hot.getCellValidator(activeEditor.row, activeEditor.col)) {
// Postpone sorting until the cell's value is validated and saved.
this.hot.addHookOnce('postAfterValidate', () => {
this.sort(nextConfig);
});
} else {
this.sort(nextConfig);
}
}
}
/**
* Destroys the plugin instance.
*/
destroy() {
var _this$columnStatesMan;
// TODO: Probably not supported yet by ESLint: https://github.com/eslint/eslint/issues/11045
// eslint-disable-next-line no-unused-expressions
(_this$columnStatesMan = this.columnStatesManager) === null || _this$columnStatesMan === void 0 || _this$columnStatesMan.destroy();
super.destroy();
}
}
exports.ColumnSorting = ColumnSorting;
function _loadOrSortBySettings() {
const storedAllSortSettings = this.getAllSavedSortSettings();
if ((0, _object.isObject)(storedAllSortSettings)) {
this.sortBySettings(storedAllSortSettings);
} else {
const allSortSettings = this.hot.getSettings()[this.pluginKey];
this.sortBySettings(allSortSettings);
}
}
function _onAfterGetColHeader(column, TH) {
const headerSpanElement = (0, _utils.getHeaderSpanElement)(TH);
if ((0, _utils.isFirstLevelColumnHeader)(column, TH) === false || headerSpanElement === null) {
return;
}
const pluginSettingsForColumn = this.getFirstCellSettings(column)[this.pluginKey];
const showSortIndicator = pluginSettingsForColumn.indicator;
const headerActionEnabled = pluginSettingsForColumn.headerAction;
this.updateHeaderClasses(headerSpanElement, this.columnStatesManager, column, showSortIndicator, headerActionEnabled);
if (this.hot.getSettings().ariaTags) {
const currentSortState = this.columnStatesManager.getSortOrderOfColumn(column);
(0, _element.setAttribute)(TH, ...(0, _a11y.A11Y_SORT)(currentSortState ? `${currentSortState}ending` : 'none'));
}
}
function _onAfterLoadData(initialLoad) {
if (initialLoad === true) {
// TODO: Workaround? It should be refactored / described.
if (this.hot.view) {
_assertClassBrand(_ColumnSorting_brand, this, _loadOrSortBySettings).call(this);
}
}
}
function _onBeforeOnCellMouseDown(event, coords, TD, controller) {
if ((0, _utils.wasHeaderClickedProperly)(coords.row, coords.col, event) === false) {
return;
}
if (this.wasClickableHeaderClicked(event, coords.col) && this.hot.getShortcutManager().isCtrlPressed()) {
controller.column = true;
}
}