673 lines
23 KiB
JavaScript
673 lines
23 KiB
JavaScript
"use strict";
|
||||
|
|
|
|||
|
|
exports.__esModule = true;
|
|||
|
|
require("core-js/modules/es.array.push.js");
|
|||
|
|
var _base = require("../base");
|
|||
|
|
var _pluginHooks = _interopRequireDefault(require("../../pluginHooks"));
|
|||
|
|
var _array = require("../../helpers/array");
|
|||
|
|
var _element = require("../../helpers/dom/element");
|
|||
|
|
var _number = require("../../helpers/number");
|
|||
|
|
var _eventManager = _interopRequireDefault(require("../../eventManager"));
|
|||
|
|
var _backlight = _interopRequireDefault(require("./ui/backlight"));
|
|||
|
|
var _guideline = _interopRequireDefault(require("./ui/guideline"));
|
|||
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|||
|
|
_pluginHooks.default.getSingleton().register('beforeRowMove');
|
|||
|
|
_pluginHooks.default.getSingleton().register('afterRowMove');
|
|||
|
|
const PLUGIN_KEY = 'manualRowMove';
|
|||
|
|
exports.PLUGIN_KEY = PLUGIN_KEY;
|
|||
|
|
const PLUGIN_PRIORITY = 140;
|
|||
|
|
exports.PLUGIN_PRIORITY = PLUGIN_PRIORITY;
|
|||
|
|
const privatePool = new WeakMap();
|
|||
|
|
const CSS_PLUGIN = 'ht__manualRowMove';
|
|||
|
|
const CSS_SHOW_UI = 'show-ui';
|
|||
|
|
const CSS_ON_MOVING = 'on-moving--rows';
|
|||
|
|
const CSS_AFTER_SELECTION = 'after-selection--rows';
|
|||
|
|
|
|||
|
|
/* eslint-disable jsdoc/require-description-complete-sentence */
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @plugin ManualRowMove
|
|||
|
|
* @class ManualRowMove
|
|||
|
|
*
|
|||
|
|
* @description
|
|||
|
|
* This plugin allows to change rows order. To make rows order persistent the {@link Options#persistentState}
|
|||
|
|
* plugin should be enabled.
|
|||
|
|
*
|
|||
|
|
* API:
|
|||
|
|
* - `moveRow` - move single row to the new position.
|
|||
|
|
* - `moveRows` - move many rows (as an array of indexes) to the new position.
|
|||
|
|
* - `dragRow` - drag single row to the new position.
|
|||
|
|
* - `dragRows` - drag many rows (as an array of indexes) to the new position.
|
|||
|
|
*
|
|||
|
|
* [Documentation](@/guides/rows/row-moving.md) explain differences between drag and move actions. Please keep in mind that if you want apply visual changes,
|
|||
|
|
* you have to call manually the `render` method on the instance of Handsontable.
|
|||
|
|
*
|
|||
|
|
* The plugin creates additional components to make moving possibly using user interface:
|
|||
|
|
* - backlight - highlight of selected rows.
|
|||
|
|
* - guideline - line which shows where rows has been moved.
|
|||
|
|
*
|
|||
|
|
* @class ManualRowMove
|
|||
|
|
* @plugin ManualRowMove
|
|||
|
|
*/
|
|||
|
|
class ManualRowMove extends _base.BasePlugin {
|
|||
|
|
static get PLUGIN_KEY() {
|
|||
|
|
return PLUGIN_KEY;
|
|||
|
|
}
|
|||
|
|
static get PLUGIN_PRIORITY() {
|
|||
|
|
return PLUGIN_PRIORITY;
|
|||
|
|
}
|
|||
|
|
constructor(hotInstance) {
|
|||
|
|
super(hotInstance);
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Set up WeakMap of plugin to sharing private parameters;.
|
|||
|
|
*/
|
|||
|
|
privatePool.set(this, {
|
|||
|
|
rowsToMove: [],
|
|||
|
|
pressed: void 0,
|
|||
|
|
target: {
|
|||
|
|
eventPageY: void 0,
|
|||
|
|
coords: void 0,
|
|||
|
|
TD: void 0,
|
|||
|
|
row: void 0
|
|||
|
|
},
|
|||
|
|
cachedDropIndex: void 0
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Event Manager object.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @type {object}
|
|||
|
|
*/
|
|||
|
|
this.eventManager = new _eventManager.default(this);
|
|||
|
|
/**
|
|||
|
|
* Backlight UI object.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @type {object}
|
|||
|
|
*/
|
|||
|
|
this.backlight = new _backlight.default(hotInstance);
|
|||
|
|
/**
|
|||
|
|
* Guideline UI object.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @type {object}
|
|||
|
|
*/
|
|||
|
|
this.guideline = new _guideline.default(hotInstance);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 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 ManualRowMove#enablePlugin} method is called.
|
|||
|
|
*
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
isEnabled() {
|
|||
|
|
return !!this.hot.getSettings()[PLUGIN_KEY];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Enables the plugin functionality for this Handsontable instance.
|
|||
|
|
*/
|
|||
|
|
enablePlugin() {
|
|||
|
|
var _this = this;
|
|||
|
|
if (this.enabled) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
this.addHook('beforeOnCellMouseDown', function () {
|
|||
|
|
return _this.onBeforeOnCellMouseDown(...arguments);
|
|||
|
|
});
|
|||
|
|
this.addHook('beforeOnCellMouseOver', function () {
|
|||
|
|
return _this.onBeforeOnCellMouseOver(...arguments);
|
|||
|
|
});
|
|||
|
|
this.addHook('afterScrollHorizontally', () => this.onAfterScrollHorizontally());
|
|||
|
|
this.addHook('afterLoadData', function () {
|
|||
|
|
return _this.onAfterLoadData(...arguments);
|
|||
|
|
});
|
|||
|
|
this.buildPluginUI();
|
|||
|
|
this.registerEvents();
|
|||
|
|
|
|||
|
|
// TODO: move adding plugin classname to BasePlugin.
|
|||
|
|
(0, _element.addClass)(this.hot.rootElement, CSS_PLUGIN);
|
|||
|
|
super.enablePlugin();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Updates the plugin's state.
|
|||
|
|
*
|
|||
|
|
* This method is executed when [`updateSettings()`](@/api/core.md#updatesettings) is invoked with any of the following configuration options:
|
|||
|
|
* - [`manualRowMove`](@/api/options.md#manualrowmove)
|
|||
|
|
*/
|
|||
|
|
updatePlugin() {
|
|||
|
|
this.disablePlugin();
|
|||
|
|
this.enablePlugin();
|
|||
|
|
this.moveBySettingsOrLoad();
|
|||
|
|
super.updatePlugin();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Disables the plugin functionality for this Handsontable instance.
|
|||
|
|
*/
|
|||
|
|
disablePlugin() {
|
|||
|
|
(0, _element.removeClass)(this.hot.rootElement, CSS_PLUGIN);
|
|||
|
|
this.unregisterEvents();
|
|||
|
|
this.backlight.destroy();
|
|||
|
|
this.guideline.destroy();
|
|||
|
|
super.disablePlugin();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Moves a single row.
|
|||
|
|
*
|
|||
|
|
* To see the outcome, rerender your grid by calling [`render()`](@/api/core.md#render).
|
|||
|
|
*
|
|||
|
|
* @param {number} row Visual row index to be moved.
|
|||
|
|
* @param {number} finalIndex Visual row index, being a start index for the moved rows. Points to where the elements will be placed after the moving action.
|
|||
|
|
* To check the visualization of the final index, please take a look at [documentation](@/guides/rows/row-moving.md#drag-and-move-actions-of-manualrowmove-plugin).
|
|||
|
|
* @fires Hooks#beforeRowMove
|
|||
|
|
* @fires Hooks#afterRowMove
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
moveRow(row, finalIndex) {
|
|||
|
|
return this.moveRows([row], finalIndex);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Moves multiple rows.
|
|||
|
|
*
|
|||
|
|
* To see the outcome, rerender your grid by calling [`render()`](@/api/core.md#render).
|
|||
|
|
*
|
|||
|
|
* @param {Array} rows Array of visual row indexes to be moved.
|
|||
|
|
* @param {number} finalIndex Visual row index, being a start index for the moved rows. Points to where the elements will be placed after the moving action.
|
|||
|
|
* To check the visualization of the final index, please take a look at [documentation](@/guides/rows/row-moving.md#drag-and-move-actions-of-manualrowmove-plugin).
|
|||
|
|
* @fires Hooks#beforeRowMove
|
|||
|
|
* @fires Hooks#afterRowMove
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
moveRows(rows, finalIndex) {
|
|||
|
|
const priv = privatePool.get(this);
|
|||
|
|
const dropIndex = priv.cachedDropIndex;
|
|||
|
|
const movePossible = this.isMovePossible(rows, finalIndex);
|
|||
|
|
const beforeMoveHook = this.hot.runHooks('beforeRowMove', rows, finalIndex, dropIndex, movePossible);
|
|||
|
|
priv.cachedDropIndex = void 0;
|
|||
|
|
if (beforeMoveHook === false) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (movePossible) {
|
|||
|
|
this.hot.rowIndexMapper.moveIndexes(rows, finalIndex);
|
|||
|
|
}
|
|||
|
|
const movePerformed = movePossible && this.isRowOrderChanged(rows, finalIndex);
|
|||
|
|
this.hot.runHooks('afterRowMove', rows, finalIndex, dropIndex, movePossible, movePerformed);
|
|||
|
|
return movePerformed;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Drag a single row to drop index position.
|
|||
|
|
*
|
|||
|
|
* @param {number} row Visual row index to be dragged.
|
|||
|
|
* @param {number} dropIndex Visual row index, being a drop index for the moved rows. Points to where we are going to drop the moved elements.
|
|||
|
|
* To check visualization of drop index please take a look at [documentation](@/guides/rows/row-moving.md#drag-and-move-actions-of-manualrowmove-plugin).
|
|||
|
|
* @fires Hooks#beforeRowMove
|
|||
|
|
* @fires Hooks#afterRowMove
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
dragRow(row, dropIndex) {
|
|||
|
|
return this.dragRows([row], dropIndex);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Drag multiple rows to drop index position.
|
|||
|
|
*
|
|||
|
|
* @param {Array} rows Array of visual row indexes to be dragged.
|
|||
|
|
* @param {number} dropIndex Visual row index, being a drop index for the moved rows. Points to where we are going to drop the moved elements.
|
|||
|
|
* To check visualization of drop index please take a look at [documentation](@/guides/rows/row-moving.md#drag-and-move-actions-of-manualrowmove-plugin).
|
|||
|
|
* @fires Hooks#beforeRowMove
|
|||
|
|
* @fires Hooks#afterRowMove
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
dragRows(rows, dropIndex) {
|
|||
|
|
const finalIndex = this.countFinalIndex(rows, dropIndex);
|
|||
|
|
const priv = privatePool.get(this);
|
|||
|
|
priv.cachedDropIndex = dropIndex;
|
|||
|
|
return this.moveRows(rows, finalIndex);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Indicates if it's possible to move rows to the desired position. Some of the actions aren't possible, i.e. You can’t move more than one element to the last position.
|
|||
|
|
*
|
|||
|
|
* @param {Array} movedRows Array of visual row indexes to be moved.
|
|||
|
|
* @param {number} finalIndex Visual row index, being a start index for the moved rows. Points to where the elements will be placed after the moving action.
|
|||
|
|
* To check the visualization of the final index, please take a look at [documentation](@/guides/rows/row-moving.md#drag-and-move-actions-of-manualrowmove-plugin).
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
isMovePossible(movedRows, finalIndex) {
|
|||
|
|
const length = this.hot.rowIndexMapper.getNotTrimmedIndexesLength();
|
|||
|
|
|
|||
|
|
// An attempt to transfer more rows to start destination than is possible (only when moving from the top to the bottom).
|
|||
|
|
const tooHighDestinationIndex = movedRows.length + finalIndex > length;
|
|||
|
|
const tooLowDestinationIndex = finalIndex < 0;
|
|||
|
|
const tooLowMovedRowIndex = movedRows.some(movedRow => movedRow < 0);
|
|||
|
|
const tooHighMovedRowIndex = movedRows.some(movedRow => movedRow >= length);
|
|||
|
|
if (tooHighDestinationIndex || tooLowDestinationIndex || tooLowMovedRowIndex || tooHighMovedRowIndex) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Indicates if order of rows was changed.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @param {Array} movedRows Array of visual row indexes to be moved.
|
|||
|
|
* @param {number} finalIndex Visual row index, being a start index for the moved rows. Points to where the elements will be placed after the moving action.
|
|||
|
|
* To check the visualization of the final index, please take a look at [documentation](@/guides/rows/row-moving.md#drag-and-move-actions-of-manualrowmove-plugin).
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
isRowOrderChanged(movedRows, finalIndex) {
|
|||
|
|
return movedRows.some((row, nrOfMovedElement) => row - nrOfMovedElement !== finalIndex);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Count the final row index from the drop index.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @param {Array} movedRows Array of visual row indexes to be moved.
|
|||
|
|
* @param {number} dropIndex Visual row index, being a drop index for the moved rows.
|
|||
|
|
* @returns {number} Visual row index, being a start index for the moved rows.
|
|||
|
|
*/
|
|||
|
|
countFinalIndex(movedRows, dropIndex) {
|
|||
|
|
const numberOfRowsLowerThanDropIndex = (0, _array.arrayReduce)(movedRows, (numberOfRows, currentRowIndex) => {
|
|||
|
|
if (currentRowIndex < dropIndex) {
|
|||
|
|
numberOfRows += 1;
|
|||
|
|
}
|
|||
|
|
return numberOfRows;
|
|||
|
|
}, 0);
|
|||
|
|
return dropIndex - numberOfRowsLowerThanDropIndex;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Gets the sum of the heights of rows in the provided range.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @param {number} fromRow Visual row index.
|
|||
|
|
* @param {number} toRow Visual row index.
|
|||
|
|
* @returns {number}
|
|||
|
|
*/
|
|||
|
|
getRowsHeight(fromRow, toRow) {
|
|||
|
|
const rowMapper = this.hot.rowIndexMapper;
|
|||
|
|
let rowsHeight = 0;
|
|||
|
|
for (let visualRowIndex = fromRow; visualRowIndex <= toRow; visualRowIndex++) {
|
|||
|
|
const renderableIndex = rowMapper.getRenderableFromVisualIndex(visualRowIndex);
|
|||
|
|
if (renderableIndex !== null) {
|
|||
|
|
rowsHeight += this.hot.view._wt.wtTable.getRowHeight(renderableIndex) || 23;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return rowsHeight;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Loads initial settings when persistent state is saved or when plugin was initialized as an array.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
moveBySettingsOrLoad() {
|
|||
|
|
const pluginSettings = this.hot.getSettings()[PLUGIN_KEY];
|
|||
|
|
if (Array.isArray(pluginSettings)) {
|
|||
|
|
this.moveRows(pluginSettings, 0);
|
|||
|
|
} else if (pluginSettings !== void 0) {
|
|||
|
|
const persistentState = this.persistentStateLoad();
|
|||
|
|
if (persistentState.length) {
|
|||
|
|
this.moveRows(persistentState, 0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Checks if the provided row is in the fixedRowsTop section.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @param {number} row Visual row index to check.
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
isFixedRowTop(row) {
|
|||
|
|
return row < this.hot.getSettings().fixedRowsTop;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Checks if the provided row is in the fixedRowsBottom section.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @param {number} row Visual row index to check.
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
isFixedRowBottom(row) {
|
|||
|
|
return row > this.hot.countRows() - 1 - this.hot.getSettings().fixedRowsBottom;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Saves the manual row positions to the persistent state (the {@link Options#persistentState} option has to be enabled).
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @fires Hooks#persistentStateSave
|
|||
|
|
*/
|
|||
|
|
persistentStateSave() {
|
|||
|
|
// The `PersistentState` plugin should be refactored.
|
|||
|
|
this.hot.runHooks('persistentStateSave', 'manualRowMove', this.hot.rowIndexMapper.getIndexesSequence());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Loads the manual row positions from the persistent state (the {@link Options#persistentState} option has to be enabled).
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @fires Hooks#persistentStateLoad
|
|||
|
|
* @returns {Array} Stored state.
|
|||
|
|
*/
|
|||
|
|
persistentStateLoad() {
|
|||
|
|
const storedState = {};
|
|||
|
|
this.hot.runHooks('persistentStateLoad', 'manualRowMove', storedState);
|
|||
|
|
return storedState.value ? storedState.value : [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Prepares an array of indexes based on actual selection.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @returns {Array}
|
|||
|
|
*/
|
|||
|
|
prepareRowsToMoving() {
|
|||
|
|
const selection = this.hot.getSelectedRangeLast();
|
|||
|
|
const selectedRows = [];
|
|||
|
|
if (!selection) {
|
|||
|
|
return selectedRows;
|
|||
|
|
}
|
|||
|
|
const {
|
|||
|
|
from,
|
|||
|
|
to
|
|||
|
|
} = selection;
|
|||
|
|
const start = Math.min(from.row, to.row);
|
|||
|
|
const end = Math.max(from.row, to.row);
|
|||
|
|
(0, _number.rangeEach)(start, end, i => {
|
|||
|
|
selectedRows.push(i);
|
|||
|
|
});
|
|||
|
|
return selectedRows;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Update the UI visual position.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
refreshPositions() {
|
|||
|
|
const priv = privatePool.get(this);
|
|||
|
|
const coords = priv.target.coords;
|
|||
|
|
const firstVisible = this.hot.view.getFirstFullyVisibleRow();
|
|||
|
|
const lastVisible = this.hot.view.getLastFullyVisibleRow();
|
|||
|
|
const countRows = this.hot.countRows();
|
|||
|
|
if (this.isFixedRowTop(coords.row) && firstVisible > 0) {
|
|||
|
|
this.hot.scrollViewportTo(this.hot.rowIndexMapper.getNearestNotHiddenIndex(firstVisible - 1, -1));
|
|||
|
|
}
|
|||
|
|
if (this.isFixedRowBottom(coords.row) && lastVisible < countRows) {
|
|||
|
|
this.hot.scrollViewportTo(this.hot.rowIndexMapper.getNearestNotHiddenIndex(lastVisible + 1, 1), undefined, true);
|
|||
|
|
}
|
|||
|
|
const wtTable = this.hot.view._wt.wtTable;
|
|||
|
|
const TD = priv.target.TD;
|
|||
|
|
const rootElement = this.hot.rootElement;
|
|||
|
|
const rootElementOffset = (0, _element.offset)(rootElement);
|
|||
|
|
const trimmingContainer = (0, _element.getTrimmingContainer)(rootElement);
|
|||
|
|
const tableScroll = wtTable.holder.scrollTop;
|
|||
|
|
let trimmingContainerScroll;
|
|||
|
|
|
|||
|
|
// Trimming container is the `window` element.
|
|||
|
|
if (this.hot.rootWindow === trimmingContainer) {
|
|||
|
|
trimmingContainerScroll = trimmingContainer.scrollY;
|
|||
|
|
} else {
|
|||
|
|
trimmingContainerScroll = trimmingContainer.scrollTop;
|
|||
|
|
}
|
|||
|
|
const pixelsAbove = rootElementOffset.top - trimmingContainerScroll;
|
|||
|
|
const pixelsRelToTableStart = priv.target.eventPageY - pixelsAbove + tableScroll;
|
|||
|
|
const hiderHeight = wtTable.hider.offsetHeight;
|
|||
|
|
const tbodyOffsetTop = wtTable.TBODY.offsetTop;
|
|||
|
|
const backlightElemMarginTop = this.backlight.getOffset().top;
|
|||
|
|
const backlightElemHeight = this.backlight.getSize().height;
|
|||
|
|
const tdMiddle = TD.offsetHeight / 2;
|
|||
|
|
const tdHeight = TD.offsetHeight;
|
|||
|
|
let tdStartPixel = this.hot.view.THEAD.offsetHeight + this.getRowsHeight(0, coords.row - 1);
|
|||
|
|
const isBelowTable = pixelsRelToTableStart >= tdStartPixel + tdMiddle;
|
|||
|
|
if (this.isFixedRowTop(coords.row)) {
|
|||
|
|
tdStartPixel += wtTable.holder.scrollTop;
|
|||
|
|
}
|
|||
|
|
if (coords.row < 0) {
|
|||
|
|
// if hover on colHeader
|
|||
|
|
priv.target.row = firstVisible > 0 ? firstVisible - 1 : firstVisible;
|
|||
|
|
} else if (isBelowTable) {
|
|||
|
|
// if hover on lower part of TD
|
|||
|
|
priv.target.row = coords.row + 1;
|
|||
|
|
// unfortunately first row is bigger than rest
|
|||
|
|
tdStartPixel += coords.row === 0 ? tdHeight - 1 : tdHeight;
|
|||
|
|
} else {
|
|||
|
|
// elsewhere on table
|
|||
|
|
priv.target.row = coords.row;
|
|||
|
|
}
|
|||
|
|
let backlightTop = pixelsRelToTableStart;
|
|||
|
|
let guidelineTop = tdStartPixel;
|
|||
|
|
if (pixelsRelToTableStart + backlightElemHeight + backlightElemMarginTop >= hiderHeight) {
|
|||
|
|
// prevent display backlight below table
|
|||
|
|
backlightTop = hiderHeight - backlightElemHeight - backlightElemMarginTop;
|
|||
|
|
} else if (pixelsRelToTableStart + backlightElemMarginTop < tbodyOffsetTop) {
|
|||
|
|
// prevent display above below table
|
|||
|
|
backlightTop = tbodyOffsetTop + Math.abs(backlightElemMarginTop);
|
|||
|
|
}
|
|||
|
|
if (tdStartPixel >= hiderHeight - 1) {
|
|||
|
|
// prevent display guideline below table
|
|||
|
|
guidelineTop = hiderHeight - 1;
|
|||
|
|
}
|
|||
|
|
this.backlight.setPosition(backlightTop);
|
|||
|
|
this.guideline.setPosition(guidelineTop);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Binds the events used by the plugin.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
registerEvents() {
|
|||
|
|
const {
|
|||
|
|
documentElement
|
|||
|
|
} = this.hot.rootDocument;
|
|||
|
|
this.eventManager.addEventListener(documentElement, 'mousemove', event => this.onMouseMove(event));
|
|||
|
|
this.eventManager.addEventListener(documentElement, 'mouseup', () => this.onMouseUp());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Unbinds the events used by the plugin.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
unregisterEvents() {
|
|||
|
|
this.eventManager.clear();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Change the behavior of selection / dragging.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @param {MouseEvent} event `mousedown` event properties.
|
|||
|
|
* @param {CellCoords} coords Visual cell coordinates where was fired event.
|
|||
|
|
* @param {HTMLElement} TD Cell represented as HTMLElement.
|
|||
|
|
* @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.
|
|||
|
|
*/
|
|||
|
|
onBeforeOnCellMouseDown(event, coords, TD, controller) {
|
|||
|
|
const {
|
|||
|
|
wtTable,
|
|||
|
|
wtViewport
|
|||
|
|
} = this.hot.view._wt;
|
|||
|
|
const isHeaderSelection = this.hot.selection.isSelectedByRowHeader();
|
|||
|
|
const selection = this.hot.getSelectedRangeLast();
|
|||
|
|
const priv = privatePool.get(this);
|
|||
|
|
if (!selection || !isHeaderSelection || priv.pressed || event.button !== 0) {
|
|||
|
|
priv.pressed = false;
|
|||
|
|
priv.rowsToMove.length = 0;
|
|||
|
|
(0, _element.removeClass)(this.hot.rootElement, [CSS_ON_MOVING, CSS_SHOW_UI]);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const guidelineIsNotReady = this.guideline.isBuilt() && !this.guideline.isAppended();
|
|||
|
|
const backlightIsNotReady = this.backlight.isBuilt() && !this.backlight.isAppended();
|
|||
|
|
if (guidelineIsNotReady && backlightIsNotReady) {
|
|||
|
|
this.guideline.appendTo(wtTable.hider);
|
|||
|
|
this.backlight.appendTo(wtTable.hider);
|
|||
|
|
}
|
|||
|
|
const {
|
|||
|
|
from,
|
|||
|
|
to
|
|||
|
|
} = selection;
|
|||
|
|
const start = Math.min(from.row, to.row);
|
|||
|
|
const end = Math.max(from.row, to.row);
|
|||
|
|
if (coords.col < 0 && coords.row >= start && coords.row <= end) {
|
|||
|
|
controller.row = true;
|
|||
|
|
priv.pressed = true;
|
|||
|
|
priv.target.eventPageY = event.pageY;
|
|||
|
|
priv.target.coords = coords;
|
|||
|
|
priv.target.TD = TD;
|
|||
|
|
priv.rowsToMove = this.prepareRowsToMoving();
|
|||
|
|
const leftPos = wtTable.holder.scrollLeft + wtViewport.getRowHeaderWidth();
|
|||
|
|
this.backlight.setPosition(null, leftPos);
|
|||
|
|
this.backlight.setSize(wtTable.hider.offsetWidth - leftPos, this.getRowsHeight(start, end));
|
|||
|
|
this.backlight.setOffset((this.getRowsHeight(start, coords.row - 1) + event.offsetY) * -1, null);
|
|||
|
|
(0, _element.addClass)(this.hot.rootElement, CSS_ON_MOVING);
|
|||
|
|
this.refreshPositions();
|
|||
|
|
} else {
|
|||
|
|
(0, _element.removeClass)(this.hot.rootElement, CSS_AFTER_SELECTION);
|
|||
|
|
priv.pressed = false;
|
|||
|
|
priv.rowsToMove.length = 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 'mouseMove' event callback. Fired when pointer move on document.documentElement.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @param {MouseEvent} event `mousemove` event properties.
|
|||
|
|
*/
|
|||
|
|
onMouseMove(event) {
|
|||
|
|
const priv = privatePool.get(this);
|
|||
|
|
if (!priv.pressed) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// callback for browser which doesn't supports CSS pointer-event: none
|
|||
|
|
if (event.target === this.backlight.element) {
|
|||
|
|
const height = this.backlight.getSize().height;
|
|||
|
|
this.backlight.setSize(null, 0);
|
|||
|
|
setTimeout(function () {
|
|||
|
|
this.backlight.setPosition(null, height);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
priv.target.eventPageY = event.pageY;
|
|||
|
|
this.refreshPositions();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 'beforeOnCellMouseOver' hook callback. Fired when pointer was over cell.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
* @param {MouseEvent} event `mouseover` event properties.
|
|||
|
|
* @param {CellCoords} coords Visual cell coordinates where was fired event.
|
|||
|
|
* @param {HTMLElement} TD Cell represented as HTMLElement.
|
|||
|
|
* @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.
|
|||
|
|
*/
|
|||
|
|
onBeforeOnCellMouseOver(event, coords, TD, controller) {
|
|||
|
|
const selectedRange = this.hot.getSelectedRangeLast();
|
|||
|
|
const priv = privatePool.get(this);
|
|||
|
|
if (!selectedRange || !priv.pressed) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (priv.rowsToMove.indexOf(coords.row) > -1) {
|
|||
|
|
(0, _element.removeClass)(this.hot.rootElement, CSS_SHOW_UI);
|
|||
|
|
} else {
|
|||
|
|
(0, _element.addClass)(this.hot.rootElement, CSS_SHOW_UI);
|
|||
|
|
}
|
|||
|
|
controller.row = true;
|
|||
|
|
controller.column = true;
|
|||
|
|
controller.cell = true;
|
|||
|
|
priv.target.coords = coords;
|
|||
|
|
priv.target.TD = TD;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* `onMouseUp` hook callback.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
onMouseUp() {
|
|||
|
|
const priv = privatePool.get(this);
|
|||
|
|
const target = priv.target.row;
|
|||
|
|
const rowsLen = priv.rowsToMove.length;
|
|||
|
|
priv.pressed = false;
|
|||
|
|
priv.backlightHeight = 0;
|
|||
|
|
(0, _element.removeClass)(this.hot.rootElement, [CSS_ON_MOVING, CSS_SHOW_UI, CSS_AFTER_SELECTION]);
|
|||
|
|
if (this.hot.selection.isSelectedByRowHeader()) {
|
|||
|
|
(0, _element.addClass)(this.hot.rootElement, CSS_AFTER_SELECTION);
|
|||
|
|
}
|
|||
|
|
if (rowsLen < 1 || target === void 0) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const firstMovedVisualRow = priv.rowsToMove[0];
|
|||
|
|
const firstMovedPhysicalRow = this.hot.toPhysicalRow(firstMovedVisualRow);
|
|||
|
|
const movePerformed = this.dragRows(priv.rowsToMove, target);
|
|||
|
|
priv.rowsToMove.length = 0;
|
|||
|
|
if (movePerformed === true) {
|
|||
|
|
this.persistentStateSave();
|
|||
|
|
this.hot.render();
|
|||
|
|
this.hot.view.adjustElementsSize(true);
|
|||
|
|
const selectionStart = this.hot.toVisualRow(firstMovedPhysicalRow);
|
|||
|
|
const selectionEnd = selectionStart + rowsLen - 1;
|
|||
|
|
this.hot.selectRows(selectionStart, selectionEnd);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* `afterScrollHorizontally` hook callback. Fired the table was scrolled horizontally.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
onAfterScrollHorizontally() {
|
|||
|
|
const wtTable = this.hot.view._wt.wtTable;
|
|||
|
|
const headerWidth = this.hot.view._wt.wtViewport.getRowHeaderWidth();
|
|||
|
|
const scrollLeft = wtTable.holder.scrollLeft;
|
|||
|
|
const posLeft = headerWidth + scrollLeft;
|
|||
|
|
this.backlight.setPosition(null, posLeft);
|
|||
|
|
this.backlight.setSize(wtTable.hider.offsetWidth - posLeft);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Builds the plugin's UI.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
buildPluginUI() {
|
|||
|
|
this.backlight.build();
|
|||
|
|
this.guideline.build();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Callback for the `afterLoadData` hook.
|
|||
|
|
*
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
onAfterLoadData() {
|
|||
|
|
this.moveBySettingsOrLoad();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Destroys the plugin instance.
|
|||
|
|
*/
|
|||
|
|
destroy() {
|
|||
|
|
this.backlight.destroy();
|
|||
|
|
this.guideline.destroy();
|
|||
|
|
super.destroy();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
exports.ManualRowMove = ManualRowMove;
|