mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Cursor in custom widgets
Summary: Adding a new method `setCursorPos` in the widget API, and a new configuration option for the ready message `allowSelectBy` that exposes custom widgets in the `Select by` dropdown. With this, a custom widget can control the position of the linked widgets and is able to change the column in the creator panel. Test Plan: Added new test. Existing tests should pass. Reviewers: JakubSerafin Reviewed By: JakubSerafin Subscribers: JakubSerafin Differential Revision: https://phab.getgrist.com/D3993
This commit is contained in:
parent
c02acff361
commit
b6a431dd58
@ -1,5 +1,5 @@
|
|||||||
import { CursorPos } from "app/client/components/Cursor";
|
|
||||||
import { DocModel, ViewFieldRec } from "app/client/models/DocModel";
|
import { DocModel, ViewFieldRec } from "app/client/models/DocModel";
|
||||||
|
import { CursorPos } from 'app/plugin/GristAPI';
|
||||||
import BaseRowModel = require("app/client/models/BaseRowModel");
|
import BaseRowModel = require("app/client/models/BaseRowModel");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
|
import type {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
|
||||||
import type {CellValue} from 'app/common/DocActions';
|
import type {CellValue} from 'app/common/DocActions';
|
||||||
import type {TableData, UIRowId} from 'app/common/TableData';
|
import type {TableData} from 'app/common/TableData';
|
||||||
|
import type {UIRowId} from 'app/plugin/GristAPI';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The CopySelection class is an abstraction for a subset of currently selected cells.
|
* The CopySelection class is an abstraction for a subset of currently selected cells.
|
||||||
|
@ -8,17 +8,10 @@ import BaseView from 'app/client/components/BaseView';
|
|||||||
import * as commands from 'app/client/components/commands';
|
import * as commands from 'app/client/components/commands';
|
||||||
import BaseRowModel from 'app/client/models/BaseRowModel';
|
import BaseRowModel from 'app/client/models/BaseRowModel';
|
||||||
import {LazyArrayModel} from 'app/client/models/DataTableModel';
|
import {LazyArrayModel} from 'app/client/models/DataTableModel';
|
||||||
import type {UIRowId} from 'app/common/TableData';
|
import {CursorPos, UIRowId} from 'app/plugin/GristAPI';
|
||||||
import {Disposable} from 'grainjs';
|
import {Disposable} from 'grainjs';
|
||||||
import * as ko from 'knockout';
|
import * as ko from 'knockout';
|
||||||
|
|
||||||
export interface CursorPos {
|
|
||||||
rowId?: UIRowId;
|
|
||||||
rowIndex?: number;
|
|
||||||
fieldIndex?: number;
|
|
||||||
sectionId?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
function nullAsUndefined<T>(value: T|null|undefined): T|undefined {
|
function nullAsUndefined<T>(value: T|null|undefined): T|undefined {
|
||||||
return value == null ? undefined : value;
|
return value == null ? undefined : value;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import {CursorPos} from 'app/client/components/Cursor';
|
|
||||||
import {GristDoc} from 'app/client/components/GristDoc';
|
import {GristDoc} from 'app/client/components/GristDoc';
|
||||||
import {getStorage} from 'app/client/lib/storage';
|
import {getStorage} from 'app/client/lib/storage';
|
||||||
import {IDocPage, isViewDocPage, ViewDocPage} from 'app/common/gristUrls';
|
import {IDocPage, isViewDocPage, ViewDocPage} from 'app/common/gristUrls';
|
||||||
import {Disposable, Listener, Observable} from 'grainjs';
|
import {Disposable, Listener, Observable} from 'grainjs';
|
||||||
import {reportError} from 'app/client/models/errors';
|
import {reportError} from 'app/client/models/errors';
|
||||||
|
import {CursorPos} from 'app/plugin/GristAPI';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enriched cursor position with a view id
|
* Enriched cursor position with a view id
|
||||||
|
@ -9,7 +9,6 @@ import BaseView from 'app/client/components/BaseView';
|
|||||||
import {isNumericLike, isNumericOnly} from 'app/client/components/ChartView';
|
import {isNumericLike, isNumericOnly} from 'app/client/components/ChartView';
|
||||||
import {CodeEditorPanel} from 'app/client/components/CodeEditorPanel';
|
import {CodeEditorPanel} from 'app/client/components/CodeEditorPanel';
|
||||||
import * as commands from 'app/client/components/commands';
|
import * as commands from 'app/client/components/commands';
|
||||||
import {CursorPos} from 'app/client/components/Cursor';
|
|
||||||
import {CursorMonitor, ViewCursorPos} from "app/client/components/CursorMonitor";
|
import {CursorMonitor, ViewCursorPos} from "app/client/components/CursorMonitor";
|
||||||
import {DocComm} from 'app/client/components/DocComm';
|
import {DocComm} from 'app/client/components/DocComm';
|
||||||
import * as DocConfigTab from 'app/client/components/DocConfigTab';
|
import * as DocConfigTab from 'app/client/components/DocConfigTab';
|
||||||
@ -70,6 +69,7 @@ import {LocalPlugin} from "app/common/plugin";
|
|||||||
import {StringUnion} from 'app/common/StringUnion';
|
import {StringUnion} from 'app/common/StringUnion';
|
||||||
import {TableData} from 'app/common/TableData';
|
import {TableData} from 'app/common/TableData';
|
||||||
import {DocStateComparison} from 'app/common/UserAPI';
|
import {DocStateComparison} from 'app/common/UserAPI';
|
||||||
|
import {CursorPos} from 'app/plugin/GristAPI';
|
||||||
import {
|
import {
|
||||||
bundleChanges,
|
bundleChanges,
|
||||||
Computed,
|
Computed,
|
||||||
@ -1256,7 +1256,7 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
const fieldIndex = activeSection.viewFields.peek().all().findIndex(f => f.colRef.peek() === hash.colRef);
|
const fieldIndex = activeSection.viewFields.peek().all().findIndex(f => f.colRef.peek() === hash.colRef);
|
||||||
if (fieldIndex >= 0) {
|
if (fieldIndex >= 0) {
|
||||||
const view = await this._waitForView(activeSection);
|
const view = await this._waitForView(activeSection);
|
||||||
view?.setCursorPos({sectionId: hash.sectionId, rowId: hash.rowId, fieldIndex});
|
view?.setCursorPos({rowId: hash.rowId, fieldIndex});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.viewLayout?.maximized.set(hash.sectionId);
|
this.viewLayout?.maximized.set(hash.sectionId);
|
||||||
@ -1306,7 +1306,7 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
const fieldIndex = popupSection.viewFields.peek().all().findIndex(f => f.colRef.peek() === hash.colRef);
|
const fieldIndex = popupSection.viewFields.peek().all().findIndex(f => f.colRef.peek() === hash.colRef);
|
||||||
if (fieldIndex >= 0) {
|
if (fieldIndex >= 0) {
|
||||||
const view = await this._waitForView(popupSection);
|
const view = await this._waitForView(popupSection);
|
||||||
view?.setCursorPos({sectionId: hash.sectionId, rowId: hash.rowId, fieldIndex});
|
view?.setCursorPos({rowId: hash.rowId, fieldIndex});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,13 @@ import {DocModel} from 'app/client/models/DocModel';
|
|||||||
import {ColumnRec} from "app/client/models/entities/ColumnRec";
|
import {ColumnRec} from "app/client/models/entities/ColumnRec";
|
||||||
import {TableRec} from "app/client/models/entities/TableRec";
|
import {TableRec} from "app/client/models/entities/TableRec";
|
||||||
import {ViewSectionRec} from "app/client/models/entities/ViewSectionRec";
|
import {ViewSectionRec} from "app/client/models/entities/ViewSectionRec";
|
||||||
import {UIRowId} from "app/common/TableData";
|
|
||||||
import {LinkConfig} from "app/client/ui/selectBy";
|
import {LinkConfig} from "app/client/ui/selectBy";
|
||||||
import {FilterColValues, QueryOperation} from "app/common/ActiveDocAPI";
|
import {FilterColValues, QueryOperation} from "app/common/ActiveDocAPI";
|
||||||
import {isList, isListType, isRefListType} from "app/common/gristTypes";
|
import {isList, isListType, isRefListType} from "app/common/gristTypes";
|
||||||
import * as gutil from "app/common/gutil";
|
import * as gutil from "app/common/gutil";
|
||||||
|
import {UIRowId} from 'app/plugin/GristAPI';
|
||||||
import {encodeObject} from 'app/plugin/objtypes';
|
import {encodeObject} from 'app/plugin/objtypes';
|
||||||
import {Disposable, toKo} from "grainjs";
|
import {Disposable} from "grainjs";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import identity = require('lodash/identity');
|
import identity = require('lodash/identity');
|
||||||
import mapValues = require('lodash/mapValues');
|
import mapValues = require('lodash/mapValues');
|
||||||
@ -85,7 +85,7 @@ export class LinkingState extends Disposable {
|
|||||||
|
|
||||||
if (tgtColId) {
|
if (tgtColId) {
|
||||||
const operation = isRefListType(tgtCol.type()) ? 'intersects' : 'in';
|
const operation = isRefListType(tgtCol.type()) ? 'intersects' : 'in';
|
||||||
if (srcSection.parentKey() === 'custom') {
|
if (srcSection.selectedRowsActive()) {
|
||||||
this.filterColValues = this._srcCustomFilter(tgtColId, operation);
|
this.filterColValues = this._srcCustomFilter(tgtColId, operation);
|
||||||
} else if (srcColId) {
|
} else if (srcColId) {
|
||||||
this.filterColValues = this._srcCellFilter(tgtColId, operation);
|
this.filterColValues = this._srcCellFilter(tgtColId, operation);
|
||||||
@ -128,7 +128,7 @@ export class LinkingState extends Disposable {
|
|||||||
}
|
}
|
||||||
_filterColValues(result);
|
_filterColValues(result);
|
||||||
}
|
}
|
||||||
} else if (srcSection.parentKey() === 'custom') {
|
} else if (srcSection.selectedRowsActive()) {
|
||||||
this.filterColValues = this._srcCustomFilter('id', 'in');
|
this.filterColValues = this._srcCustomFilter('id', 'in');
|
||||||
} else {
|
} else {
|
||||||
const srcValueFunc = srcColId ? this._makeSrcCellGetter() : identity;
|
const srcValueFunc = srcColId ? this._makeSrcCellGetter() : identity;
|
||||||
@ -207,7 +207,7 @@ export class LinkingState extends Disposable {
|
|||||||
// Value for this.filterColValues based on the values in srcSection.selectedRows
|
// Value for this.filterColValues based on the values in srcSection.selectedRows
|
||||||
private _srcCustomFilter(colId: string, operation: QueryOperation): ko.Computed<FilterColValues> | undefined {
|
private _srcCustomFilter(colId: string, operation: QueryOperation): ko.Computed<FilterColValues> | undefined {
|
||||||
return this.autoDispose(ko.computed(() => {
|
return this.autoDispose(ko.computed(() => {
|
||||||
const values = toKo(ko, this._srcSection.selectedRows)();
|
const values = this._srcSection.selectedRows();
|
||||||
return {filters: {[colId]: values}, operations: {[colId]: operation}} as FilterColValues;
|
return {filters: {[colId]: values}, operations: {[colId]: operation}} as FilterColValues;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {CursorPos} from 'app/client/components/Cursor';
|
|
||||||
import {GristDoc} from 'app/client/components/GristDoc';
|
import {GristDoc} from 'app/client/components/GristDoc';
|
||||||
import * as dispose from 'app/client/lib/dispose';
|
import * as dispose from 'app/client/lib/dispose';
|
||||||
import {MinimalActionGroup} from 'app/common/ActionGroup';
|
import {MinimalActionGroup} from 'app/common/ActionGroup';
|
||||||
import {PromiseChain, setDefault} from 'app/common/gutil';
|
import {PromiseChain, setDefault} from 'app/common/gutil';
|
||||||
|
import {CursorPos} from 'app/plugin/GristAPI';
|
||||||
import {fromKo, Observable} from 'grainjs';
|
import {fromKo, Observable} from 'grainjs';
|
||||||
import * as ko from 'knockout';
|
import * as ko from 'knockout';
|
||||||
import sortBy = require('lodash/sortBy');
|
import sortBy = require('lodash/sortBy');
|
||||||
|
@ -7,7 +7,7 @@ import {AccessLevel, isSatisfied} from 'app/common/CustomWidget';
|
|||||||
import {DisposableWithEvents} from 'app/common/DisposableWithEvents';
|
import {DisposableWithEvents} from 'app/common/DisposableWithEvents';
|
||||||
import {BulkColValues, fromTableDataAction, RowRecord} from 'app/common/DocActions';
|
import {BulkColValues, fromTableDataAction, RowRecord} from 'app/common/DocActions';
|
||||||
import {extractInfoFromColType, reencodeAsAny} from 'app/common/gristTypes';
|
import {extractInfoFromColType, reencodeAsAny} from 'app/common/gristTypes';
|
||||||
import {AccessTokenOptions, CustomSectionAPI, GristDocAPI, GristView,
|
import {AccessTokenOptions, CursorPos, CustomSectionAPI, GristDocAPI, GristView,
|
||||||
InteractionOptionsRequest, WidgetAPI, WidgetColumnMap} from 'app/plugin/grist-plugin-api';
|
InteractionOptionsRequest, WidgetAPI, WidgetColumnMap} from 'app/plugin/grist-plugin-api';
|
||||||
import {MsgType, Rpc} from 'grain-rpc';
|
import {MsgType, Rpc} from 'grain-rpc';
|
||||||
import {Computed, Disposable, dom, Observable} from 'grainjs';
|
import {Computed, Disposable, dom, Observable} from 'grainjs';
|
||||||
@ -380,12 +380,25 @@ export class GristViewImpl implements GristView {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is deprecated method to turn on cursor linking. Previously it was used
|
||||||
|
* to create a custom row id filter. Now widgets can be treated as normal source of linking.
|
||||||
|
* Now allowSelectBy should be set using the ready event.
|
||||||
|
*/
|
||||||
public async allowSelectBy(): Promise<void> {
|
public async allowSelectBy(): Promise<void> {
|
||||||
this._baseView.viewSection.allowSelectBy.set(true);
|
this._baseView.viewSection.allowSelectBy(true);
|
||||||
|
// This is to preserve a legacy behavior, where when allowSelectBy is called widget expected
|
||||||
|
// that the filter was already applied to clear all rows.
|
||||||
|
this._baseView.viewSection.selectedRows([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setSelectedRows(rowIds: number[]): Promise<void> {
|
public async setSelectedRows(rowIds: number[]|null): Promise<void> {
|
||||||
this._baseView.viewSection.selectedRows.set(rowIds);
|
this._baseView.viewSection.selectedRows(rowIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setCursorPos(cursorPos: CursorPos): Promise<void> {
|
||||||
|
this._baseView.setCursorPos(cursorPos);
|
||||||
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _visibleColumns() {
|
private _visibleColumns() {
|
||||||
@ -615,5 +628,8 @@ export class CustomSectionAPIImpl extends Disposable implements CustomSectionAPI
|
|||||||
} else {
|
} else {
|
||||||
this._section.columnsToMap(null);
|
this._section.columnsToMap(null);
|
||||||
}
|
}
|
||||||
|
if (settings.allowSelectBy !== undefined) {
|
||||||
|
this._section.allowSelectBy(settings.allowSelectBy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
app/client/declarations.d.ts
vendored
3
app/client/declarations.d.ts
vendored
@ -25,7 +25,7 @@ declare module "app/client/components/Base" {
|
|||||||
|
|
||||||
declare module "app/client/components/BaseView" {
|
declare module "app/client/components/BaseView" {
|
||||||
|
|
||||||
import {Cursor, CursorPos} from 'app/client/components/Cursor';
|
import {Cursor} from 'app/client/components/Cursor';
|
||||||
import {GristDoc} from 'app/client/components/GristDoc';
|
import {GristDoc} from 'app/client/components/GristDoc';
|
||||||
import {IGristUrlState} from 'app/common/gristUrls';
|
import {IGristUrlState} from 'app/common/gristUrls';
|
||||||
import {SelectionSummary} from 'app/client/components/SelectionSummary';
|
import {SelectionSummary} from 'app/client/components/SelectionSummary';
|
||||||
@ -39,6 +39,7 @@ declare module "app/client/components/BaseView" {
|
|||||||
import {SortedRowSet} from 'app/client/models/rowset';
|
import {SortedRowSet} from 'app/client/models/rowset';
|
||||||
import {IColumnFilterMenuOptions} from 'app/client/ui/ColumnFilterMenu';
|
import {IColumnFilterMenuOptions} from 'app/client/ui/ColumnFilterMenu';
|
||||||
import {FieldBuilder} from "app/client/widgets/FieldBuilder";
|
import {FieldBuilder} from "app/client/widgets/FieldBuilder";
|
||||||
|
import {CursorPos} from 'app/plugin/GristAPI';
|
||||||
import {DomArg} from 'grainjs';
|
import {DomArg} from 'grainjs';
|
||||||
import {IOpenController} from 'popweasel';
|
import {IOpenController} from 'popweasel';
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import * as rowset from 'app/client/models/rowset';
|
|||||||
import { MANUALSORT } from 'app/common/gristTypes';
|
import { MANUALSORT } from 'app/common/gristTypes';
|
||||||
import { SortFunc } from 'app/common/SortFunc';
|
import { SortFunc } from 'app/common/SortFunc';
|
||||||
import { Sort } from 'app/common/SortSpec';
|
import { Sort } from 'app/common/SortSpec';
|
||||||
import { UIRowId } from 'app/common/TableData';
|
import { UIRowId } from 'app/plugin/GristAPI';
|
||||||
import * as ko from 'knockout';
|
import * as ko from 'knockout';
|
||||||
import range = require('lodash/range');
|
import range = require('lodash/range');
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ import {isHiddenTable, isSummaryTable} from 'app/common/isHiddenTable';
|
|||||||
import {canEdit} from 'app/common/roles';
|
import {canEdit} from 'app/common/roles';
|
||||||
import {RowFilterFunc} from 'app/common/RowFilterFunc';
|
import {RowFilterFunc} from 'app/common/RowFilterFunc';
|
||||||
import {schema, SchemaTypes} from 'app/common/schema';
|
import {schema, SchemaTypes} from 'app/common/schema';
|
||||||
import {UIRowId} from 'app/common/TableData';
|
|
||||||
import {ACLRuleRec, createACLRuleRec} from 'app/client/models/entities/ACLRuleRec';
|
import {ACLRuleRec, createACLRuleRec} from 'app/client/models/entities/ACLRuleRec';
|
||||||
import {ColumnRec, createColumnRec} from 'app/client/models/entities/ColumnRec';
|
import {ColumnRec, createColumnRec} from 'app/client/models/entities/ColumnRec';
|
||||||
import {createDocInfoRec, DocInfoRec} from 'app/client/models/entities/DocInfoRec';
|
import {createDocInfoRec, DocInfoRec} from 'app/client/models/entities/DocInfoRec';
|
||||||
@ -45,6 +44,7 @@ import {CellRec, createCellRec} from 'app/client/models/entities/CellRec';
|
|||||||
import {RefListValue} from 'app/common/gristTypes';
|
import {RefListValue} from 'app/common/gristTypes';
|
||||||
import {decodeObject} from 'app/plugin/objtypes';
|
import {decodeObject} from 'app/plugin/objtypes';
|
||||||
import {toKo} from 'grainjs';
|
import {toKo} from 'grainjs';
|
||||||
|
import {UIRowId} from 'app/plugin/GristAPI';
|
||||||
|
|
||||||
// Re-export all the entity types available. The recommended usage is like this:
|
// Re-export all the entity types available. The recommended usage is like this:
|
||||||
// import {ColumnRec, ViewFieldRec} from 'app/client/models/DocModel';
|
// import {ColumnRec, ViewFieldRec} from 'app/client/models/DocModel';
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
* TODO: client-side should show "..." or "50000 more rows not shown" in that case.
|
* TODO: client-side should show "..." or "50000 more rows not shown" in that case.
|
||||||
* TODO: Reference columns don't work properly because always use a displayCol which relies on formulas
|
* TODO: Reference columns don't work properly because always use a displayCol which relies on formulas
|
||||||
*/
|
*/
|
||||||
|
import {ClientColumnGettersByColId} from 'app/client/models/ClientColumnGetters';
|
||||||
import DataTableModel from 'app/client/models/DataTableModel';
|
import DataTableModel from 'app/client/models/DataTableModel';
|
||||||
import {DocModel} from 'app/client/models/DocModel';
|
import {DocModel} from 'app/client/models/DocModel';
|
||||||
import {BaseFilteredRowSource, RowList, RowSource} from 'app/client/models/rowset';
|
import {BaseFilteredRowSource, RowList, RowSource} from 'app/client/models/rowset';
|
||||||
@ -36,11 +37,11 @@ import {DocData} from 'app/common/DocData';
|
|||||||
import {nativeCompare} from 'app/common/gutil';
|
import {nativeCompare} from 'app/common/gutil';
|
||||||
import {IRefCountSub, RefCountMap} from 'app/common/RefCountMap';
|
import {IRefCountSub, RefCountMap} from 'app/common/RefCountMap';
|
||||||
import {getLinkingFilterFunc, RowFilterFunc} from 'app/common/RowFilterFunc';
|
import {getLinkingFilterFunc, RowFilterFunc} from 'app/common/RowFilterFunc';
|
||||||
import {TableData as BaseTableData, UIRowId} from 'app/common/TableData';
|
import {TableData as BaseTableData} from 'app/common/TableData';
|
||||||
import {tbind} from 'app/common/tbind';
|
import {tbind} from 'app/common/tbind';
|
||||||
|
import {UIRowId} from 'app/plugin/GristAPI';
|
||||||
import {Disposable, Holder, IDisposableOwnerT} from 'grainjs';
|
import {Disposable, Holder, IDisposableOwnerT} from 'grainjs';
|
||||||
import * as ko from 'knockout';
|
import * as ko from 'knockout';
|
||||||
import {ClientColumnGettersByColId} from 'app/client/models/ClientColumnGetters';
|
|
||||||
import debounce = require('lodash/debounce');
|
import debounce = require('lodash/debounce');
|
||||||
|
|
||||||
// Limit on the how many rows to request for OnDemand tables.
|
// Limit on the how many rows to request for OnDemand tables.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// tslint:disable:no-console
|
// tslint:disable:no-console
|
||||||
// TODO: Add documentation and clean up log statements.
|
// TODO: Add documentation and clean up log statements.
|
||||||
|
|
||||||
import {CursorPos} from 'app/client/components/Cursor';
|
|
||||||
import {GristDoc} from 'app/client/components/GristDoc';
|
import {GristDoc} from 'app/client/components/GristDoc';
|
||||||
import {PageRec, ViewFieldRec, ViewSectionRec} from 'app/client/models/DocModel';
|
import {PageRec, ViewFieldRec, ViewSectionRec} from 'app/client/models/DocModel';
|
||||||
import {reportError} from 'app/client/models/errors';
|
import {reportError} from 'app/client/models/errors';
|
||||||
@ -10,9 +9,10 @@ import {IDocPage} from 'app/common/gristUrls';
|
|||||||
import {nativeCompare, waitObs} from 'app/common/gutil';
|
import {nativeCompare, waitObs} from 'app/common/gutil';
|
||||||
import {TableData} from 'app/common/TableData';
|
import {TableData} from 'app/common/TableData';
|
||||||
import {BaseFormatter} from 'app/common/ValueFormatter';
|
import {BaseFormatter} from 'app/common/ValueFormatter';
|
||||||
|
import { makeT } from 'app/client/lib/localization';
|
||||||
|
import {CursorPos} from 'app/plugin/GristAPI';
|
||||||
import {Computed, Disposable, Observable} from 'grainjs';
|
import {Computed, Disposable, Observable} from 'grainjs';
|
||||||
import debounce = require('lodash/debounce');
|
import debounce = require('lodash/debounce');
|
||||||
import { makeT } from 'app/client/lib/localization';
|
|
||||||
|
|
||||||
const t = makeT('SearchModel');
|
const t = makeT('SearchModel');
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import {ColumnRec, ViewFieldRec, ViewSectionRec} from 'app/client/models/DocMode
|
|||||||
import {TableData} from 'app/client/models/TableData';
|
import {TableData} from 'app/client/models/TableData';
|
||||||
import {buildColFilter, ColumnFilterFunc} from 'app/common/ColumnFilterFunc';
|
import {buildColFilter, ColumnFilterFunc} from 'app/common/ColumnFilterFunc';
|
||||||
import {buildRowFilter, RowFilterFunc, RowValueFunc } from 'app/common/RowFilterFunc';
|
import {buildRowFilter, RowFilterFunc, RowValueFunc } from 'app/common/RowFilterFunc';
|
||||||
import {UIRowId} from 'app/common/TableData';
|
import {UIRowId} from 'app/plugin/GristAPI';
|
||||||
import {Computed, Disposable, MutableObsArray, obsArray, Observable, UseCB} from 'grainjs';
|
import {Computed, Disposable, MutableObsArray, obsArray, Observable, UseCB} from 'grainjs';
|
||||||
|
|
||||||
export type {ColumnFilterFunc};
|
export type {ColumnFilterFunc};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import BaseView from 'app/client/components/BaseView';
|
import BaseView from 'app/client/components/BaseView';
|
||||||
import {CursorPos} from 'app/client/components/Cursor';
|
|
||||||
import {LinkingState} from 'app/client/components/LinkingState';
|
import {LinkingState} from 'app/client/components/LinkingState';
|
||||||
import {KoArray} from 'app/client/lib/koArray';
|
import {KoArray} from 'app/client/lib/koArray';
|
||||||
import {
|
import {
|
||||||
@ -22,11 +21,11 @@ import {AccessLevel, ICustomWidget} from 'app/common/CustomWidget';
|
|||||||
import {UserAction} from 'app/common/DocActions';
|
import {UserAction} from 'app/common/DocActions';
|
||||||
import {arrayRepeat} from 'app/common/gutil';
|
import {arrayRepeat} from 'app/common/gutil';
|
||||||
import {Sort} from 'app/common/SortSpec';
|
import {Sort} from 'app/common/SortSpec';
|
||||||
import {UIRowId} from 'app/common/TableData';
|
|
||||||
import {ColumnsToMap, WidgetColumnMap} from 'app/plugin/CustomSectionAPI';
|
import {ColumnsToMap, WidgetColumnMap} from 'app/plugin/CustomSectionAPI';
|
||||||
import {ColumnToMapImpl} from 'app/client/models/ColumnToMap';
|
import {ColumnToMapImpl} from 'app/client/models/ColumnToMap';
|
||||||
import {BEHAVIOR} from 'app/client/models/entities/ColumnRec';
|
import {BEHAVIOR} from 'app/client/models/entities/ColumnRec';
|
||||||
import {removeRule, RuleOwner} from 'app/client/models/RuleOwner';
|
import {removeRule, RuleOwner} from 'app/client/models/RuleOwner';
|
||||||
|
import {CursorPos, UIRowId} from 'app/plugin/GristAPI';
|
||||||
import {Computed, Holder, Observable} from 'grainjs';
|
import {Computed, Holder, Observable} from 'grainjs';
|
||||||
import * as ko from 'knockout';
|
import * as ko from 'knockout';
|
||||||
import defaults = require('lodash/defaults');
|
import defaults = require('lodash/defaults');
|
||||||
@ -192,10 +191,14 @@ export interface ViewSectionRec extends IRowModel<"_grist_Views_section">, RuleO
|
|||||||
desiredAccessLevel: ko.Observable<AccessLevel|null>;
|
desiredAccessLevel: ko.Observable<AccessLevel|null>;
|
||||||
|
|
||||||
// Show widget as linking source. Used by custom widget.
|
// Show widget as linking source. Used by custom widget.
|
||||||
allowSelectBy: Observable<boolean>;
|
allowSelectBy: ko.Observable<boolean>;
|
||||||
|
|
||||||
// List of selected rows
|
// List of selected rows from a custom widget, or null if a filter shouldn't be applied.
|
||||||
selectedRows: Observable<number[]>;
|
selectedRows: ko.Observable<number[]|null>;
|
||||||
|
|
||||||
|
// If the row filter is active (i.e. if selectedRows is non-null). Separate computed to avoid
|
||||||
|
// re-computing the filter when selectedRows changes.
|
||||||
|
selectedRowsActive: ko.Computed<boolean>;
|
||||||
|
|
||||||
editingFormula: ko.Computed<boolean>;
|
editingFormula: ko.Computed<boolean>;
|
||||||
|
|
||||||
@ -714,8 +717,9 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel):
|
|||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.allowSelectBy = Observable.create(this, false);
|
this.allowSelectBy = ko.observable(false);
|
||||||
this.selectedRows = Observable.create(this, []);
|
this.selectedRows = ko.observable(null as number[]|null);
|
||||||
|
this.selectedRowsActive = this.autoDispose(ko.pureComputed(() => this.selectedRows() !== null));
|
||||||
|
|
||||||
this.tableId = this.autoDispose(ko.pureComputed(() => this.table().tableId()));
|
this.tableId = this.autoDispose(ko.pureComputed(() => this.table().tableId()));
|
||||||
const rawSection = this.autoDispose(ko.pureComputed(() => this.table().rawViewSection()));
|
const rawSection = this.autoDispose(ko.pureComputed(() => this.table().rawViewSection()));
|
||||||
|
@ -24,8 +24,9 @@
|
|||||||
import koArray, {KoArray} from 'app/client/lib/koArray';
|
import koArray, {KoArray} from 'app/client/lib/koArray';
|
||||||
import {DisposableWithEvents} from 'app/common/DisposableWithEvents';
|
import {DisposableWithEvents} from 'app/common/DisposableWithEvents';
|
||||||
import {CompareFunc, sortedIndex} from 'app/common/gutil';
|
import {CompareFunc, sortedIndex} from 'app/common/gutil';
|
||||||
import {SkippableRows, UIRowId} from 'app/common/TableData';
|
import {SkippableRows} from 'app/common/TableData';
|
||||||
import {RowFilterFunc} from "app/common/RowFilterFunc";
|
import {RowFilterFunc} from "app/common/RowFilterFunc";
|
||||||
|
import {UIRowId} from 'app/plugin/GristAPI';
|
||||||
import {Observable} from 'grainjs';
|
import {Observable} from 'grainjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
import * as commands from 'app/client/components/commands';
|
import * as commands from 'app/client/components/commands';
|
||||||
import {GristDoc} from 'app/client/components/GristDoc';
|
import {GristDoc} from 'app/client/components/GristDoc';
|
||||||
|
import {FocusLayer} from 'app/client/lib/FocusLayer';
|
||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
import {ColumnFilter, NEW_FILTER_JSON} from 'app/client/models/ColumnFilter';
|
import {ColumnFilter, NEW_FILTER_JSON} from 'app/client/models/ColumnFilter';
|
||||||
import {ColumnFilterMenuModel, IFilterCount} from 'app/client/models/ColumnFilterMenuModel';
|
import {ColumnFilterMenuModel, IFilterCount} from 'app/client/models/ColumnFilterMenuModel';
|
||||||
@ -13,21 +14,30 @@ import {FilterInfo} from 'app/client/models/entities/ViewSectionRec';
|
|||||||
import {RowSource} from 'app/client/models/rowset';
|
import {RowSource} from 'app/client/models/rowset';
|
||||||
import {ColumnFilterFunc, SectionFilter} from 'app/client/models/SectionFilter';
|
import {ColumnFilterFunc, SectionFilter} from 'app/client/models/SectionFilter';
|
||||||
import {TableData} from 'app/client/models/TableData';
|
import {TableData} from 'app/client/models/TableData';
|
||||||
|
import {ColumnFilterCalendarView} from 'app/client/ui/ColumnFilterCalendarView';
|
||||||
|
import {relativeDatesControl} from 'app/client/ui/ColumnFilterMenuUtils';
|
||||||
import {cssInput} from 'app/client/ui/cssInput';
|
import {cssInput} from 'app/client/ui/cssInput';
|
||||||
|
import {DateRangeOptions, IDateRangeOption} from 'app/client/ui/DateRangeOptions';
|
||||||
import {cssPinButton} from 'app/client/ui/RightPanelStyles';
|
import {cssPinButton} from 'app/client/ui/RightPanelStyles';
|
||||||
import {basicButton, primaryButton, textButton} from 'app/client/ui2018/buttons';
|
import {basicButton, primaryButton, textButton} from 'app/client/ui2018/buttons';
|
||||||
import {cssLabel as cssCheckboxLabel, cssCheckboxSquare, cssLabelText, Indeterminate, labeledTriStateSquareCheckbox
|
import {cssLabel as cssCheckboxLabel, cssCheckboxSquare,
|
||||||
} from 'app/client/ui2018/checkbox';
|
cssLabelText, Indeterminate, labeledTriStateSquareCheckbox} from 'app/client/ui2018/checkbox';
|
||||||
import {theme, vars} from 'app/client/ui2018/cssVars';
|
import {theme, vars} from 'app/client/ui2018/cssVars';
|
||||||
import {icon} from 'app/client/ui2018/icons';
|
import {icon} from 'app/client/ui2018/icons';
|
||||||
import {cssOptionRowIcon, menu, menuCssClass, menuDivider, menuItem} from 'app/client/ui2018/menus';
|
import {cssOptionRowIcon, menu, menuCssClass, menuDivider, menuItem} from 'app/client/ui2018/menus';
|
||||||
|
import {cssDeleteButton, cssDeleteIcon, cssToken as cssTokenTokenBase} from 'app/client/widgets/ChoiceListEditor';
|
||||||
|
import {ChoiceOptions} from 'app/client/widgets/ChoiceTextBox';
|
||||||
|
import {choiceToken} from 'app/client/widgets/ChoiceToken';
|
||||||
import {CellValue} from 'app/common/DocActions';
|
import {CellValue} from 'app/common/DocActions';
|
||||||
import {IRelativeDateSpec, isEquivalentFilter, isRelativeBound} from "app/common/FilterState";
|
import {IRelativeDateSpec, isEquivalentFilter, isRelativeBound} from 'app/common/FilterState';
|
||||||
import {formatRelBounds} from "app/common/RelativeDates";
|
import {extractTypeFromColType, isDateLikeType, isList, isNumberType, isRefListType} from 'app/common/gristTypes';
|
||||||
import {
|
import {formatRelBounds} from 'app/common/RelativeDates';
|
||||||
Computed, dom, DomArg, DomElementArg, DomElementMethod, IDisposableOwner, input, makeTestId,
|
import {createFormatter} from 'app/common/ValueFormatter';
|
||||||
Observable, styled
|
import {UIRowId} from 'app/plugin/GristAPI';
|
||||||
} from 'grainjs';
|
import {decodeObject} from 'app/plugin/objtypes';
|
||||||
|
import {Computed, dom, DomArg, DomElementArg, DomElementMethod, IDisposableOwner,
|
||||||
|
input, makeTestId, Observable, styled} from 'grainjs';
|
||||||
|
import {IOpenController, IPopupOptions, setPopupToCreateDom} from 'popweasel';
|
||||||
import concat = require('lodash/concat');
|
import concat = require('lodash/concat');
|
||||||
import identity = require('lodash/identity');
|
import identity = require('lodash/identity');
|
||||||
import noop = require('lodash/noop');
|
import noop = require('lodash/noop');
|
||||||
@ -35,18 +45,6 @@ import partition = require('lodash/partition');
|
|||||||
import some = require('lodash/some');
|
import some = require('lodash/some');
|
||||||
import tail = require('lodash/tail');
|
import tail = require('lodash/tail');
|
||||||
import debounce = require('lodash/debounce');
|
import debounce = require('lodash/debounce');
|
||||||
import {IOpenController, IPopupOptions, setPopupToCreateDom} from 'popweasel';
|
|
||||||
import {decodeObject} from 'app/plugin/objtypes';
|
|
||||||
import {extractTypeFromColType, isDateLikeType, isList, isNumberType, isRefListType} from 'app/common/gristTypes';
|
|
||||||
import {choiceToken} from 'app/client/widgets/ChoiceToken';
|
|
||||||
import {ChoiceOptions} from 'app/client/widgets/ChoiceTextBox';
|
|
||||||
import {ColumnFilterCalendarView} from 'app/client/ui/ColumnFilterCalendarView';
|
|
||||||
import {cssDeleteButton, cssDeleteIcon, cssToken as cssTokenTokenBase} from 'app/client/widgets/ChoiceListEditor';
|
|
||||||
import {relativeDatesControl} from 'app/client/ui/ColumnFilterMenuUtils';
|
|
||||||
import {FocusLayer} from 'app/client/lib/FocusLayer';
|
|
||||||
import {DateRangeOptions, IDateRangeOption} from 'app/client/ui/DateRangeOptions';
|
|
||||||
import {createFormatter} from 'app/common/ValueFormatter';
|
|
||||||
import {UIRowId} from 'app/common/TableData';
|
|
||||||
|
|
||||||
const t = makeT('ColumnFilterMenu');
|
const t = makeT('ColumnFilterMenu');
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import {CursorPos} from 'app/client/components/Cursor';
|
|
||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
import { KoSaveableObservable } from 'app/client/models/modelUtil';
|
import {KoSaveableObservable} from 'app/client/models/modelUtil';
|
||||||
import {autoGrow} from 'app/client/ui/forms';
|
import {autoGrow} from 'app/client/ui/forms';
|
||||||
import {textarea} from 'app/client/ui/inputs';
|
import {textarea} from 'app/client/ui/inputs';
|
||||||
import {cssLabel, cssRow} from 'app/client/ui/RightPanelStyles';
|
import {cssLabel, cssRow} from 'app/client/ui/RightPanelStyles';
|
||||||
import {testId, theme} from 'app/client/ui2018/cssVars';
|
import {testId, theme} from 'app/client/ui2018/cssVars';
|
||||||
|
import {CursorPos} from 'app/plugin/GristAPI';
|
||||||
import {dom, fromKo, MultiHolder, styled} from 'grainjs';
|
import {dom, fromKo, MultiHolder, styled} from 'grainjs';
|
||||||
|
|
||||||
const t = makeT('DescriptionConfig');
|
const t = makeT('DescriptionConfig');
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
import {CursorPos} from 'app/client/components/Cursor';
|
|
||||||
import {GristDoc} from 'app/client/components/GristDoc';
|
import {GristDoc} from 'app/client/components/GristDoc';
|
||||||
import {BEHAVIOR, ColumnRec} from 'app/client/models/entities/ColumnRec';
|
import {BEHAVIOR, ColumnRec} from 'app/client/models/entities/ColumnRec';
|
||||||
import {buildHighlightedCode, cssCodeBlock} from 'app/client/ui/CodeHighlight';
|
import {buildHighlightedCode, cssCodeBlock} from 'app/client/ui/CodeHighlight';
|
||||||
@ -16,6 +15,7 @@ import {selectMenu, selectOption, selectTitle} from 'app/client/ui2018/menus';
|
|||||||
import {createFormulaErrorObs, cssError} from 'app/client/widgets/FormulaEditor';
|
import {createFormulaErrorObs, cssError} from 'app/client/widgets/FormulaEditor';
|
||||||
import {sanitizeIdent} from 'app/common/gutil';
|
import {sanitizeIdent} from 'app/common/gutil';
|
||||||
import {Theme} from 'app/common/ThemePrefs';
|
import {Theme} from 'app/common/ThemePrefs';
|
||||||
|
import {CursorPos} from 'app/plugin/GristAPI';
|
||||||
import {bundleChanges, Computed, dom, DomContents, DomElementArg, fromKo, MultiHolder,
|
import {bundleChanges, Computed, dom, DomContents, DomElementArg, fromKo, MultiHolder,
|
||||||
Observable, styled} from 'grainjs';
|
Observable, styled} from 'grainjs';
|
||||||
import * as ko from 'knockout';
|
import * as ko from 'knockout';
|
||||||
|
@ -127,7 +127,7 @@ function isValidLink(source: LinkNode, target: LinkNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// custom widget must allow select by
|
// custom widget must allow select by
|
||||||
if (!source.section.allowSelectBy.get()) {
|
if (!source.section.allowSelectBy()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as commands from 'app/client/components/commands';
|
import * as commands from 'app/client/components/commands';
|
||||||
import {Cursor, CursorPos} from 'app/client/components/Cursor';
|
import {Cursor} from 'app/client/components/Cursor';
|
||||||
import {GristDoc} from 'app/client/components/GristDoc';
|
import {GristDoc} from 'app/client/components/GristDoc';
|
||||||
import {UnsavedChange} from 'app/client/components/UnsavedChanges';
|
import {UnsavedChange} from 'app/client/components/UnsavedChanges';
|
||||||
import {makeT} from 'app/client/lib/localization';
|
import {makeT} from 'app/client/lib/localization';
|
||||||
@ -14,6 +14,7 @@ import {CellValue} from "app/common/DocActions";
|
|||||||
import * as gutil from 'app/common/gutil';
|
import * as gutil from 'app/common/gutil';
|
||||||
import {CellPosition} from "app/client/components/CellPosition";
|
import {CellPosition} from "app/client/components/CellPosition";
|
||||||
import {FloatingEditor} from 'app/client/widgets/FloatingEditor';
|
import {FloatingEditor} from 'app/client/widgets/FloatingEditor';
|
||||||
|
import {CursorPos} from 'app/plugin/GristAPI';
|
||||||
import isEqual = require('lodash/isEqual');
|
import isEqual = require('lodash/isEqual');
|
||||||
import {Disposable, dom, Emitter, Holder, MultiHolder, Observable} from 'grainjs';
|
import {Disposable, dom, Emitter, Holder, MultiHolder, Observable} from 'grainjs';
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import {ActionGroup} from 'app/common/ActionGroup';
|
import {ActionGroup} from 'app/common/ActionGroup';
|
||||||
import {BulkAddRecord, CellValue, TableDataAction, UserAction} from 'app/common/DocActions';
|
import {BulkAddRecord, CellValue, TableDataAction, UserAction} from 'app/common/DocActions';
|
||||||
import {FormulaProperties} from 'app/common/GranularAccessClause';
|
import {FormulaProperties} from 'app/common/GranularAccessClause';
|
||||||
import {UIRowId} from 'app/common/TableData';
|
|
||||||
import {FetchUrlOptions, UploadResult} from 'app/common/uploads';
|
import {FetchUrlOptions, UploadResult} from 'app/common/uploads';
|
||||||
import {DocStateComparison, PermissionData, UserAccessData} from 'app/common/UserAPI';
|
import {DocStateComparison, PermissionData, UserAccessData} from 'app/common/UserAPI';
|
||||||
import {ParseOptions} from 'app/plugin/FileParserAPI';
|
import {ParseOptions} from 'app/plugin/FileParserAPI';
|
||||||
import {AccessTokenOptions, AccessTokenResult} from 'app/plugin/GristAPI';
|
import {AccessTokenOptions, AccessTokenResult, UIRowId} from 'app/plugin/GristAPI';
|
||||||
import {IMessage} from 'grain-rpc';
|
import {IMessage} from 'grain-rpc';
|
||||||
|
|
||||||
export interface ApplyUAOptions {
|
export interface ApplyUAOptions {
|
||||||
|
@ -2,21 +2,18 @@
|
|||||||
* TableData maintains a single table's data.
|
* TableData maintains a single table's data.
|
||||||
*/
|
*/
|
||||||
import {ActionDispatcher} from 'app/common/ActionDispatcher';
|
import {ActionDispatcher} from 'app/common/ActionDispatcher';
|
||||||
import {
|
import {BulkAddRecord, BulkColValues, CellValue, ColInfo, ColInfoWithId, ColValues, DocAction,
|
||||||
BulkAddRecord, BulkColValues, CellValue, ColInfo, ColInfoWithId, ColValues, DocAction,
|
|
||||||
isSchemaAction, ReplaceTableData, RowRecord, TableDataAction} from 'app/common/DocActions';
|
isSchemaAction, ReplaceTableData, RowRecord, TableDataAction} from 'app/common/DocActions';
|
||||||
import {getDefaultForType} from 'app/common/gristTypes';
|
import {getDefaultForType} from 'app/common/gristTypes';
|
||||||
import {arrayRemove, arraySplice, getDistinctValues} from 'app/common/gutil';
|
import {arrayRemove, arraySplice, getDistinctValues} from 'app/common/gutil';
|
||||||
import {SchemaTypes} from "app/common/schema";
|
import {SchemaTypes} from 'app/common/schema';
|
||||||
|
import {UIRowId} from 'app/plugin/GristAPI';
|
||||||
import isEqual = require('lodash/isEqual');
|
import isEqual = require('lodash/isEqual');
|
||||||
import fromPairs = require('lodash/fromPairs');
|
import fromPairs = require('lodash/fromPairs');
|
||||||
|
|
||||||
export interface ColTypeMap { [colId: string]: string; }
|
export interface ColTypeMap { [colId: string]: string; }
|
||||||
|
|
||||||
// This is the row ID used in the client, but it's helpful to have available in some common code
|
|
||||||
// as well, which is why it's declared in app/common. Note that for data actions and stored data,
|
|
||||||
// 'new' is not used.
|
|
||||||
export type UIRowId = number | 'new';
|
|
||||||
|
|
||||||
type UIRowFunc<T> = (rowId: UIRowId) => T;
|
type UIRowFunc<T> = (rowId: UIRowId) => T;
|
||||||
|
|
||||||
|
@ -5,9 +5,9 @@ import {encodeQueryParams, isAffirmative} from 'app/common/gutil';
|
|||||||
import {LocalPlugin} from 'app/common/plugin';
|
import {LocalPlugin} from 'app/common/plugin';
|
||||||
import {StringUnion} from 'app/common/StringUnion';
|
import {StringUnion} from 'app/common/StringUnion';
|
||||||
import {TelemetryLevel} from 'app/common/Telemetry';
|
import {TelemetryLevel} from 'app/common/Telemetry';
|
||||||
import {UIRowId} from 'app/common/TableData';
|
|
||||||
import {getGristConfig} from 'app/common/urlUtils';
|
import {getGristConfig} from 'app/common/urlUtils';
|
||||||
import {Document} from 'app/common/UserAPI';
|
import {Document} from 'app/common/UserAPI';
|
||||||
|
import {UIRowId} from 'app/plugin/GristAPI';
|
||||||
import clone = require('lodash/clone');
|
import clone = require('lodash/clone');
|
||||||
import pickBy = require('lodash/pickBy');
|
import pickBy = require('lodash/pickBy');
|
||||||
import {ThemeAppearance, ThemeAppearanceChecker, ThemeName, ThemeNameChecker} from './ThemePrefs';
|
import {ThemeAppearance, ThemeAppearanceChecker, ThemeName, ThemeNameChecker} from './ThemePrefs';
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {TableData, UIRowId} from 'app/common/TableData';
|
import {TableData} from 'app/common/TableData';
|
||||||
|
import {UIRowId} from 'app/plugin/GristAPI';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether a table (identified by the rowId of its metadata record) should
|
* Return whether a table (identified by the rowId of its metadata record) should
|
||||||
|
@ -19,6 +19,7 @@ export const InteractionOptionsRequest = t.iface([], {
|
|||||||
"requiredAccess": t.opt("string"),
|
"requiredAccess": t.opt("string"),
|
||||||
"hasCustomOptions": t.opt("boolean"),
|
"hasCustomOptions": t.opt("boolean"),
|
||||||
"columns": t.opt("ColumnsToMap"),
|
"columns": t.opt("ColumnsToMap"),
|
||||||
|
"allowSelectBy": t.opt("boolean"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const InteractionOptions = t.iface([], {
|
export const InteractionOptions = t.iface([], {
|
||||||
|
@ -50,6 +50,10 @@ export interface InteractionOptionsRequest {
|
|||||||
* and those requested by Custom Widget.
|
* and those requested by Custom Widget.
|
||||||
*/
|
*/
|
||||||
columns?: ColumnsToMap,
|
columns?: ColumnsToMap,
|
||||||
|
/**
|
||||||
|
* Show widget as linking source.
|
||||||
|
*/
|
||||||
|
allowSelectBy?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,6 +4,15 @@
|
|||||||
import * as t from "ts-interface-checker";
|
import * as t from "ts-interface-checker";
|
||||||
// tslint:disable:object-literal-key-quotes
|
// tslint:disable:object-literal-key-quotes
|
||||||
|
|
||||||
|
export const UIRowId = t.union("number", t.lit('new'));
|
||||||
|
|
||||||
|
export const CursorPos = t.iface([], {
|
||||||
|
"rowId": t.opt("UIRowId"),
|
||||||
|
"rowIndex": t.opt("number"),
|
||||||
|
"fieldIndex": t.opt("number"),
|
||||||
|
"sectionId": t.opt("number"),
|
||||||
|
});
|
||||||
|
|
||||||
export const ComponentKind = t.union(t.lit("safeBrowser"), t.lit("safePython"), t.lit("unsafeNode"));
|
export const ComponentKind = t.union(t.lit("safeBrowser"), t.lit("safePython"), t.lit("unsafeNode"));
|
||||||
|
|
||||||
export const GristAPI = t.iface([], {
|
export const GristAPI = t.iface([], {
|
||||||
@ -25,7 +34,8 @@ export const GristView = t.iface([], {
|
|||||||
"fetchSelectedTable": t.func("any"),
|
"fetchSelectedTable": t.func("any"),
|
||||||
"fetchSelectedRecord": t.func("any", t.param("rowId", "number")),
|
"fetchSelectedRecord": t.func("any", t.param("rowId", "number")),
|
||||||
"allowSelectBy": t.func("void"),
|
"allowSelectBy": t.func("void"),
|
||||||
"setSelectedRows": t.func("void", t.param("rowIds", t.array("number"))),
|
"setSelectedRows": t.func("void", t.param("rowIds", t.union(t.array("number"), "null"))),
|
||||||
|
"setCursorPos": t.func("void", t.param("pos", "CursorPos")),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AccessTokenOptions = t.iface([], {
|
export const AccessTokenOptions = t.iface([], {
|
||||||
@ -39,6 +49,8 @@ export const AccessTokenResult = t.iface([], {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const exportedTypeSuite: t.ITypeSuite = {
|
const exportedTypeSuite: t.ITypeSuite = {
|
||||||
|
UIRowId,
|
||||||
|
CursorPos,
|
||||||
ComponentKind,
|
ComponentKind,
|
||||||
GristAPI,
|
GristAPI,
|
||||||
GristDocAPI,
|
GristDocAPI,
|
||||||
|
@ -37,6 +37,36 @@
|
|||||||
|
|
||||||
import {RenderOptions, RenderTarget} from './RenderOptions';
|
import {RenderOptions, RenderTarget} from './RenderOptions';
|
||||||
|
|
||||||
|
// This is the row ID used in the client, but it's helpful to have available in some common code
|
||||||
|
// as well, which is why it's declared here. Note that for data actions and stored data,
|
||||||
|
// 'new' is not used.
|
||||||
|
/**
|
||||||
|
* Represents the id of a row in a table. The value of the `id` column. Might be a number or 'new' value for a new row.
|
||||||
|
*/
|
||||||
|
export type UIRowId = number | 'new';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the position of an active cursor on a page.
|
||||||
|
*/
|
||||||
|
export interface CursorPos {
|
||||||
|
/**
|
||||||
|
* The rowId (value of the `id` column) of the current cursor position, or 'new' if the cursor is on a new row.
|
||||||
|
*/
|
||||||
|
rowId?: UIRowId;
|
||||||
|
/**
|
||||||
|
* The index of the current row in the current view.
|
||||||
|
*/
|
||||||
|
rowIndex?: number;
|
||||||
|
/**
|
||||||
|
* The index of the selected field in the current view.
|
||||||
|
*/
|
||||||
|
fieldIndex?: number;
|
||||||
|
/**
|
||||||
|
* The id of a section that this cursor is in. Ignored when setting a cursor position for a particular view.
|
||||||
|
*/
|
||||||
|
sectionId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export type ComponentKind = "safeBrowser" | "safePython" | "unsafeNode";
|
export type ComponentKind = "safeBrowser" | "safePython" | "unsafeNode";
|
||||||
|
|
||||||
export const RPC_GRISTAPI_INTERFACE = '_grist_api';
|
export const RPC_GRISTAPI_INTERFACE = '_grist_api';
|
||||||
@ -123,14 +153,20 @@ export interface GristView {
|
|||||||
// because ts-interface-builder does not properly support index-signature.
|
// because ts-interface-builder does not properly support index-signature.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow custom widget to be listed as a possible source for linking with SELECT BY.
|
* Deprecated now. It was used for filtering selected table by `setSelectedRows` method.
|
||||||
|
* Now the preferred way it to use ready message.
|
||||||
*/
|
*/
|
||||||
allowSelectBy(): Promise<void>;
|
allowSelectBy(): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the list of selected rows to be used against any linked widget. Requires `allowSelectBy()`.
|
* Set the list of selected rows to be used against any linked widget.
|
||||||
*/
|
*/
|
||||||
setSelectedRows(rowIds: number[]): Promise<void>;
|
setSelectedRows(rowIds: number[]|null): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the cursor position to a specific row and field. `sectionId` is ignored. Used for widget linking.
|
||||||
|
*/
|
||||||
|
setCursorPos(pos: CursorPos): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,6 +75,10 @@ export const allowSelectBy = viewApi.allowSelectBy;
|
|||||||
export const setSelectedRows = viewApi.setSelectedRows;
|
export const setSelectedRows = viewApi.setSelectedRows;
|
||||||
|
|
||||||
|
|
||||||
|
export const setCursorPos = viewApi.setCursorPos;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches data backing the widget as for [[GristView.fetchSelectedTable]],
|
* Fetches data backing the widget as for [[GristView.fetchSelectedTable]],
|
||||||
* but decoding data by default, replacing e.g. ['D', timestamp] with
|
* but decoding data by default, replacing e.g. ['D', timestamp] with
|
||||||
|
@ -72,7 +72,7 @@ import {InactivityTimer} from 'app/common/InactivityTimer';
|
|||||||
import {Interval} from 'app/common/Interval';
|
import {Interval} from 'app/common/Interval';
|
||||||
import * as roles from 'app/common/roles';
|
import * as roles from 'app/common/roles';
|
||||||
import {schema, SCHEMA_VERSION} from 'app/common/schema';
|
import {schema, SCHEMA_VERSION} from 'app/common/schema';
|
||||||
import {MetaRowRecord, SingleCell, UIRowId} from 'app/common/TableData';
|
import {MetaRowRecord, SingleCell} from 'app/common/TableData';
|
||||||
import {TelemetryEvent, TelemetryMetadataByLevel} from 'app/common/Telemetry';
|
import {TelemetryEvent, TelemetryMetadataByLevel} from 'app/common/Telemetry';
|
||||||
import {FetchUrlOptions, UploadResult} from 'app/common/uploads';
|
import {FetchUrlOptions, UploadResult} from 'app/common/uploads';
|
||||||
import {Document as APIDocument, DocReplacementOptions, DocState, DocStateComparison} from 'app/common/UserAPI';
|
import {Document as APIDocument, DocReplacementOptions, DocState, DocStateComparison} from 'app/common/UserAPI';
|
||||||
@ -81,7 +81,7 @@ import {guessColInfo} from 'app/common/ValueGuesser';
|
|||||||
import {parseUserAction} from 'app/common/ValueParser';
|
import {parseUserAction} from 'app/common/ValueParser';
|
||||||
import {Document} from 'app/gen-server/entity/Document';
|
import {Document} from 'app/gen-server/entity/Document';
|
||||||
import {ParseFileResult, ParseOptions} from 'app/plugin/FileParserAPI';
|
import {ParseFileResult, ParseOptions} from 'app/plugin/FileParserAPI';
|
||||||
import {AccessTokenOptions, AccessTokenResult, GristDocAPI} from 'app/plugin/GristAPI';
|
import {AccessTokenOptions, AccessTokenResult, GristDocAPI, UIRowId} from 'app/plugin/GristAPI';
|
||||||
import {compileAclFormula} from 'app/server/lib/ACLFormula';
|
import {compileAclFormula} from 'app/server/lib/ACLFormula';
|
||||||
import {AssistanceSchemaPromptV1Context} from 'app/server/lib/Assistance';
|
import {AssistanceSchemaPromptV1Context} from 'app/server/lib/Assistance';
|
||||||
import {AssistanceContext} from 'app/common/AssistancePrompts';
|
import {AssistanceContext} from 'app/common/AssistancePrompts';
|
||||||
|
1
test/fixtures/sites/hello/index.html
vendored
1
test/fixtures/sites/hello/index.html
vendored
@ -1,5 +1,6 @@
|
|||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
|
<script src="/grist-plugin-api.js"></script>
|
||||||
<h1 id="hello-title">Hello World</h1>
|
<h1 id="hello-title">Hello World</h1>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1281,10 +1281,10 @@ export async function checkForErrors() {
|
|||||||
/**
|
/**
|
||||||
* Opens a Creator Panel on Widget/Table settings tab.
|
* Opens a Creator Panel on Widget/Table settings tab.
|
||||||
*/
|
*/
|
||||||
export async function openWidgetPanel() {
|
export async function openWidgetPanel(tab: 'widget'|'sortAndFilter'|'data' = 'widget') {
|
||||||
await toggleSidePanel('right', 'open');
|
await toggleSidePanel('right', 'open');
|
||||||
await driver.find('.test-right-tab-pagewidget').click();
|
await driver.find('.test-right-tab-pagewidget').click();
|
||||||
await driver.find(".test-config-widget").click();
|
await driver.find(`.test-config-${tab}`).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user