UNPKG

fin-hypergrid

Version:

Canvas-based high-performance grid

400 lines (347 loc) 11.9 kB
'use strict'; var Scrollbar = require('./modules').Scrollbar; /** * @summary Scrollbar support. * @desc Additions to `Hypergrid.prototype` for scrollbar support. * * All members are documented on the {@link Hypergrid} page. * @mixin scrolling.mixin */ exports.mixin = { /** * A float value between 0.0 - 1.0 of the vertical scroll position. * @type {number} * @memberOf Hypergrid# */ vScrollValue: 0, /** * A float value between 0.0 - 1.0 of the horizontal scroll position. * @type {number} * @memberOf Hypergrid# */ hScrollValue: 0, /** * The verticl scroll bar model/controller. * @type {FinBar} * @memberOf Hypergrid# */ sbVScroller: null, /** * The horizontal scroll bar model/controller. * @type {FinBar} * @memberOf Hypergrid# */ sbHScroller: null, /** * The previous value of sbVScrollVal. * @type {number} * @memberOf Hypergrid# */ sbPrevVScrollValue: null, /** * The previous value of sbHScrollValue. * @type {number} * @memberOf Hypergrid# */ sbPrevHScrollValue: null, scrollingNow: false, /** * @memberOf Hypergrid# * @summary Set for `scrollingNow` field. * @param {boolean} isItNow - The type of event we are interested in. */ setScrollingNow: function(isItNow) { this.scrollingNow = isItNow; }, /** * @memberOf Hypergrid# * @returns {boolean} The `scrollingNow` field. */ isScrollingNow: function() { return this.scrollingNow; }, /** * @memberOf Hypergrid# * @summary Scroll horizontal and vertically by the provided offsets. * @param {number} offsetX - Scroll in the x direction this much. * @param {number} offsetY - Scroll in the y direction this much. */ scrollBy: function(offsetX, offsetY) { this.scrollHBy(offsetX); this.scrollVBy(offsetY); }, /** * @memberOf Hypergrid# * @summary Scroll vertically by the provided offset. * @param {number} offsetY - Scroll in the y direction this much. */ scrollVBy: function(offsetY) { var max = this.sbVScroller.range.max; var oldValue = this.getVScrollValue(); var newValue = Math.min(max, Math.max(0, oldValue + offsetY)); if (newValue !== oldValue) { this.setVScrollValue(newValue); } }, /** * @memberOf Hypergrid# * @summary Scroll horizontally by the provided offset. * @param {number} offsetX - Scroll in the x direction this much. */ scrollHBy: function(offsetX) { var max = this.sbHScroller.range.max; var oldValue = this.getHScrollValue(); var newValue = Math.min(max, Math.max(0, oldValue + offsetX)); if (newValue !== oldValue) { this.setHScrollValue(newValue); } }, scrollToMakeVisible: function(c, r) { var delta, dw = this.renderer.dataWindow, fixedColumnCount = this.properties.fixedColumnCount, fixedRowCount = this.properties.fixedRowCount; // scroll only if target not in fixed columns if (c >= fixedColumnCount) { // target is to left of scrollable columns; negative delta scrolls left if ((delta = c - dw.origin.x) < 0) { this.sbHScroller.index += delta; // target is to right of scrollable columns; positive delta scrolls right // Note: The +1 forces right-most column to scroll left (just in case it was only partially in view) } else if ((c - dw.corner.x) > 0) { this.sbHScroller.index = this.renderer.getMinimumLeftPositionToShowColumn(c); } } if ( r >= fixedRowCount && // scroll only if target not in fixed rows ( // target is above scrollable rows; negative delta scrolls up (delta = r - dw.origin.y - 1) < 0 || // target is below scrollable rows; positive delta scrolls down (delta = r - dw.corner.y) > 0 ) ) { this.sbVScroller.index += delta; } }, selectCellAndScrollToMakeVisible: function(c, r) { this.scrollToMakeVisible(c, r); this.selectCell(c, r, true); }, /** * @memberOf Hypergrid# * @desc Set the vertical scroll value. * @param {number} newValue - The new scroll value. */ setVScrollValue: function(y) { var self = this; y = Math.min(this.sbVScroller.range.max, Math.max(0, Math.round(y))); if (y !== this.vScrollValue) { this.behavior.setScrollPositionY(y); this.behavior.changed(); var oldY = this.vScrollValue; this.vScrollValue = y; this.scrollValueChangedNotification(); setTimeout(function() { // self.sbVRangeAdapter.subjectChanged(); self.fireScrollEvent('fin-scroll-y', oldY, y); }); } }, /** * @memberOf Hypergrid# * @return {number} The vertical scroll value. */ getVScrollValue: function() { return this.vScrollValue; }, /** * @memberOf Hypergrid# * @desc Set the horizontal scroll value. * @param {number} newValue - The new scroll value. */ setHScrollValue: function(x) { var self = this; x = Math.min(this.sbHScroller.range.max, Math.max(0, Math.round(x))); if (x !== this.hScrollValue) { this.behavior.setScrollPositionX(x); this.behavior.changed(); var oldX = this.hScrollValue; this.hScrollValue = x; this.scrollValueChangedNotification(); setTimeout(function() { //self.sbHRangeAdapter.subjectChanged(); self.fireScrollEvent('fin-scroll-x', oldX, x); //self.synchronizeScrollingBoundries(); // todo: Commented off to prevent the grid from bouncing back, but there may be repercussions... }); } }, /** * @memberOf Hypergrid# * @returns The vertical scroll value. */ getHScrollValue: function() { return this.hScrollValue; }, /** * @memberOf Hypergrid# * @desc Initialize the scroll bars. */ initScrollbars: function() { if (this.sbHScroller && this.sbVScroller){ return; } var self = this; var horzBar = new Scrollbar({ orientation: 'horizontal', deltaXFactor: this.constructor.defaults.wheelHFactor, onchange: self.setHScrollValue.bind(self), cssStylesheetReferenceElement: this.div }); var vertBar = new Scrollbar({ orientation: 'vertical', deltaYFactor: this.constructor.defaults.wheelVFactor, onchange: self.setVScrollValue.bind(self), cssStylesheetReferenceElement: this.div, paging: { up: self.pageUp.bind(self), down: self.pageDown.bind(self) } }); this.sbHScroller = horzBar; this.sbVScroller = vertBar; var hPrefix = this.properties.hScrollbarClassPrefix; var vPrefix = this.properties.vScrollbarClassPrefix; if (hPrefix && hPrefix !== '') { this.sbHScroller.classPrefix = hPrefix; } if (vPrefix && vPrefix !== '') { this.sbVScroller.classPrefix = vPrefix; } this.div.appendChild(horzBar.bar); this.div.appendChild(vertBar.bar); this.resizeScrollbars(); }, resizeScrollbars: function() { this.sbHScroller.shortenBy(this.sbVScroller).resize(); //this.sbVScroller.shortenBy(this.sbHScroller); this.sbVScroller.resize(); }, /** * @memberOf Hypergrid# * @desc Scroll values have changed, we've been notified. */ setVScrollbarValues: function(max) { this.sbVScroller.range = { min: 0, max: max }; }, setHScrollbarValues: function(max) { this.sbHScroller.range = { min: 0, max: max }; }, scrollValueChangedNotification: function() { if ( this.hScrollValue !== this.sbPrevHScrollValue || this.vScrollValue !== this.sbPrevVScrollValue ) { this.sbPrevHScrollValue = this.hScrollValue; this.sbPrevVScrollValue = this.vScrollValue; if (this.cellEditor) { this.cellEditor.scrollValueChangedNotification(); } this.computeCellsBounds(); } }, /** * @memberOf Hypergrid# * @desc The data dimensions have changed, or our pixel boundaries have changed. * Adjust the scrollbar properties as necessary. */ synchronizeScrollingBoundaries: function() { var bounds = this.getBounds(); if (!bounds) { return; } var numFixedColumns = this.getFixedColumnCount(), numColumns = this.getColumnCount(), numRows = this.getRowCount(), scrollableWidth = bounds.width - this.behavior.getFixedColumnsMaxWidth(), gridProps = this.properties, borderBox = gridProps.boxSizing === 'border-box', lineGap = borderBox ? 0 : gridProps.gridLinesVWidth; for ( var columnsWidth = 0, lastPageColumnCount = 0; lastPageColumnCount < numColumns && columnsWidth < scrollableWidth; lastPageColumnCount++ ) { columnsWidth += this.getColumnWidth(numColumns - lastPageColumnCount - 1) + lineGap; } if (columnsWidth > scrollableWidth) { lastPageColumnCount--; } var scrollableHeight = this.renderer.getVisibleScrollHeight(); lineGap = borderBox ? 0 : gridProps.gridLinesHWidth; for ( var rowsHeight = 0, lastPageRowCount = 0; lastPageRowCount < numRows && rowsHeight < scrollableHeight; lastPageRowCount++ ) { rowsHeight += this.getRowHeight(numRows - lastPageRowCount - 1) + lineGap; } if (rowsHeight > scrollableHeight) { lastPageRowCount--; } // inform scroll bars if (this.sbHScroller) { var hMax = Math.max(0, numColumns - numFixedColumns - lastPageColumnCount); this.setHScrollbarValues(hMax); this.setHScrollValue(Math.min(this.getHScrollValue(), hMax)); } if (this.sbVScroller) { var vMax = Math.max(0, numRows - gridProps.fixedRowCount - lastPageRowCount); this.setVScrollbarValues(vMax); this.setVScrollValue(Math.min(this.getVScrollValue(), vMax)); } this.computeCellsBounds(); // schedule to happen *after* the repaint setTimeout(this.resizeScrollbars.bind(this)); }, /** * @memberOf Hypergrid# * @desc Scroll up one full page. * @returns {number} */ pageUp: function() { var rowNum = this.renderer.getPageUpRow(); this.setVScrollValue(rowNum); return rowNum; }, /** * @memberOf Hypergrid# * @desc Scroll down one full page. * @returns {number} */ pageDown: function() { var rowNum = this.renderer.getPageDownRow(); this.setVScrollValue(rowNum); return rowNum; }, /** * @memberOf Hypergrid# * @desc Not yet implemented. */ pageLeft: function() { throw 'page left not yet implemented'; }, /** * @memberOf Hypergrid# * @desc Not yet implemented. */ pageRight: function() { throw 'page right not yet implemented'; } };