fin-hypergrid
Version:
Canvas-based high-performance grid
400 lines (347 loc) • 11.9 kB
JavaScript
'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';
}
};