@revolist/revogrid
Version:
Virtual reactive data grid spreadsheet component - RevoGrid.
124 lines (123 loc) • 4.35 kB
JavaScript
/*!
* Built by Revolist OU ❤️
*/
import { scaleValue } from "../utils";
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) {
if (virtualSize > contentSize) {
return 0;
}
return contentSize + (virtualSize ? clientSize - virtualSize : 0);
}
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.params = {
rgRow: Object.assign({}, initialParams),
rgCol: Object.assign({}, initialParams),
};
}
setParams(params, dimension) {
const virtualContentSize = getContentSize(params.contentSize, params.clientSize, params.virtualSize);
this.params[dimension] = Object.assign(Object.assign({}, params), { maxSize: virtualContentSize - params.clientSize, virtualContentSize });
}
// 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.previousScroll[e.dimension] = this.wrapCoordinate(e.coordinate, params);
this.preventArtificialScroll[e.dimension] = null;
this.cfg.applyScroll(Object.assign(Object.assign({}, e), { coordinate: params.virtualSize
? this.convert(e.coordinate, params, false)
: e.coordinate }));
}
catch (id) {
window.cancelAnimationFrame(id);
}
}
/**
* 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);
// let component know about scroll event started
this.cfg.runScroll({
dimension: dimension,
coordinate: param.virtualSize
? this.convert(coordinate, param)
: coordinate,
delta,
outside,
});
}
getParams(dimension) {
return this.params[dimension];
}
// check if scroll outside of region to avoid looping
wrapCoordinate(c, param) {
if (c < 0) {
return NO_COORDINATE;
}
if (typeof param.maxSize === 'number' && c > param.maxSize) {
return param.maxSize;
}
return 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;
}
/* convert virtual to real and back, scale range */
convert(pos, param, toReal = true) {
var _a;
const minRange = param.clientSize;
const from = [0, ((_a = param.virtualContentSize) !== null && _a !== void 0 ? _a : minRange) - minRange];
const to = [0, param.contentSize - param.virtualSize];
if (toReal) {
return scaleValue(pos, from, to);
}
return scaleValue(pos, to, from);
}
}