UNPKG

fin-hypergrid

Version:
417 lines (363 loc) 12.9 kB
'use strict'; var Feature = require('./Feature'); /** * @constructor * @extends Feature */ var CellSelection = Feature.extend('CellSelection', { /** * The pixel location of the mouse pointer during a drag operation. * @type {window.fin.rectangular.Point} * @memberOf CellSelection.prototype */ currentDrag: null, /** * the cell coordinates of the where the mouse pointer is during a drag operation * @type {Object} * @memberOf CellSelection.prototype */ lastDragCell: null, /** * a millisecond value representing the previous time an autoscroll started * @type {number} * @default 0 * @memberOf CellSelection.prototype */ sbLastAuto: 0, /** * a millisecond value representing the time the current autoscroll started * @type {number} * @default 0 * @memberOf CellSelection.prototype */ sbAutoStart: 0, /** * @memberOf CellSelection.prototype * @param {Hypergrid} grid * @param {Object} event - the event details */ handleMouseUp: function(grid, event) { if (this.dragging) { this.dragging = false; } if (this.next) { this.next.handleMouseUp(grid, event); } }, /** * @memberOf CellSelection.prototype * @param {Hypergrid} grid * @param {Object} event - the event details */ handleMouseDown: function(grid, event) { var dx = event.gridCell.x, dy = event.dataCell.y, isSelectable = grid.behavior.getCellProperty(event.dataCell.x, event.gridCell.y, 'cellSelection'); if (isSelectable && event.isDataCell && !event.primitiveEvent.detail.isRightClick) { var dCell = grid.newPoint(dx, dy), primEvent = event.primitiveEvent, keys = primEvent.detail.keys; this.dragging = true; this.extendSelection(grid, dCell, keys); } else if (this.next) { this.next.handleMouseDown(grid, event); } }, /** * @memberOf CellSelection.prototype * @param {Hypergrid} grid * @param {Object} event - the event details */ handleMouseDrag: function(grid, event) { if (this.dragging && grid.properties.cellSelection && !event.primitiveEvent.detail.isRightClick) { this.currentDrag = event.primitiveEvent.detail.mouse; this.lastDragCell = grid.newPoint(event.gridCell.x, event.dataCell.y); this.checkDragScroll(grid, this.currentDrag); this.handleMouseDragCellSelection(grid, this.lastDragCell, event.primitiveEvent.detail.keys); } else if (this.next) { this.next.handleMouseDrag(grid, event); } }, /** * @memberOf CellSelection.prototype * @param {Hypergrid} grid * @param {Object} event - the event details */ handleKeyDown: function(grid, event) { var detail = event.detail, cellEvent = grid.getGridCellFromLastSelection(true), navKey = cellEvent && ( cellEvent.properties.mappedNavKey(detail.char, detail.ctrl) || cellEvent.properties.navKey(detail.char, detail.ctrl) ), handler = this['handle' + navKey]; // STEP 1: Move the selection if (handler) { handler.call(this, grid, detail); // STEP 2: Open the cell editor at the new position if it has `editOnNextCell` and is `editable` cellEvent = grid.getGridCellFromLastSelection(true); // new cell if (cellEvent.properties.editOnNextCell) { grid.editAt(cellEvent); // succeeds only if `editable` } // STEP 3: If editor not opened on new cell, take focus if (!grid.cellEditor) { grid.takeFocus(); } } else if (this.next) { this.next.handleKeyDown(grid, event); } }, /** * @memberOf CellSelection.prototype * @desc Handle a mousedrag selection. * @param {Hypergrid} grid * @param {Object} mouse - the event details * @param {Array} keys - array of the keys that are currently pressed down */ handleMouseDragCellSelection: function(grid, gridCell, keys) { var x = Math.max(0, gridCell.x), y = Math.max(0, gridCell.y), previousDragExtent = grid.getDragExtent(), mouseDown = grid.getMouseDown(), newX = x - mouseDown.x, newY = y - mouseDown.y; if (previousDragExtent.x === newX && previousDragExtent.y === newY) { return; } grid.clearMostRecentSelection(); grid.select(mouseDown.x, mouseDown.y, newX, newY); grid.setDragExtent(grid.newPoint(newX, newY)); grid.repaint(); }, /** * @memberOf CellSelection.prototype * @desc this checks while were dragging if we go outside the visible bounds, if so, kick off the external autoscroll check function (above) * @param {Hypergrid} grid * @param {Object} mouse - the event details */ checkDragScroll: function(grid, mouse) { if (!grid.properties.scrollingEnabled) { return; } var b = grid.getDataBounds(); var inside = b.contains(mouse); if (inside) { if (grid.isScrollingNow()) { grid.setScrollingNow(false); } } else if (!grid.isScrollingNow()) { grid.setScrollingNow(true); this.scrollDrag(grid); } }, /** * @memberOf CellSelection.prototype * @desc this function makes sure that while we are dragging outside of the grid visible bounds, we srcroll accordingly * @param {Hypergrid} grid */ scrollDrag: function(grid) { if (!grid.isScrollingNow()) { return; } var dragStartedInHeaderArea = grid.isMouseDownInHeaderArea(), lastDragCell = this.lastDragCell, b = grid.getDataBounds(), xOffset = 0, yOffset = 0, numFixedColumns = grid.getFixedColumnCount(), numFixedRows = grid.getFixedRowCount(), dragEndInFixedAreaX = lastDragCell.x < numFixedColumns, dragEndInFixedAreaY = lastDragCell.y < numFixedRows; if (!dragStartedInHeaderArea) { if (this.currentDrag.x < b.origin.x) { xOffset = -1; } if (this.currentDrag.y < b.origin.y) { yOffset = -1; } } if (this.currentDrag.x > b.origin.x + b.extent.x) { xOffset = 1; } if (this.currentDrag.y > b.origin.y + b.extent.y) { yOffset = 1; } var dragCellOffsetX = xOffset; var dragCellOffsetY = yOffset; if (dragEndInFixedAreaX) { dragCellOffsetX = 0; } if (dragEndInFixedAreaY) { dragCellOffsetY = 0; } this.lastDragCell = lastDragCell.plusXY(dragCellOffsetX, dragCellOffsetY); grid.scrollBy(xOffset, yOffset); this.handleMouseDragCellSelection(grid, lastDragCell, []); // update the selection grid.repaint(); setTimeout(this.scrollDrag.bind(this, grid), 25); }, /** * @memberOf CellSelection.prototype * @desc extend a selection or create one if there isnt yet * @param {Hypergrid} grid * @param {Object} gridCell - the event details * @param {Array} keys - array of the keys that are currently pressed down */ extendSelection: function(grid, gridCell, keys) { var hasCTRL = keys.indexOf('CTRL') >= 0, hasSHIFT = keys.indexOf('SHIFT') >= 0, mousePoint = grid.getMouseDown(), x = gridCell.x, // - numFixedColumns + scrollLeft; y = gridCell.y; // - numFixedRows + scrollTop; //were outside of the grid do nothing if (x < 0 || y < 0) { return; } //we have repeated a click in the same spot deslect the value from last time if ( hasCTRL && x === mousePoint.x && y === mousePoint.y ) { grid.clearMostRecentSelection(); grid.popMouseDown(); grid.repaint(); return; } if (!hasCTRL && !hasSHIFT) { grid.clearSelections(); } if (hasSHIFT) { grid.clearMostRecentSelection(); grid.select(mousePoint.x, mousePoint.y, x - mousePoint.x, y - mousePoint.y); grid.setDragExtent(grid.newPoint(x - mousePoint.x, y - mousePoint.y)); } else { grid.select(x, y, 0, 0); grid.setMouseDown(grid.newPoint(x, y)); grid.setDragExtent(grid.newPoint(0, 0)); } grid.repaint(); }, /** * @memberOf CellSelection.prototype * @param {Hypergrid} grid */ handleDOWNSHIFT: function(grid) { this.moveShiftSelect(grid, 0, 1); }, /** * @memberOf CellSelection.prototype * @param {Hypergrid} grid * @param {Object} event - the event details */ handleUPSHIFT: function(grid) { this.moveShiftSelect(grid, 0, -1); }, /** * @memberOf CellSelection.prototype * @param {Hypergrid} grid * @param {Object} event - the event details */ handleLEFTSHIFT: function(grid) { this.moveShiftSelect(grid, -1, 0); }, /** * @memberOf CellSelection.prototype * @param {Hypergrid} grid * @param {Object} event - the event details */ handleRIGHTSHIFT: function(grid) { this.moveShiftSelect(grid, 1, 0); }, /** * @memberOf CellSelection.prototype * @param {Hypergrid} grid * @param {Object} event - the event details */ handleDOWN: function(grid, event) { //keep the browser viewport from auto scrolling on key event event.primitiveEvent.preventDefault(); var count = this.getAutoScrollAcceleration(); grid.moveSingleSelect(0, count); }, /** * @memberOf CellSelection.prototype * @param {Hypergrid} grid * @param {Object} event - the event details */ handleUP: function(grid, event) { //keep the browser viewport from auto scrolling on key event event.primitiveEvent.preventDefault(); var count = this.getAutoScrollAcceleration(); grid.moveSingleSelect(0, -count); }, /** * @memberOf CellSelection.prototype * @param {Hypergrid} grid * @param {Object} event - the event details */ handleLEFT: function(grid) { grid.moveSingleSelect(-1, 0); }, /** * @memberOf CellSelection.prototype * @param {Hypergrid} grid * @param {Object} event - the event details */ handleRIGHT: function(grid) { grid.moveSingleSelect(1, 0); }, /** * @memberOf CellSelection.prototype * @desc If we are holding down the same navigation key, accelerate the increment we scroll * #### returns: integer */ getAutoScrollAcceleration: function() { var count = 1; var elapsed = this.getAutoScrollDuration() / 2000; count = Math.max(1, Math.floor(elapsed * elapsed * elapsed * elapsed)); return count; }, /** * @memberOf CellSelection.prototype * @desc set the start time to right now when we initiate an auto scroll */ setAutoScrollStartTime: function() { this.sbAutoStart = Date.now(); }, /** * @memberOf CellSelection.prototype * @desc update the autoscroll start time if we haven't autoscrolled within the last 500ms otherwise update the current autoscroll time */ pingAutoScroll: function() { var now = Date.now(); if (now - this.sbLastAuto > 500) { this.setAutoScrollStartTime(); } this.sbLastAuto = Date.now(); }, /** * @memberOf CellSelection.prototype * @desc answer how long we have been auto scrolling * #### returns: integer */ getAutoScrollDuration: function() { if (Date.now() - this.sbLastAuto > 500) { return 0; } return Date.now() - this.sbAutoStart; }, /** * @memberOf CellSelection.prototype * @desc Augment the most recent selection extent by (offsetX,offsetY) and scroll if necessary. * @param {Hypergrid} grid * @param {number} offsetX - x coordinate to start at * @param {number} offsetY - y coordinate to start at */ moveShiftSelect: function(grid, offsetX, offsetY) { if (grid.extendSelect(offsetX, offsetY)) { this.pingAutoScroll(); } } }); module.exports = CellSelection;