(core) Adds setSelectedRows to the grist api for custom view

Summary:
This is needed to let custom widget driver filtering of other widget in the same page.

Descripion here:
 - https://grist.quip.com/ctytAQJoFMsM/Hopefully-Small-Projects#temp:C:NNCfe2030b27647439886ca83595

Test Plan: New api tested in a new nbrowser test

Reviewers: paulfitz

Reviewed By: paulfitz

Differential Revision: https://phab.getgrist.com/D3253
pull/132/head
Cyprien P 2 years ago
parent e264094412
commit 2f6eafff35

@ -10,7 +10,7 @@ import {ClientQuery, QueryOperation} from "app/common/ActiveDocAPI";
import {isList, isRefListType} from "app/common/gristTypes";
import * as gutil from "app/common/gutil";
import {encodeObject} from 'app/plugin/objtypes';
import {Disposable} from "grainjs";
import {Disposable, toKo} from "grainjs";
import * as ko from "knockout";
import mapValues = require('lodash/mapValues');
import pickBy = require('lodash/pickBy');
@ -87,7 +87,9 @@ export class LinkingState extends Disposable {
if (tgtColId) {
const operation = isRefListType(tgtCol.type()) ? 'intersects' : 'in';
if (srcColId) {
if (srcSection.parentKey() === 'custom') {
this.filterColValues = this._srcCustomFilter(tgtColId, operation);
} else if (srcColId) {
this.filterColValues = this._srcCellFilter(tgtColId, operation);
} else {
this.filterColValues = this._simpleFilter(tgtColId, operation, (rowId => [rowId]));
@ -122,6 +124,8 @@ export class LinkingState extends Disposable {
} else if (isSummaryOf(tgtSection.table(), srcSection.table())) {
// TODO: We should move the cursor, but don't currently it for summaries. For that, we need a
// column or map representing the inverse of summary table's "group" column.
} else if (srcSection.parentKey() === 'custom') {
this.filterColValues = this._srcCustomFilter('id', 'in');
} else {
const srcValueFunc = srcColId ? this._makeSrcCellGetter() : identity;
if (srcValueFunc) {
@ -196,6 +200,14 @@ 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)();
return {filters: {[colId]: values}, operations: {[colId]: operation}} as FilterColValues;
}));
}
// Returns a function which returns the value of the cell
// in srcCol in the selected record of srcSection.
// Uses a row model to create a dependency on the cell's value,

@ -360,6 +360,14 @@ export class GristViewImpl implements GristView {
return data;
}
public async allowSelectBy(): Promise<void> {
this._baseView.viewSection.allowSelectBy.set(true);
}
public async setSelectedRows(rowIds: number[]): Promise<void> {
this._baseView.viewSection.selectedRows.set(rowIds);
}
private _visibleColumns() {
const columns: ColumnRec[] = this._baseView.viewSection.columns.peek();
const hiddenCols = this._baseView.viewSection.hiddenColumns.peek().map(c => c.id.peek());

@ -22,7 +22,7 @@ import {arrayRepeat} from 'app/common/gutil';
import {Sort} from 'app/common/SortSpec';
import {ColumnsToMap, WidgetColumnMap} from 'app/plugin/CustomSectionAPI';
import {ColumnToMapImpl} from 'app/client/models/ColumnToMap';
import {Computed} from 'grainjs';
import {Computed, Observable} from 'grainjs';
import * as ko from 'knockout';
import defaults = require('lodash/defaults');
@ -159,6 +159,12 @@ export interface ViewSectionRec extends IRowModel<"_grist_Views_section"> {
// Temporary variable holding widget desired access (changed either from manifest or via API).
desiredAccessLevel: ko.Observable<AccessLevel|null>;
// Show widget as linking source. Used by custom widget.
allowSelectBy: Observable<boolean>;
// List of selected rows
selectedRows: Observable<number[]>;
// Save all filters of fields/columns in the section.
saveFilters(): Promise<void>;
@ -562,4 +568,7 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel):
}
return result;
});
this.allowSelectBy = Observable.create(this, false);
this.selectedRows = Observable.create(this, []);
}

@ -74,11 +74,24 @@ function isValidLink(source: LinkNode, target: LinkNode) {
return false;
}
// cannot select from chart or custom
if (['chart', 'custom'].includes(source.widgetType)) {
// cannot select from chart
if (source.widgetType === 'chart') {
return false;
}
if (source.widgetType === 'custom') {
// custom widget do not support linking by columns
if (source.tableId !== source.section.table.peek().primaryTableId.peek()) {
return false;
}
// custom widget must allow select by
if (!source.section.allowSelectBy.get()) {
return false;
}
}
// The link must not create a cycle
if (source.ancestors.has(target.section.getRowId())) {
return false;

@ -7,8 +7,7 @@ import * as t from "ts-interface-checker";
export const ComponentKind = t.union(t.lit("safeBrowser"), t.lit("safePython"), t.lit("unsafeNode"));
export const GristAPI = t.iface([], {
"render": t.func("number", t.param("path", "string"), t.param("target", "RenderTarget"),
t.param("options", "RenderOptions", true)),
"render": t.func("number", t.param("path", "string"), t.param("target", "RenderTarget"), t.param("options", "RenderOptions", true)),
"dispose": t.func("void", t.param("procId", "number")),
"subscribe": t.func("void", t.param("tableId", "string")),
"unsubscribe": t.func("void", t.param("tableId", "string")),
@ -24,6 +23,8 @@ export const GristDocAPI = t.iface([], {
export const GristView = t.iface([], {
"fetchSelectedTable": t.func("any"),
"fetchSelectedRecord": t.func("any", t.param("rowId", "number")),
"allowSelectBy": t.func("void"),
"setSelectedRows": t.func("void", t.param("rowIds", t.array("number"))),
});
const exportedTypeSuite: t.ITypeSuite = {

@ -99,4 +99,10 @@ export interface GristView {
// Similar TODO to fetchSelectedTable for return type.
fetchSelectedRecord(rowId: number): Promise<any>;
// Allow custom widget to be listed as a possible source for linking with SELECT BY.
allowSelectBy(): Promise<void>;
// Set the list of selected rows to be used against any linked widget. Requires `allowSelectBy()`.
setSelectedRows(rowIds: number[]): Promise<void>;
}

@ -47,6 +47,8 @@ export const coreDocApi = rpc.getStub<GristDocAPI>('GristDocAPI@grist', checkers
export const viewApi = rpc.getStub<GristView>('GristView', checkers.GristView);
export const widgetApi = rpc.getStub<WidgetAPI>('WidgetAPI', checkers.WidgetAPI);
export const sectionApi = rpc.getStub<CustomSectionAPI>('CustomSectionAPI', checkers.CustomSectionAPI);
export const allowSelectBy = viewApi.allowSelectBy;
export const setSelectedRows = viewApi.setSelectedRows;
export const docApi: GristDocAPI & GristView = {
...coreDocApi,

Loading…
Cancel
Save