@revolist/revogrid
Version:
Virtual reactive data grid spreadsheet component - RevoGrid.
152 lines (151 loc) • 5.69 kB
JavaScript
/*!
* Built by Revolist OU ❤️
*/
import { addMissingItems, getFirstItem, getLastItem, getUpdatedItemsByPosition, isActiveRange, setItemSizes, updateMissingAndRange, isActiveRangeOutsideLastItem, } from "./viewport.helpers";
import { createStore } from "@stencil/store";
import { setStore } from "../../utils";
/**
* Viewport store
* Used for virtualization (process of rendering only visible part of data)
* Redraws viewport based on position and dimension
*/
function initialState() {
return {
// virtual item information per rendered item
items: [],
// virtual dom item order to render
start: 0,
end: 0,
// size of virtual viewport in px
virtualSize: 0,
// total number of items
realCount: 0,
// size of viewport in px
clientSize: 0,
};
}
/**
* Viewport store class
*/
export class ViewportStore {
get lastCoordinate() {
return this.lastKnownScroll;
}
set lastCoordinate(value) {
this.lastKnownScroll = value;
}
constructor(type) {
this.type = type;
// last coordinate for store position restore
this.lastKnownScroll = 0;
this.store = createStore(initialState());
}
/**
* Render viewport based on coordinate
* It's the main method for draw
* Use force if you want to re-render viewport
*/
setViewPortCoordinate(position, dimension, force = false) {
const viewportSize = this.store.get('virtualSize');
// no visible data to calculate
if (!viewportSize) {
return;
}
const frameOffset = 1;
const singleOffsetInPx = dimension.originItemSize * frameOffset;
// add offset to virtual size from both sides
const outsize = singleOffsetInPx * 2;
// math virtual size is based on visible area + 2 items outside of visible area
const virtualSize = viewportSize + outsize;
// expected no scroll if real size less than virtual size, position is 0
let maxCoordinate = 0;
// if there is nodes outside of viewport, max coordinate has to be adjusted
if (dimension.realSize > viewportSize) {
// max coordinate is real size minus virtual/rendered space
maxCoordinate = dimension.realSize - viewportSize - singleOffsetInPx;
}
let pos = position;
// limit position to max and min coordinates
if (pos < 0) {
pos = 0;
}
else if (pos > maxCoordinate) {
pos = maxCoordinate;
}
// store last coordinate for further restore on redraw
this.lastCoordinate = pos;
// actual position is less than first item start based on offset
pos -= singleOffsetInPx;
pos = pos < 0 ? 0 : pos < maxCoordinate ? pos : maxCoordinate;
let allItems;
// if force clear all items and start from 0
if (force) {
allItems = {
items: [],
start: 0,
end: 0,
};
}
else {
allItems = this.getItems();
}
const firstItem = getFirstItem(allItems);
const lastItem = getLastItem(allItems);
let toUpdate = {};
// left position changed
// verify if new position is in range of previously rendered first item
if (!isActiveRange(pos, dimension.realSize, firstItem, lastItem)) {
toUpdate = Object.assign(Object.assign({}, toUpdate), getUpdatedItemsByPosition(pos, allItems, this.store.get('realCount'), virtualSize, dimension));
this.setViewport(Object.assign({}, toUpdate));
// verify is render area is outside of last item
}
else if (isActiveRangeOutsideLastItem(pos, virtualSize, firstItem, lastItem)) {
const items = [...allItems.items];
// check is any item missing for fulfill content
const missing = addMissingItems(firstItem, this.store.get('realCount'), virtualSize + pos - firstItem.start, allItems, {
sizes: dimension.sizes,
originItemSize: dimension.originItemSize,
});
// update missing items
if (missing.length) {
const range = {
start: this.store.get('start'),
end: this.store.get('end'),
};
updateMissingAndRange(items, missing, range);
toUpdate = Object.assign(Object.assign(Object.assign({}, toUpdate), { items: [...items] }), range);
this.setViewport(Object.assign({}, toUpdate));
}
}
}
/**
* Set sizes for existing items
*/
setOriginalSizes(size) {
const items = this.store.get('items');
const count = items.length;
// viewport not inited
if (!count) {
return;
}
setStore(this.store, {
items: setItemSizes(items, this.store.get('start'), size, this.lastCoordinate),
});
}
getItems() {
return {
items: this.store.get('items'),
start: this.store.get('start'),
end: this.store.get('end'),
};
}
setViewport(data) {
// drop items on virtual size change, require a new item set
// drop items on real size change, require a new item set
if (typeof data.realCount === 'number' || typeof data.virtualSize === 'number') {
data = Object.assign(Object.assign({}, data), { items: data.items || [] });
}
setStore(this.store, data);
}
}
//# sourceMappingURL=viewport.store.js.map