mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +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:
@@ -1,5 +1,5 @@
|
||||
import { CursorPos } from "app/client/components/Cursor";
|
||||
import { DocModel, ViewFieldRec } from "app/client/models/DocModel";
|
||||
import { CursorPos } from 'app/plugin/GristAPI';
|
||||
import BaseRowModel = require("app/client/models/BaseRowModel");
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
|
||||
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.
|
||||
|
||||
@@ -8,17 +8,10 @@ import BaseView from 'app/client/components/BaseView';
|
||||
import * as commands from 'app/client/components/commands';
|
||||
import BaseRowModel from 'app/client/models/BaseRowModel';
|
||||
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 * as ko from 'knockout';
|
||||
|
||||
export interface CursorPos {
|
||||
rowId?: UIRowId;
|
||||
rowIndex?: number;
|
||||
fieldIndex?: number;
|
||||
sectionId?: number;
|
||||
}
|
||||
|
||||
function nullAsUndefined<T>(value: T|null|undefined): T|undefined {
|
||||
return value == null ? undefined : value;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {CursorPos} from 'app/client/components/Cursor';
|
||||
import {GristDoc} from 'app/client/components/GristDoc';
|
||||
import {getStorage} from 'app/client/lib/storage';
|
||||
import {IDocPage, isViewDocPage, ViewDocPage} from 'app/common/gristUrls';
|
||||
import {Disposable, Listener, Observable} from 'grainjs';
|
||||
import {reportError} from 'app/client/models/errors';
|
||||
import {CursorPos} from 'app/plugin/GristAPI';
|
||||
|
||||
/**
|
||||
* 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 {CodeEditorPanel} from 'app/client/components/CodeEditorPanel';
|
||||
import * as commands from 'app/client/components/commands';
|
||||
import {CursorPos} from 'app/client/components/Cursor';
|
||||
import {CursorMonitor, ViewCursorPos} from "app/client/components/CursorMonitor";
|
||||
import {DocComm} from 'app/client/components/DocComm';
|
||||
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 {TableData} from 'app/common/TableData';
|
||||
import {DocStateComparison} from 'app/common/UserAPI';
|
||||
import {CursorPos} from 'app/plugin/GristAPI';
|
||||
import {
|
||||
bundleChanges,
|
||||
Computed,
|
||||
@@ -1256,7 +1256,7 @@ export class GristDoc extends DisposableWithEvents {
|
||||
const fieldIndex = activeSection.viewFields.peek().all().findIndex(f => f.colRef.peek() === hash.colRef);
|
||||
if (fieldIndex >= 0) {
|
||||
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);
|
||||
@@ -1306,7 +1306,7 @@ export class GristDoc extends DisposableWithEvents {
|
||||
const fieldIndex = popupSection.viewFields.peek().all().findIndex(f => f.colRef.peek() === hash.colRef);
|
||||
if (fieldIndex >= 0) {
|
||||
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 {TableRec} from "app/client/models/entities/TableRec";
|
||||
import {ViewSectionRec} from "app/client/models/entities/ViewSectionRec";
|
||||
import {UIRowId} from "app/common/TableData";
|
||||
import {LinkConfig} from "app/client/ui/selectBy";
|
||||
import {FilterColValues, QueryOperation} from "app/common/ActiveDocAPI";
|
||||
import {isList, isListType, isRefListType} from "app/common/gristTypes";
|
||||
import * as gutil from "app/common/gutil";
|
||||
import {UIRowId} from 'app/plugin/GristAPI';
|
||||
import {encodeObject} from 'app/plugin/objtypes';
|
||||
import {Disposable, toKo} from "grainjs";
|
||||
import {Disposable} from "grainjs";
|
||||
import * as ko from "knockout";
|
||||
import identity = require('lodash/identity');
|
||||
import mapValues = require('lodash/mapValues');
|
||||
@@ -85,7 +85,7 @@ export class LinkingState extends Disposable {
|
||||
|
||||
if (tgtColId) {
|
||||
const operation = isRefListType(tgtCol.type()) ? 'intersects' : 'in';
|
||||
if (srcSection.parentKey() === 'custom') {
|
||||
if (srcSection.selectedRowsActive()) {
|
||||
this.filterColValues = this._srcCustomFilter(tgtColId, operation);
|
||||
} else if (srcColId) {
|
||||
this.filterColValues = this._srcCellFilter(tgtColId, operation);
|
||||
@@ -128,7 +128,7 @@ export class LinkingState extends Disposable {
|
||||
}
|
||||
_filterColValues(result);
|
||||
}
|
||||
} else if (srcSection.parentKey() === 'custom') {
|
||||
} else if (srcSection.selectedRowsActive()) {
|
||||
this.filterColValues = this._srcCustomFilter('id', 'in');
|
||||
} else {
|
||||
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
|
||||
private _srcCustomFilter(colId: string, operation: QueryOperation): ko.Computed<FilterColValues> | undefined {
|
||||
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;
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {CursorPos} from 'app/client/components/Cursor';
|
||||
import {GristDoc} from 'app/client/components/GristDoc';
|
||||
import * as dispose from 'app/client/lib/dispose';
|
||||
import {MinimalActionGroup} from 'app/common/ActionGroup';
|
||||
import {PromiseChain, setDefault} from 'app/common/gutil';
|
||||
import {CursorPos} from 'app/plugin/GristAPI';
|
||||
import {fromKo, Observable} from 'grainjs';
|
||||
import * as ko from 'knockout';
|
||||
import sortBy = require('lodash/sortBy');
|
||||
|
||||
@@ -7,7 +7,7 @@ import {AccessLevel, isSatisfied} from 'app/common/CustomWidget';
|
||||
import {DisposableWithEvents} from 'app/common/DisposableWithEvents';
|
||||
import {BulkColValues, fromTableDataAction, RowRecord} from 'app/common/DocActions';
|
||||
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';
|
||||
import {MsgType, Rpc} from 'grain-rpc';
|
||||
import {Computed, Disposable, dom, Observable} from 'grainjs';
|
||||
@@ -380,12 +380,25 @@ export class GristViewImpl implements GristView {
|
||||
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> {
|
||||
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> {
|
||||
this._baseView.viewSection.selectedRows.set(rowIds);
|
||||
public async setSelectedRows(rowIds: number[]|null): Promise<void> {
|
||||
this._baseView.viewSection.selectedRows(rowIds);
|
||||
}
|
||||
|
||||
public setCursorPos(cursorPos: CursorPos): Promise<void> {
|
||||
this._baseView.setCursorPos(cursorPos);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private _visibleColumns() {
|
||||
@@ -615,5 +628,8 @@ export class CustomSectionAPIImpl extends Disposable implements CustomSectionAPI
|
||||
} else {
|
||||
this._section.columnsToMap(null);
|
||||
}
|
||||
if (settings.allowSelectBy !== undefined) {
|
||||
this._section.allowSelectBy(settings.allowSelectBy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user