@mhmdaljefri/revogrid
Version:
Virtual reactive data grid component - RevoGrid.
122 lines (121 loc) • 4.06 kB
JavaScript
/**
* Store is responsible for visible
* Viewport information for each dimension
* Redraw items during scrolling
*/
import { createStore } from '@stencil/store';
import { addMissingItems, getFirstItem, getLastItem, getUpdatedItemsByPosition, isActiveRange, updateMissingAndRange } from './viewport.helpers';
import { setStore } from '../../utils/store.utils';
function initialState() {
return {
// virtual item information per rendered item
items: [],
// virtual dom item order to render
start: 0,
end: 0,
// size of viewport in px
virtualSize: 0,
// total number of items
realCount: 0,
// last coordinate for store position restore
lastCoordinate: 0,
};
}
export default class ViewportStore {
constructor() {
this.store = createStore(initialState());
this.store.onChange('realCount', () => this.clear());
}
/** Render viewport based on coordinate, this is main method for draw */
setViewPortCoordinate(position, dimension) {
let virtualSize = this.store.get('virtualSize');
// no visible data to calculate
if (!virtualSize) {
return;
}
const frameOffset = dimension.frameOffset;
const outsize = frameOffset * 2 * dimension.originItemSize;
virtualSize += outsize;
let maxCoordinate = virtualSize;
if (dimension.realSize > virtualSize) {
maxCoordinate = dimension.realSize - virtualSize;
}
let toUpdate = {
lastCoordinate: position,
};
let pos = position;
pos -= frameOffset * dimension.originItemSize;
pos = pos < 0 ? 0 : pos < maxCoordinate ? pos : maxCoordinate;
const firstItem = getFirstItem(this.getItems());
const lastItem = getLastItem(this.getItems());
// left position changed
if (!isActiveRange(pos, firstItem)) {
toUpdate = Object.assign(Object.assign({}, toUpdate), getUpdatedItemsByPosition(pos, this.getItems(), this.store.get('realCount'), virtualSize, dimension));
setStore(this.store, Object.assign({}, toUpdate));
// right position changed
}
else if (firstItem && this.store.get('virtualSize') + pos > (lastItem === null || lastItem === void 0 ? void 0 : lastItem.end)) {
// check is any item missing for full fill content
const missing = addMissingItems(firstItem, this.store.get('realCount'), virtualSize + pos - firstItem.start, this.getItems(), dimension);
if (missing.length) {
const items = [...this.store.get('items')];
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);
setStore(this.store, Object.assign({}, toUpdate));
}
}
}
/** Update viewport sizes */
setViewPortDimension(sizes) {
const items = this.store.get('items');
const count = items.length;
// viewport not inited
if (!count) {
return;
}
let changedCoordinate = 0;
let i = 0;
let start = this.store.get('start');
// loop through array from initial item after recombination
while (i < count) {
const item = items[start];
// change pos if size change present before
if (changedCoordinate) {
item.start += changedCoordinate;
item.end += changedCoordinate;
}
// change size
const size = sizes[item.itemIndex];
if (size) {
const changedSize = size - item.size;
changedCoordinate += changedSize;
item.size = size;
item.end = item.start + size;
}
// loop by start index
start++;
i++;
if (start === count) {
start = 0;
}
}
setStore(this.store, { items: [...items] });
}
getItems() {
return {
items: this.store.get('items'),
start: this.store.get('start'),
end: this.store.get('end'),
};
}
setViewport(data) {
setStore(this.store, data);
}
clear() {
this.store.set('items', []);
}
}