@quantlab/handsontable
Version:
Spreadsheet-like data grid editor that provides copy/paste functionality compatible with Excel/Google Docs
537 lines (437 loc) • 18.7 kB
JavaScript
'use strict';
exports.__esModule = true;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _element = require('./../../../helpers/dom/element');
var _event = require('./../../../helpers/dom/event');
var _object = require('./../../../helpers/object');
var _browser = require('./../../../helpers/browser');
var _eventManager = require('./../../../eventManager');
var _eventManager2 = _interopRequireDefault(_eventManager);
var _coords = require('./cell/coords');
var _coords2 = _interopRequireDefault(_coords);
var _base = require('./overlay/_base.js');
var _base2 = _interopRequireDefault(_base);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
*
*/
var Border = function () {
/**
* @param {Walkontable} wotInstance
* @param {Object} settings
*/
function Border(wotInstance, settings) {
_classCallCheck(this, Border);
if (!settings) {
return;
}
this.eventManager = new _eventManager2.default(wotInstance);
this.instance = wotInstance;
this.wot = wotInstance;
this.settings = settings;
this.mouseDown = false;
this.main = null;
this.top = null;
this.left = null;
this.bottom = null;
this.right = null;
this.topStyle = null;
this.leftStyle = null;
this.bottomStyle = null;
this.rightStyle = null;
this.cornerDefaultStyle = {
width: '5px',
height: '5px',
borderWidth: '2px',
borderStyle: 'solid',
borderColor: '#FFF'
};
this.corner = null;
this.cornerStyle = null;
this.createBorders(settings);
this.registerListeners();
}
/**
* Register all necessary events
*/
_createClass(Border, [{
key: 'registerListeners',
value: function registerListeners() {
var _this2 = this;
this.eventManager.addEventListener(document.body, 'mousedown', function () {
return _this2.onMouseDown();
});
this.eventManager.addEventListener(document.body, 'mouseup', function () {
return _this2.onMouseUp();
});
var _loop = function _loop(c, len) {
_this2.eventManager.addEventListener(_this2.main.childNodes[c], 'mouseenter', function (event) {
return _this2.onMouseEnter(event, _this2.main.childNodes[c]);
});
};
for (var c = 0, len = this.main.childNodes.length; c < len; c++) {
_loop(c, len);
}
}
/**
* Mouse down listener
*
* @private
*/
}, {
key: 'onMouseDown',
value: function onMouseDown() {
this.mouseDown = true;
}
/**
* Mouse up listener
*
* @private
*/
}, {
key: 'onMouseUp',
value: function onMouseUp() {
this.mouseDown = false;
}
/**
* Mouse enter listener for fragment selection functionality.
*
* @private
* @param {Event} event Dom event
* @param {HTMLElement} parentElement Part of border element.
*/
}, {
key: 'onMouseEnter',
value: function onMouseEnter(event, parentElement) {
if (!this.mouseDown || !this.wot.getSetting('hideBorderOnMouseDownOver')) {
return;
}
event.preventDefault();
(0, _event.stopImmediatePropagation)(event);
var _this = this;
var bounds = parentElement.getBoundingClientRect();
// Hide border to prevents selection jumping when fragmentSelection is enabled.
parentElement.style.display = 'none';
function isOutside(event) {
if (event.clientY < Math.floor(bounds.top)) {
return true;
}
if (event.clientY > Math.ceil(bounds.top + bounds.height)) {
return true;
}
if (event.clientX < Math.floor(bounds.left)) {
return true;
}
if (event.clientX > Math.ceil(bounds.left + bounds.width)) {
return true;
}
}
function handler(event) {
if (isOutside(event)) {
_this.eventManager.removeEventListener(document.body, 'mousemove', handler);
parentElement.style.display = 'block';
}
}
this.eventManager.addEventListener(document.body, 'mousemove', handler);
}
/**
* Create border elements
*
* @param {Object} settings
*/
}, {
key: 'createBorders',
value: function createBorders(settings) {
this.main = document.createElement('div');
var borderDivs = ['top', 'left', 'bottom', 'right', 'corner'];
var style = this.main.style;
style.position = 'absolute';
style.top = 0;
style.left = 0;
for (var i = 0; i < 5; i++) {
var position = borderDivs[i];
var div = document.createElement('div');
div.className = 'wtBorder ' + (this.settings.className || ''); // + borderDivs[i];
if (this.settings[position] && this.settings[position].hide) {
div.className += ' hidden';
}
style = div.style;
style.backgroundColor = this.settings[position] && this.settings[position].color ? this.settings[position].color : settings.border.color;
style.height = this.settings[position] && this.settings[position].width ? this.settings[position].width + 'px' : settings.border.width + 'px';
style.width = this.settings[position] && this.settings[position].width ? this.settings[position].width + 'px' : settings.border.width + 'px';
this.main.appendChild(div);
}
this.top = this.main.childNodes[0];
this.left = this.main.childNodes[1];
this.bottom = this.main.childNodes[2];
this.right = this.main.childNodes[3];
this.topStyle = this.top.style;
this.leftStyle = this.left.style;
this.bottomStyle = this.bottom.style;
this.rightStyle = this.right.style;
this.corner = this.main.childNodes[4];
this.corner.className += ' corner';
this.cornerStyle = this.corner.style;
this.cornerStyle.width = this.cornerDefaultStyle.width;
this.cornerStyle.height = this.cornerDefaultStyle.height;
this.cornerStyle.border = [this.cornerDefaultStyle.borderWidth, this.cornerDefaultStyle.borderStyle, this.cornerDefaultStyle.borderColor].join(' ');
if ((0, _browser.isMobileBrowser)()) {
this.createMultipleSelectorHandles();
}
this.disappear();
if (!this.wot.wtTable.bordersHolder) {
this.wot.wtTable.bordersHolder = document.createElement('div');
this.wot.wtTable.bordersHolder.className = 'htBorders';
this.wot.wtTable.spreader.appendChild(this.wot.wtTable.bordersHolder);
}
this.wot.wtTable.bordersHolder.insertBefore(this.main, this.wot.wtTable.bordersHolder.firstChild);
}
/**
* Create multiple selector handler for mobile devices
*/
}, {
key: 'createMultipleSelectorHandles',
value: function createMultipleSelectorHandles() {
this.selectionHandles = {
topLeft: document.createElement('DIV'),
topLeftHitArea: document.createElement('DIV'),
bottomRight: document.createElement('DIV'),
bottomRightHitArea: document.createElement('DIV')
};
var width = 10;
var hitAreaWidth = 40;
this.selectionHandles.topLeft.className = 'topLeftSelectionHandle';
this.selectionHandles.topLeftHitArea.className = 'topLeftSelectionHandle-HitArea';
this.selectionHandles.bottomRight.className = 'bottomRightSelectionHandle';
this.selectionHandles.bottomRightHitArea.className = 'bottomRightSelectionHandle-HitArea';
this.selectionHandles.styles = {
topLeft: this.selectionHandles.topLeft.style,
topLeftHitArea: this.selectionHandles.topLeftHitArea.style,
bottomRight: this.selectionHandles.bottomRight.style,
bottomRightHitArea: this.selectionHandles.bottomRightHitArea.style
};
var hitAreaStyle = {
position: 'absolute',
height: hitAreaWidth + 'px',
width: hitAreaWidth + 'px',
'border-radius': parseInt(hitAreaWidth / 1.5, 10) + 'px'
};
for (var prop in hitAreaStyle) {
if ((0, _object.hasOwnProperty)(hitAreaStyle, prop)) {
this.selectionHandles.styles.bottomRightHitArea[prop] = hitAreaStyle[prop];
this.selectionHandles.styles.topLeftHitArea[prop] = hitAreaStyle[prop];
}
}
var handleStyle = {
position: 'absolute',
height: width + 'px',
width: width + 'px',
'border-radius': parseInt(width / 1.5, 10) + 'px',
background: '#F5F5FF',
border: '1px solid #4285c8'
};
for (var _prop in handleStyle) {
if ((0, _object.hasOwnProperty)(handleStyle, _prop)) {
this.selectionHandles.styles.bottomRight[_prop] = handleStyle[_prop];
this.selectionHandles.styles.topLeft[_prop] = handleStyle[_prop];
}
}
this.main.appendChild(this.selectionHandles.topLeft);
this.main.appendChild(this.selectionHandles.bottomRight);
this.main.appendChild(this.selectionHandles.topLeftHitArea);
this.main.appendChild(this.selectionHandles.bottomRightHitArea);
}
}, {
key: 'isPartRange',
value: function isPartRange(row, col) {
if (this.wot.selections.area.cellRange) {
if (row != this.wot.selections.area.cellRange.to.row || col != this.wot.selections.area.cellRange.to.col) {
return true;
}
}
return false;
}
}, {
key: 'updateMultipleSelectionHandlesPosition',
value: function updateMultipleSelectionHandlesPosition(row, col, top, left, width, height) {
var handleWidth = parseInt(this.selectionHandles.styles.topLeft.width, 10);
var hitAreaWidth = parseInt(this.selectionHandles.styles.topLeftHitArea.width, 10);
this.selectionHandles.styles.topLeft.top = parseInt(top - handleWidth, 10) + 'px';
this.selectionHandles.styles.topLeft.left = parseInt(left - handleWidth, 10) + 'px';
this.selectionHandles.styles.topLeftHitArea.top = parseInt(top - hitAreaWidth / 4 * 3, 10) + 'px';
this.selectionHandles.styles.topLeftHitArea.left = parseInt(left - hitAreaWidth / 4 * 3, 10) + 'px';
this.selectionHandles.styles.bottomRight.top = parseInt(top + height, 10) + 'px';
this.selectionHandles.styles.bottomRight.left = parseInt(left + width, 10) + 'px';
this.selectionHandles.styles.bottomRightHitArea.top = parseInt(top + height - hitAreaWidth / 4, 10) + 'px';
this.selectionHandles.styles.bottomRightHitArea.left = parseInt(left + width - hitAreaWidth / 4, 10) + 'px';
if (this.settings.border.multipleSelectionHandlesVisible && this.settings.border.multipleSelectionHandlesVisible()) {
this.selectionHandles.styles.topLeft.display = 'block';
this.selectionHandles.styles.topLeftHitArea.display = 'block';
if (this.isPartRange(row, col)) {
this.selectionHandles.styles.bottomRight.display = 'none';
this.selectionHandles.styles.bottomRightHitArea.display = 'none';
} else {
this.selectionHandles.styles.bottomRight.display = 'block';
this.selectionHandles.styles.bottomRightHitArea.display = 'block';
}
} else {
this.selectionHandles.styles.topLeft.display = 'none';
this.selectionHandles.styles.bottomRight.display = 'none';
this.selectionHandles.styles.topLeftHitArea.display = 'none';
this.selectionHandles.styles.bottomRightHitArea.display = 'none';
}
if (row == this.wot.wtSettings.getSetting('fixedRowsTop') || col == this.wot.wtSettings.getSetting('fixedColumnsLeft')) {
this.selectionHandles.styles.topLeft.zIndex = '9999';
this.selectionHandles.styles.topLeftHitArea.zIndex = '9999';
} else {
this.selectionHandles.styles.topLeft.zIndex = '';
this.selectionHandles.styles.topLeftHitArea.zIndex = '';
}
}
/**
* Show border around one or many cells
*
* @param {Array} corners
*/
}, {
key: 'appear',
value: function appear(corners) {
if (this.disabled) {
return;
}
var isMultiple, fromTD, toTD, fromOffset, toOffset, containerOffset, top, minTop, left, minLeft, height, width, fromRow, fromColumn, toRow, toColumn, trimmingContainer, cornerOverlappingContainer, ilen;
ilen = this.wot.wtTable.getRenderedRowsCount();
for (var i = 0; i < ilen; i++) {
var s = this.wot.wtTable.rowFilter.renderedToSource(i);
if (s >= corners[0] && s <= corners[2]) {
fromRow = s;
break;
}
}
for (var _i = ilen - 1; _i >= 0; _i--) {
var _s = this.wot.wtTable.rowFilter.renderedToSource(_i);
if (_s >= corners[0] && _s <= corners[2]) {
toRow = _s;
break;
}
}
ilen = this.wot.wtTable.getRenderedColumnsCount();
for (var _i2 = 0; _i2 < ilen; _i2++) {
var _s2 = this.wot.wtTable.columnFilter.renderedToSource(_i2);
if (_s2 >= corners[1] && _s2 <= corners[3]) {
fromColumn = _s2;
break;
}
}
for (var _i3 = ilen - 1; _i3 >= 0; _i3--) {
var _s3 = this.wot.wtTable.columnFilter.renderedToSource(_i3);
if (_s3 >= corners[1] && _s3 <= corners[3]) {
toColumn = _s3;
break;
}
}
if (fromRow === void 0 || fromColumn === void 0) {
this.disappear();
return;
}
isMultiple = fromRow !== toRow || fromColumn !== toColumn;
fromTD = this.wot.wtTable.getCell(new _coords2.default(fromRow, fromColumn));
toTD = isMultiple ? this.wot.wtTable.getCell(new _coords2.default(toRow, toColumn)) : fromTD;
fromOffset = (0, _element.offset)(fromTD);
toOffset = isMultiple ? (0, _element.offset)(toTD) : fromOffset;
containerOffset = (0, _element.offset)(this.wot.wtTable.TABLE);
minTop = fromOffset.top;
height = toOffset.top + (0, _element.outerHeight)(toTD) - minTop;
minLeft = fromOffset.left;
width = toOffset.left + (0, _element.outerWidth)(toTD) - minLeft;
top = minTop - containerOffset.top - 1;
left = minLeft - containerOffset.left - 1;
var style = (0, _element.getComputedStyle)(fromTD);
if (parseInt(style.borderTopWidth, 10) > 0) {
top += 1;
height = height > 0 ? height - 1 : 0;
}
if (parseInt(style.borderLeftWidth, 10) > 0) {
left += 1;
width = width > 0 ? width - 1 : 0;
}
this.topStyle.top = top + 'px';
this.topStyle.left = left + 'px';
this.topStyle.width = width + 'px';
this.topStyle.display = 'block';
this.leftStyle.top = top + 'px';
this.leftStyle.left = left + 'px';
this.leftStyle.height = height + 'px';
this.leftStyle.display = 'block';
var delta = Math.floor(this.settings.border.width / 2);
this.bottomStyle.top = top + height - delta + 'px';
this.bottomStyle.left = left + 'px';
this.bottomStyle.width = width + 'px';
this.bottomStyle.display = 'block';
this.rightStyle.top = top + 'px';
this.rightStyle.left = left + width - delta + 'px';
this.rightStyle.height = height + 1 + 'px';
this.rightStyle.display = 'block';
if ((0, _browser.isMobileBrowser)() || !this.hasSetting(this.settings.border.cornerVisible) || this.isPartRange(toRow, toColumn)) {
this.cornerStyle.display = 'none';
} else {
this.cornerStyle.top = top + height - 4 + 'px';
this.cornerStyle.left = left + width - 4 + 'px';
this.cornerStyle.borderRightWidth = this.cornerDefaultStyle.borderWidth;
this.cornerStyle.width = this.cornerDefaultStyle.width;
// Hide the fill handle, so the possible further adjustments won't force unneeded scrollbars.
this.cornerStyle.display = 'none';
trimmingContainer = (0, _element.getTrimmingContainer)(this.wot.wtTable.TABLE);
if (toColumn === this.wot.getSetting('totalColumns') - 1) {
cornerOverlappingContainer = toTD.offsetLeft + (0, _element.outerWidth)(toTD) + parseInt(this.cornerDefaultStyle.width, 10) / 2 >= (0, _element.innerWidth)(trimmingContainer);
if (cornerOverlappingContainer) {
this.cornerStyle.left = Math.floor(left + width - 3 - parseInt(this.cornerDefaultStyle.width, 10) / 2) + 'px';
this.cornerStyle.borderRightWidth = 0;
}
}
if (toRow === this.wot.getSetting('totalRows') - 1) {
cornerOverlappingContainer = toTD.offsetTop + (0, _element.outerHeight)(toTD) + parseInt(this.cornerDefaultStyle.height, 10) / 2 >= (0, _element.innerHeight)(trimmingContainer);
if (cornerOverlappingContainer) {
this.cornerStyle.top = Math.floor(top + height - 3 - parseInt(this.cornerDefaultStyle.height, 10) / 2) + 'px';
this.cornerStyle.borderBottomWidth = 0;
}
}
this.cornerStyle.display = 'block';
}
if ((0, _browser.isMobileBrowser)()) {
this.updateMultipleSelectionHandlesPosition(fromRow, fromColumn, top, left, width, height);
}
}
/**
* Hide border
*/
}, {
key: 'disappear',
value: function disappear() {
this.topStyle.display = 'none';
this.leftStyle.display = 'none';
this.bottomStyle.display = 'none';
this.rightStyle.display = 'none';
this.cornerStyle.display = 'none';
if ((0, _browser.isMobileBrowser)()) {
this.selectionHandles.styles.topLeft.display = 'none';
this.selectionHandles.styles.bottomRight.display = 'none';
}
}
/**
* @param {Function} setting
* @returns {*}
*/
}, {
key: 'hasSetting',
value: function hasSetting(setting) {
if (typeof setting === 'function') {
return setting();
}
return !!setting;
}
}]);
return Border;
}();
exports.default = Border;