mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(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
This commit is contained in:
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…
Reference in New Issue
Block a user