@revolist/revogrid
Version:
Virtual reactive data grid spreadsheet component - RevoGrid.
159 lines (158 loc) • 6.3 kB
JavaScript
/*!
* Built by Revolist OU ❤️
*/
import { getScrollDimension } from "./scroll.dimension.helpers";
const initialParams = {
contentSize: 0,
clientSize: 0,
virtualSize: 0,
maxSize: 0,
};
const NO_COORDINATE = -1;
/**
* Based on content size, client size and virtual size
* return full size
*/
export function getContentSize(contentSize, clientSize, virtualSize = 0) {
return getScrollDimension({
contentSize,
clientSize,
virtualSize,
}).physicalContentSize;
}
export default class LocalScrollService {
constructor(cfg) {
this.cfg = cfg;
this.preventArtificialScroll = {
rgRow: null,
rgCol: null,
};
// to check if scroll changed
this.previousScroll = {
rgRow: NO_COORDINATE,
rgCol: NO_COORDINATE,
};
this.previousLogicalScroll = {
rgRow: 0,
rgCol: 0,
};
this.params = {
rgRow: Object.assign({}, initialParams),
rgCol: Object.assign({}, initialParams),
};
}
setParams(params, dimension) {
const scrollDimension = getScrollDimension(params);
const virtualContentSize = scrollDimension.physicalContentSize;
this.params[dimension] = Object.assign(Object.assign({}, params), { maxSize: virtualContentSize - params.clientSize, virtualContentSize,
scrollDimension });
}
// apply scroll values after scroll done
async setScroll(e) {
this.cancelScroll(e.dimension);
// start frame animation
const frameAnimation = new Promise((resolve, reject) => {
// for example safari desktop has issues with animation frame
if (this.cfg.skipAnimationFrame) {
return resolve();
}
const animationId = window.requestAnimationFrame(() => {
resolve();
});
this.preventArtificialScroll[e.dimension] = reject.bind(null, animationId);
});
try {
await frameAnimation;
const params = this.getParams(e.dimension);
e.coordinate = Math.ceil(e.coordinate);
this.previousLogicalScroll[e.dimension] = this.wrapLogicalCoordinate(e.coordinate, params);
const physicalCoordinate = this.toPhysicalCoordinate(e.coordinate, params);
this.previousScroll[e.dimension] = this.wrapPhysicalCoordinate(physicalCoordinate, params);
this.preventArtificialScroll[e.dimension] = null;
this.cfg.applyScroll(Object.assign(Object.assign({}, e), { coordinate: physicalCoordinate }));
}
catch (id) {
window.cancelAnimationFrame(id);
}
}
async setScrollByDelta(e, currentPhysicalCoordinate) {
var _a;
const params = this.getParams(e.dimension);
const baseCoordinate = this.previousScroll[e.dimension] === NO_COORDINATE
? this.toLogicalCoordinate(currentPhysicalCoordinate, params)
: this.previousLogicalScroll[e.dimension];
const coordinate = this.wrapLogicalCoordinate(baseCoordinate + ((_a = e.delta) !== null && _a !== void 0 ? _a : 0), params);
const nextEvent = Object.assign(Object.assign({}, e), { coordinate });
await this.setScroll(nextEvent);
return nextEvent;
}
/**
* On scroll event started
*/
scroll(coordinate, dimension, force = false, delta, outside = false) {
// cancel all previous scrolls for same dimension
this.cancelScroll(dimension);
// drop if no change
if (!force && this.previousScroll[dimension] === coordinate) {
this.previousScroll[dimension] = NO_COORDINATE;
return;
}
const param = this.getParams(dimension);
const logicalCoordinate = this.toLogicalScrollCoordinate(coordinate, dimension, param, delta);
// let component know about scroll event started
this.cfg.runScroll({
dimension: dimension,
coordinate: logicalCoordinate,
delta,
outside,
});
this.previousLogicalScroll[dimension] = logicalCoordinate;
}
getParams(dimension) {
return this.params[dimension];
}
// check if scroll outside of region to avoid looping
wrapPhysicalCoordinate(c, param) {
if (c < 0) {
return NO_COORDINATE;
}
if (typeof param.maxSize === 'number' && c > param.maxSize) {
return param.maxSize;
}
return c;
}
wrapLogicalCoordinate(c, param) {
var _a, _b;
if (c < 0) {
return 0;
}
return Math.min(c, (_b = (_a = param.scrollDimension) === null || _a === void 0 ? void 0 : _a.logicalScrollSize) !== null && _b !== void 0 ? _b : c);
}
// prevent already started scroll, performance optimization
cancelScroll(dimension) {
var _a, _b;
(_b = (_a = this.preventArtificialScroll)[dimension]) === null || _b === void 0 ? void 0 : _b.call(_a);
this.preventArtificialScroll[dimension] = null;
}
toLogicalScrollCoordinate(coordinate, dimension, param, delta) {
const scrollDimension = param.scrollDimension;
if (!scrollDimension) {
return coordinate;
}
if (typeof delta === 'number' && scrollDimension.isCompressed) {
const base = this.previousScroll[dimension] === NO_COORDINATE
? scrollDimension.toLogicalCoordinate(coordinate - delta)
: this.previousLogicalScroll[dimension];
return scrollDimension.toLogicalCoordinate(scrollDimension.toPhysicalCoordinate(base + delta));
}
return scrollDimension.toLogicalCoordinate(coordinate);
}
toPhysicalCoordinate(coordinate, param) {
var _a, _b;
return (_b = (_a = param.scrollDimension) === null || _a === void 0 ? void 0 : _a.toPhysicalCoordinate(coordinate)) !== null && _b !== void 0 ? _b : coordinate;
}
toLogicalCoordinate(coordinate, param) {
var _a, _b;
return (_b = (_a = param.scrollDimension) === null || _a === void 0 ? void 0 : _a.toLogicalCoordinate(coordinate)) !== null && _b !== void 0 ? _b : coordinate;
}
}