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 {isList, isRefListType} from "app/common/gristTypes";
|
||||||
import * as gutil from "app/common/gutil";
|
import * as gutil from "app/common/gutil";
|
||||||
import {encodeObject} from 'app/plugin/objtypes';
|
import {encodeObject} from 'app/plugin/objtypes';
|
||||||
import {Disposable} from "grainjs";
|
import {Disposable, toKo} from "grainjs";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import mapValues = require('lodash/mapValues');
|
import mapValues = require('lodash/mapValues');
|
||||||
import pickBy = require('lodash/pickBy');
|
import pickBy = require('lodash/pickBy');
|
||||||
@ -87,7 +87,9 @@ export class LinkingState extends Disposable {
|
|||||||
|
|
||||||
if (tgtColId) {
|
if (tgtColId) {
|
||||||
const operation = isRefListType(tgtCol.type()) ? 'intersects' : 'in';
|
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);
|
this.filterColValues = this._srcCellFilter(tgtColId, operation);
|
||||||
} else {
|
} else {
|
||||||
this.filterColValues = this._simpleFilter(tgtColId, operation, (rowId => [rowId]));
|
this.filterColValues = this._simpleFilter(tgtColId, operation, (rowId => [rowId]));
|
||||||
@ -122,6 +124,8 @@ export class LinkingState extends Disposable {
|
|||||||
} else if (isSummaryOf(tgtSection.table(), srcSection.table())) {
|
} 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
|
// 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.
|
// column or map representing the inverse of summary table's "group" column.
|
||||||
|
} else if (srcSection.parentKey() === 'custom') {
|
||||||
|
this.filterColValues = this._srcCustomFilter('id', 'in');
|
||||||
} else {
|
} else {
|
||||||
const srcValueFunc = srcColId ? this._makeSrcCellGetter() : identity;
|
const srcValueFunc = srcColId ? this._makeSrcCellGetter() : identity;
|
||||||
if (srcValueFunc) {
|
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
|
// Returns a function which returns the value of the cell
|
||||||
// in srcCol in the selected record of srcSection.
|
// in srcCol in the selected record of srcSection.
|
||||||
// Uses a row model to create a dependency on the cell's value,
|
// Uses a row model to create a dependency on the cell's value,
|
||||||
|
@ -360,6 +360,14 @@ export class GristViewImpl implements GristView {
|
|||||||
return data;
|
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() {
|
private _visibleColumns() {
|
||||||
const columns: ColumnRec[] = this._baseView.viewSection.columns.peek();
|
const columns: ColumnRec[] = this._baseView.viewSection.columns.peek();
|
||||||
const hiddenCols = this._baseView.viewSection.hiddenColumns.peek().map(c => c.id.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 {Sort} from 'app/common/SortSpec';
|
||||||
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 {Computed} from 'grainjs';
|
import {Computed, Observable} from 'grainjs';
|
||||||
import * as ko from 'knockout';
|
import * as ko from 'knockout';
|
||||||
import defaults = require('lodash/defaults');
|
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).
|
// Temporary variable holding widget desired access (changed either from manifest or via API).
|
||||||
desiredAccessLevel: ko.Observable<AccessLevel|null>;
|
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.
|
// Save all filters of fields/columns in the section.
|
||||||
saveFilters(): Promise<void>;
|
saveFilters(): Promise<void>;
|
||||||
|
|
||||||
@ -562,4 +568,7 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel):
|
|||||||
}
|
}
|
||||||
return result;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cannot select from chart or custom
|
// cannot select from chart
|
||||||
if (['chart', 'custom'].includes(source.widgetType)) {
|
if (source.widgetType === 'chart') {
|
||||||
return false;
|
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
|
// The link must not create a cycle
|
||||||
if (source.ancestors.has(target.section.getRowId())) {
|
if (source.ancestors.has(target.section.getRowId())) {
|
||||||
return false;
|
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 ComponentKind = t.union(t.lit("safeBrowser"), t.lit("safePython"), t.lit("unsafeNode"));
|
||||||
|
|
||||||
export const GristAPI = t.iface([], {
|
export const GristAPI = t.iface([], {
|
||||||
"render": t.func("number", t.param("path", "string"), t.param("target", "RenderTarget"),
|
"render": t.func("number", t.param("path", "string"), t.param("target", "RenderTarget"), t.param("options", "RenderOptions", true)),
|
||||||
t.param("options", "RenderOptions", true)),
|
|
||||||
"dispose": t.func("void", t.param("procId", "number")),
|
"dispose": t.func("void", t.param("procId", "number")),
|
||||||
"subscribe": t.func("void", t.param("tableId", "string")),
|
"subscribe": t.func("void", t.param("tableId", "string")),
|
||||||
"unsubscribe": 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([], {
|
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"),
|
||||||
|
"setSelectedRows": t.func("void", t.param("rowIds", t.array("number"))),
|
||||||
});
|
});
|
||||||
|
|
||||||
const exportedTypeSuite: t.ITypeSuite = {
|
const exportedTypeSuite: t.ITypeSuite = {
|
||||||
|
@ -99,4 +99,10 @@ export interface GristView {
|
|||||||
|
|
||||||
// Similar TODO to fetchSelectedTable for return type.
|
// Similar TODO to fetchSelectedTable for return type.
|
||||||
fetchSelectedRecord(rowId: number): Promise<any>;
|
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 viewApi = rpc.getStub<GristView>('GristView', checkers.GristView);
|
||||||
export const widgetApi = rpc.getStub<WidgetAPI>('WidgetAPI', checkers.WidgetAPI);
|
export const widgetApi = rpc.getStub<WidgetAPI>('WidgetAPI', checkers.WidgetAPI);
|
||||||
export const sectionApi = rpc.getStub<CustomSectionAPI>('CustomSectionAPI', checkers.CustomSectionAPI);
|
export const sectionApi = rpc.getStub<CustomSectionAPI>('CustomSectionAPI', checkers.CustomSectionAPI);
|
||||||
|
export const allowSelectBy = viewApi.allowSelectBy;
|
||||||
|
export const setSelectedRows = viewApi.setSelectedRows;
|
||||||
|
|
||||||
export const docApi: GristDocAPI & GristView = {
|
export const docApi: GristDocAPI & GristView = {
|
||||||
...coreDocApi,
|
...coreDocApi,
|
||||||
|
Loading…
Reference in New Issue
Block a user