import {KoArray} from 'app/client/lib/koArray'; import {DocModel, IRowModel, recordSet, refRecord, ViewSectionRec} from 'app/client/models/DocModel'; import {ColumnRec, ValidationRec, ViewRec} from 'app/client/models/DocModel'; import * as modelUtil from 'app/client/models/modelUtil'; import {MANUALSORT} from 'app/common/gristTypes'; import * as ko from 'knockout'; import randomcolor from 'randomcolor'; // Represents a user-defined table. export interface TableRec extends IRowModel<"_grist_Tables"> { columns: ko.Computed>; validations: ko.Computed>; primaryView: ko.Computed; rawViewSection: ko.Computed; summarySource: ko.Computed; // A Set object of colRefs for all summarySourceCols of table. summarySourceColRefs: ko.Computed>; // tableId for normal tables, or tableId of the source table for summary tables. primaryTableId: ko.Computed; // The list of grouped by columns. groupByColumns: ko.Computed; // Grouping description. groupDesc: ko.PureComputed; // Name of the data table - title of the rawViewSection // for summary table it is name of primary table. tableName: modelUtil.KoSaveableObservable; // Table name with a default value (which is tableId). tableNameDef: modelUtil.KoSaveableObservable; // If user can select this table in various places. // Note: Some hidden tables can still be visible on RawData view. isHidden: ko.Computed; tableColor: string; disableAddRemoveRows: ko.Computed; supportsManualSort: ko.Computed; } export function createTableRec(this: TableRec, docModel: DocModel): void { this.columns = recordSet(this, docModel.columns, 'parentId', {sortBy: 'parentPos'}); this.validations = recordSet(this, docModel.validations, 'tableRef'); this.primaryView = refRecord(docModel.views, this.primaryViewId); this.rawViewSection = refRecord(docModel.viewSections, this.rawViewSectionRef); this.summarySource = refRecord(docModel.tables, this.summarySourceTable); this.isHidden = this.autoDispose( // This is repeated logic from isHiddenTable. ko.pureComputed(() => !!this.summarySourceTable() || this.tableId()?.startsWith("GristHidden")) ); // A Set object of colRefs for all summarySourceCols of this table. this.summarySourceColRefs = this.autoDispose(ko.pureComputed(() => new Set( this.columns().all().map(c => c.summarySourceCol()).filter(colRef => colRef)))); // tableId for normal tables, or tableId of the source table for summary tables. this.primaryTableId = ko.pureComputed(() => this.summarySourceTable() ? this.summarySource().tableId() : this.tableId()); this.groupByColumns = ko.pureComputed(() => this.columns().all().filter(c => c.summarySourceCol())); this.groupDesc = ko.pureComputed(() => { if (!this.summarySourceTable()) { return ''; } const groupBy = this.groupByColumns(); return `[${groupBy.length ? 'by ' + groupBy.map(c => c.label()).join(", ") : "Totals"}]`; }); // TODO: We should save this value and let users change it. this.tableColor = randomcolor({ luminosity: 'light', seed: typeof this.id() === 'number' ? 5 * this.id() : this.id() }); this.disableAddRemoveRows = ko.pureComputed(() => Boolean(this.summarySourceTable())); this.supportsManualSort = ko.pureComputed(() => this.columns().all().some(c => c.colId() === MANUALSORT)); this.tableName = modelUtil.savingComputed({ read: () => { if (this.isDisposed()) { return ''; } if (this.summarySourceTable()) { return this.summarySource().rawViewSection().title(); } else { // Need to be extra careful here, rawViewSection might be disposed. if (this.rawViewSection().isDisposed()) { return ''; } return this.rawViewSection().title(); } }, write: (setter, val) => { if (this.summarySourceTable()) { setter(this.summarySource().rawViewSection().title, val); } else { setter(this.rawViewSection().title, val); } } }); this.tableNameDef = modelUtil.fieldWithDefault( this.tableName, // TableId will be null/undefined when ACL will restrict access to it. ko.computed(() => { // During table removal, we could be disposed. if (this.isDisposed()) { return ''; } const table = this.summarySourceTable() ? this.summarySource() : this; return table.tableId() || ''; }) ); }