UNPKG

@revolist/revogrid

Version:

Virtual reactive data grid spreadsheet component - RevoGrid.

152 lines (151 loc) 5.69 kB
/*! * 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