/** * Implements a cache of ACIndex objects for columns in Grist table. * * The getColACIndex() function returns the corresponding ACIndex, building it if needed and * caching for subsequent calls. Any change to the column or a value in it invalidates the cache. * * It is available as tableData.columnACIndexes. * * It is currently used for auto-complete in the ReferenceEditor and ReferenceListEditor widgets. */ import {ACIndex, ACIndexImpl, normalizeText} from 'app/client/lib/ACIndex'; import {ColumnCache} from 'app/client/models/ColumnCache'; import {UserError} from 'app/client/models/errors'; import {TableData} from 'app/client/models/TableData'; import {localeCompare, nativeCompare} from 'app/common/gutil'; import {BaseFormatter} from 'app/common/ValueFormatter'; export interface ICellItem { rowId: number|'new'; text: string; // Formatted cell text. cleanText: string; // Trimmed lowercase text for searching. } export class ColumnACIndexes { private _columnCache = new ColumnCache>(this._tableData); constructor(private _tableData: TableData) {} /** * Returns the column index for the given column, using a cached one if available. * The formatter should be created using field.visibleColFormatter(). It's assumed that * getColACIndex() is called for the same column with the the same formatter. */ public getColACIndex(colId: string, formatter: BaseFormatter): ACIndex { return this._columnCache.getValue(colId, () => this._buildColACIndex(colId, formatter)); } private _buildColACIndex(colId: string, formatter: BaseFormatter): ACIndex { const rowIds = this._tableData.getRowIds(); const valColumn = this._tableData.getColValues(colId); if (!valColumn) { throw new UserError(`Invalid column ${this._tableData.tableId}.${colId}`); } const items: ICellItem[] = valColumn.map((val, i) => { const rowId = rowIds[i]; const text = formatter.formatAny(val); const cleanText = normalizeText(text); return {rowId, text, cleanText}; }); items.sort(itemCompare); return new ACIndexImpl(items); } } function itemCompare(a: ICellItem, b: ICellItem) { return localeCompare(a.cleanText, b.cleanText) || localeCompare(a.text, b.text) || nativeCompare(a.rowId, b.rowId); }