import {KoArray} from 'app/client/lib/koArray'; import {DocModel, IRowModel, recordSet, refRecord, TableRec, ViewFieldRec} from 'app/client/models/DocModel'; import {jsonObservable, ObjObservable} from 'app/client/models/modelUtil'; import * as gristTypes from 'app/common/gristTypes'; import {getReferencedTableId} from 'app/common/gristTypes'; import { BaseFormatter, createFullFormatterRaw, createVisibleColFormatterRaw, FullFormatterArgs } from 'app/common/ValueFormatter'; import * as ko from 'knockout'; // Represents a column in a user-defined table. export interface ColumnRec extends IRowModel<"_grist_Tables_column"> { table: ko.Computed; widgetOptionsJson: ObjObservable; viewFields: ko.Computed>; summarySource: ko.Computed; // Is an empty column (undecided if formula or data); denoted by an empty formula. isEmpty: ko.Computed; // Is a real formula column (not an empty column; i.e. contains a non-empty formula). isRealFormula: ko.Computed; // Is a trigger formula column (not formula, but contains non-empty formula) hasTriggerFormula: ko.Computed; // Used for transforming a column. // Reference to the original column for a transform column, or to itself for a non-transforming column. origColRef: ko.Observable; origCol: ko.Computed; // Indicates whether a column is transforming. Manually set, but should be true in both the original // column being transformed and that column's transform column. isTransforming: ko.Observable; // Convenience observable to obtain and set the type with no suffix pureType: ko.Computed; // The column's display column _displayColModel: ko.Computed; // Display col ref to use for the column, defaulting to the plain column itself. displayColRef: ko.Computed; // The display column to use for the column, or the column itself when no displayCol is set. displayColModel: ko.Computed; visibleColModel: ko.Computed; disableModifyBase: ko.Computed; // True if column config can't be modified (name, type, etc.) disableModify: ko.Computed; // True if column can't be modified or is being transformed. disableEditData: ko.Computed; // True to disable editing of the data in this column. isHiddenCol: ko.Computed; // Returns the rowModel for the referenced table, or null, if is not a reference column. refTable: ko.Computed; // Helper for Reference/ReferenceList columns, which returns a formatter according // to the visibleCol associated with column. visibleColFormatter: ko.Computed; // A formatter for values of this column. // The difference between visibleColFormatter and formatter is especially important for ReferenceLists: // `visibleColFormatter` is for individual elements of a list, sometimes hypothetical // (i.e. they aren't actually referenced but they exist in the visible column and are relevant to e.g. autocomplete) // `formatter` formats actual cell values, e.g. a whole list from the display column. formatter: ko.Computed; // Helper which adds/removes/updates column's displayCol to match the formula. saveDisplayFormula(formula: string): Promise|undefined; } export function createColumnRec(this: ColumnRec, docModel: DocModel): void { this.table = refRecord(docModel.tables, this.parentId); this.widgetOptionsJson = jsonObservable(this.widgetOptions); this.viewFields = recordSet(this, docModel.viewFields, 'colRef'); this.summarySource = refRecord(docModel.columns, this.summarySourceCol); // Is this an empty column (undecided if formula or data); denoted by an empty formula. this.isEmpty = ko.pureComputed(() => this.isFormula() && this.formula() === ''); // Is this a real formula column (not an empty column; i.e. contains a non-empty formula). this.isRealFormula = ko.pureComputed(() => this.isFormula() && this.formula() !== ''); // If this column has a trigger formula defined this.hasTriggerFormula = ko.pureComputed(() => !this.isFormula() && this.formula() !== ''); // Used for transforming a column. // Reference to the original column for a transform column, or to itself for a non-transforming column. this.origColRef = ko.observable(this.getRowId()); this.origCol = refRecord(docModel.columns, this.origColRef); // Indicates whether a column is transforming. Manually set, but should be true in both the original // column being transformed and that column's transform column. this.isTransforming = ko.observable(false); // Convenience observable to obtain and set the type with no suffix this.pureType = ko.pureComputed(() => gristTypes.extractTypeFromColType(this.type())); // The column's display column this._displayColModel = refRecord(docModel.columns, this.displayCol); // Helper which adds/removes/updates this column's displayCol to match the formula. this.saveDisplayFormula = function(formula) { if (formula !== (this._displayColModel().formula() || '')) { return docModel.docData.sendAction(["SetDisplayFormula", this.table().tableId(), null, this.getRowId(), formula]); } }; // Display col ref to use for the column, defaulting to the plain column itself. this.displayColRef = ko.pureComputed(() => this.displayCol() || this.origColRef()); // The display column to use for the column, or the column itself when no displayCol is set. this.displayColModel = refRecord(docModel.columns, this.displayColRef); this.visibleColModel = refRecord(docModel.columns, this.visibleCol); this.disableModifyBase = ko.pureComputed(() => Boolean(this.summarySourceCol())); this.disableModify = ko.pureComputed(() => this.disableModifyBase() || this.isTransforming()); this.disableEditData = ko.pureComputed(() => Boolean(this.summarySourceCol())); this.isHiddenCol = ko.pureComputed(() => gristTypes.isHiddenCol(this.colId())); // Returns the rowModel for the referenced table, or null, if this is not a reference column. this.refTable = ko.pureComputed(() => { const refTableId = getReferencedTableId(this.type() || ""); return refTableId ? docModel.allTables.all().find(t => t.tableId() === refTableId) || null : null; }); // Helper for Reference/ReferenceList columns, which returns a formatter according to the visibleCol // associated with this column. If no visible column available, return formatting for the column itself. this.visibleColFormatter = ko.pureComputed(() => formatterForRec(this, this, docModel, 'vcol')); this.formatter = ko.pureComputed(() => formatterForRec(this, this, docModel, 'full')); } export function formatterForRec( rec: ColumnRec | ViewFieldRec, colRec: ColumnRec, docModel: DocModel, kind: 'full' | 'vcol' ): BaseFormatter { const vcol = rec.visibleColModel(); const func = kind === 'full' ? createFullFormatterRaw : createVisibleColFormatterRaw; const args: FullFormatterArgs = { docData: docModel.docData, type: colRec.type(), widgetOpts: rec.widgetOptionsJson(), visibleColType: vcol?.type(), visibleColWidgetOpts: vcol?.widgetOptionsJson(), docSettings: docModel.docInfoRow.documentSettingsJson(), }; return func(args); }