UNPKG

@revolist/revogrid

Version:

Virtual reactive data grid spreadsheet component - RevoGrid.

308 lines (307 loc) 10.9 kB
/*! * Built by Revolist OU ❤️ */ import { cropCellToMax, isHiddenStore, nextCell, SelectionStore } from "../store/index"; export const EMPTY_INDEX = -1; export class SelectionStoreConnector { constructor() { // dirty flag required to cleanup whole store in case visibility of panels changed this.dirty = false; this.stores = {}; this.columnStores = {}; this.rowStores = {}; /** * Helpers for data conversion */ this.storesByType = {}; this.storesXToType = {}; this.storesYToType = {}; this.sections = []; } get focusedStore() { var _a; for (let y in this.stores) { for (let x in this.stores[y]) { const focused = (_a = this.stores[y][x]) === null || _a === void 0 ? void 0 : _a.store.get('focus'); if (focused) { return { entity: this.stores[y][x], cell: focused, position: { x: parseInt(x, 10), y: parseInt(y, 10), }, }; } } } return null; } get edit() { var _a; return (_a = this.focusedStore) === null || _a === void 0 ? void 0 : _a.entity.store.get('edit'); } get focused() { var _a; return (_a = this.focusedStore) === null || _a === void 0 ? void 0 : _a.entity.store.get('focus'); } get selectedRange() { var _a; return (_a = this.focusedStore) === null || _a === void 0 ? void 0 : _a.entity.store.get('range'); } registerSection(e) { if (!e) { this.sections.length = 0; // some elements removed, rebuild stores this.dirty = true; return; } if (this.sections.indexOf(e) === -1) { this.sections.push(e); } } // check if require to cleanup all stores beforeUpdate() { if (this.dirty) { for (let y in this.stores) { for (let x in this.stores[y]) { this.stores[y][x].dispose(); } } this.dirty = false; } } registerColumn(x, type) { // if hidden just create store but no operations needed if (isHiddenStore(x)) { return new SelectionStore(); } if (this.columnStores[x]) { return this.columnStores[x]; } this.columnStores[x] = new SelectionStore(); // build cross-linking type to position this.storesByType[type] = x; this.storesXToType[x] = type; return this.columnStores[x]; } registerRow(y, type) { // if hidden just create store if (isHiddenStore(y)) { return new SelectionStore(); } if (this.rowStores[y]) { return this.rowStores[y]; } this.rowStores[y] = new SelectionStore(); // build cross linking type to position this.storesByType[type] = y; this.storesYToType[y] = type; return this.rowStores[y]; } /** * Cross store proxy, based on multiple dimensions */ register({ x, y }) { var _a, _b; // if hidden just create store if (isHiddenStore(x) || isHiddenStore(y)) { return new SelectionStore(); } if (!this.stores[y]) { this.stores[y] = {}; } if (this.stores[y][x]) { // Store already registered. Do not register twice return this.stores[y][x]; } this.stores[y][x] = new SelectionStore(); // proxy update, column store trigger only range area (_a = this.stores[y][x]) === null || _a === void 0 ? void 0 : _a.onChange('range', c => { this.columnStores[x].setRangeArea(c); this.rowStores[y].setRangeArea(c); }); // clean up on remove (_b = this.stores[y][x]) === null || _b === void 0 ? void 0 : _b.store.on('dispose', () => this.destroy(x, y)); return this.stores[y][x]; } destroy(x, y) { var _a, _b; (_a = this.columnStores[x]) === null || _a === void 0 ? void 0 : _a.dispose(); (_b = this.rowStores[y]) === null || _b === void 0 ? void 0 : _b.dispose(); delete this.rowStores[y]; delete this.columnStores[x]; // clear x cross-link if (this.storesXToType[x]) { const type = this.storesXToType[x]; delete this.storesXToType[x]; delete this.storesByType[type]; } // clear y cross-link if (this.storesYToType[y]) { const type = this.storesYToType[y]; delete this.storesYToType[y]; delete this.storesByType[type]; } if (this.stores[y]) { delete this.stores[y][x]; } // clear empty rows if (!Object.keys(this.stores[y] || {}).length) { delete this.stores[y]; } } setEditByCell(storePos, editCell) { this.focusByCell(storePos, editCell, editCell); this.setEdit(''); } /** * Sets the next focus cell before the current one. * * @param focus - The cell to set as the next focus. */ beforeNextFocusCell(focus) { var _a; // If there is no focused store, return early. if (!this.focusedStore) { return; } // Get the next store based on the current focus and the last cell. const lastCell = this.focusedStore.entity.store.get('lastCell'); const next = lastCell && this.getNextStore(focus, this.focusedStore.position, lastCell); // Set the next focus cell in the store. (_a = next === null || next === void 0 ? void 0 : next.store) === null || _a === void 0 ? void 0 : _a.setNextFocus(Object.assign(Object.assign({}, focus), next.item)); } focusByCell(storePos, start, end) { const store = this.stores[storePos.y][storePos.x]; this.focus(store, { focus: start, end }); } focus(store, { focus, end }) { const currentStorePointer = this.getCurrentStorePointer(store); if (!currentStorePointer) { return null; } // check for the focus in nearby store/viewport const lastCell = store.store.get('lastCell'); const next = lastCell && this.getNextStore(focus, currentStorePointer, lastCell); // if next store present - update if (next === null || next === void 0 ? void 0 : next.store) { const item = Object.assign(Object.assign({}, focus), next.item); this.focus(next.store, { focus: item, end: item }); return null; } if (lastCell) { focus = cropCellToMax(focus, lastCell); end = cropCellToMax(end, lastCell); } store.setFocus(focus, end); return focus; } /** * Retrieves the current store pointer based on the active store. * Clears focus from all stores except the active one. */ getCurrentStorePointer(store) { let currentStorePointer; // Iterate through all stores for (let y in this.stores) { for (let x in this.stores[y]) { const s = this.stores[y][x]; // Clear focus from stores other than the active one if (s !== store) { s.clearFocus(); } else { // Update the current store pointer with the active store coordinates currentStorePointer = { x: parseInt(x, 10), y: parseInt(y, 10) }; } } } return currentStorePointer; } /** * Retrieves the next store based on the focus cell and current store pointer. * If the next store exists, returns an object with the next store and the item in the new store. * If the next store does not exist, returns null. */ getNextStore(focus, currentStorePointer, lastCell) { // item in new store const nextItem = nextCell(focus, lastCell); let nextStore; if (nextItem) { Object.entries(nextItem).forEach(([type, nextItemCoord]) => { let stores; switch (type) { case 'x': // Get the X stores for the current Y coordinate of the current store pointer stores = this.getXStores(currentStorePointer.y); break; case 'y': // Get the Y stores for the current X coordinate of the current store pointer stores = this.getYStores(currentStorePointer.x); break; } // Get the next store based on the item in the new store if (nextItemCoord >= 0) { nextStore = stores[++currentStorePointer[type]]; } else { nextStore = stores[--currentStorePointer[type]]; const nextLastCell = nextStore === null || nextStore === void 0 ? void 0 : nextStore.store.get('lastCell'); if (nextLastCell) { nextItem[type] = nextLastCell[type] + nextItemCoord; } } }); } return { store: nextStore, item: nextItem, }; } clearAll() { var _a; for (let y in this.stores) { for (let x in this.stores[y]) { (_a = this.stores[y][x]) === null || _a === void 0 ? void 0 : _a.clearFocus(); } } } setEdit(val) { if (!this.focusedStore) { return; } this.focusedStore.entity.setEdit(val); } /** * Select all cells across all stores */ selectAll() { for (let y in this.stores) { for (let x in this.stores[y]) { const store = this.stores[y][x]; if (!store) { continue; } const lastCell = store.store.get('lastCell'); if (lastCell) { store.setRange({ x: 0, y: 0 }, { x: lastCell.x - 1, y: lastCell.y - 1 }); } } } } getXStores(y) { return this.stores[y]; } getYStores(x) { const stores = {}; for (let i in this.stores) { stores[i] = this.stores[i][x]; } return stores; } } //# sourceMappingURL=selection.store.connector.js.map