mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Fixing cursor position for filtered linked section.
Summary: In a selector table, when a selected row is filtered out of view, linked widgets should update based on the newly selected row. There were a few bugs that contributed to this wrong behavior: - Gridview wasn't subscribing to the current row id, and the row with id 'new' was being converted to the first row - Cursor was keeping track of the currently selected row id, it was hiding a problem behind the proper rowIndex - Undo/redo somehow leveraged the wrong rowId from the cursor during the position restore. The `No data` text was also changed to be more meaningful. Test Plan: Added and updated. Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D3937
This commit is contained in:
@@ -29,8 +29,7 @@ import {isHiddenTable, isSummaryTable} from 'app/common/isHiddenTable';
|
||||
import {canEdit} from 'app/common/roles';
|
||||
import {RowFilterFunc} from 'app/common/RowFilterFunc';
|
||||
import {schema, SchemaTypes} from 'app/common/schema';
|
||||
import {UIRowId} from 'app/common/UIRowId';
|
||||
|
||||
import {UIRowId} from 'app/common/TableData';
|
||||
import {ACLRuleRec, createACLRuleRec} from 'app/client/models/entities/ACLRuleRec';
|
||||
import {ColumnRec, createColumnRec} from 'app/client/models/entities/ColumnRec';
|
||||
import {createDocInfoRec, DocInfoRec} from 'app/client/models/entities/DocInfoRec';
|
||||
@@ -318,7 +317,7 @@ export class DocModel {
|
||||
*/
|
||||
function createTablesArray(
|
||||
tablesModel: MetaTableModel<TableRec>,
|
||||
filterFunc: RowFilterFunc<rowset.RowId> = (_row) => true
|
||||
filterFunc: RowFilterFunc<UIRowId> = (_row) => true
|
||||
) {
|
||||
const rowSource = new rowset.FilteredRowSource(filterFunc);
|
||||
rowSource.subscribeTo(tablesModel);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
*/
|
||||
import DataTableModel from 'app/client/models/DataTableModel';
|
||||
import {DocModel} from 'app/client/models/DocModel';
|
||||
import {BaseFilteredRowSource, RowId, RowList, RowSource} from 'app/client/models/rowset';
|
||||
import {BaseFilteredRowSource, RowList, RowSource} from 'app/client/models/rowset';
|
||||
import {TableData} from 'app/client/models/TableData';
|
||||
import {ActiveDocAPI, ClientQuery, QueryOperation} from 'app/common/ActiveDocAPI';
|
||||
import {CellValue, TableDataAction} from 'app/common/DocActions';
|
||||
@@ -37,7 +37,7 @@ import {isList} from "app/common/gristTypes";
|
||||
import {nativeCompare} from 'app/common/gutil';
|
||||
import {IRefCountSub, RefCountMap} from 'app/common/RefCountMap';
|
||||
import {RowFilterFunc} from 'app/common/RowFilterFunc';
|
||||
import {TableData as BaseTableData} from 'app/common/TableData';
|
||||
import {TableData as BaseTableData, UIRowId} from 'app/common/TableData';
|
||||
import {tbind} from 'app/common/tbind';
|
||||
import {decodeObject} from "app/plugin/objtypes";
|
||||
import {Disposable, Holder, IDisposableOwnerT} from 'grainjs';
|
||||
@@ -303,7 +303,7 @@ export class TableQuerySets {
|
||||
/**
|
||||
* Returns a filtering function which tells whether a row matches the given query.
|
||||
*/
|
||||
export function getFilterFunc(docData: DocData, query: ClientQuery): RowFilterFunc<RowId> {
|
||||
export function getFilterFunc(docData: DocData, query: ClientQuery): RowFilterFunc<UIRowId> {
|
||||
// NOTE we rely without checking on tableId and colIds being valid.
|
||||
const tableData: BaseTableData = docData.getTable(query.tableId)!;
|
||||
const colFuncs = Object.keys(query.filters).sort().map(
|
||||
@@ -312,22 +312,22 @@ export function getFilterFunc(docData: DocData, query: ClientQuery): RowFilterFu
|
||||
const values = new Set(query.filters[colId]);
|
||||
switch (query.operations[colId]) {
|
||||
case "intersects":
|
||||
return (rowId: RowId) => {
|
||||
return (rowId: UIRowId) => {
|
||||
const value = getter(rowId) as CellValue;
|
||||
return isList(value) &&
|
||||
(decodeObject(value) as unknown[]).some(v => values.has(v));
|
||||
};
|
||||
case "empty":
|
||||
return (rowId: RowId) => {
|
||||
return (rowId: UIRowId) => {
|
||||
const value = getter(rowId);
|
||||
// `isList(value) && value.length === 1` means `value == ['L']` i.e. an empty list
|
||||
return !value || isList(value) && value.length === 1;
|
||||
};
|
||||
case "in":
|
||||
return (rowId: RowId) => values.has(getter(rowId));
|
||||
return (rowId: UIRowId) => values.has(getter(rowId));
|
||||
}
|
||||
});
|
||||
return (rowId: RowId) => colFuncs.every(f => f(rowId));
|
||||
return (rowId: UIRowId) => colFuncs.every(f => f(rowId));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {ColumnFilter} from 'app/client/models/ColumnFilter';
|
||||
import {ColumnRec, ViewFieldRec, ViewSectionRec} from 'app/client/models/DocModel';
|
||||
import {RowId} from 'app/client/models/rowset';
|
||||
import {TableData} from 'app/client/models/TableData';
|
||||
import {buildColFilter, ColumnFilterFunc} from 'app/common/ColumnFilterFunc';
|
||||
import {buildRowFilter, RowFilterFunc, RowValueFunc } from 'app/common/RowFilterFunc';
|
||||
import {UIRowId} from 'app/common/TableData';
|
||||
import {Computed, Disposable, MutableObsArray, obsArray, Observable, UseCB} from 'grainjs';
|
||||
|
||||
export type {ColumnFilterFunc};
|
||||
@@ -26,10 +26,10 @@ type ColFilterCB = (fieldOrColumn: ViewFieldRec|ColumnRec, colFilter: ColumnFilt
|
||||
* results in their being displayed (obviating the need to maintain their rowId explicitly).
|
||||
*/
|
||||
export class SectionFilter extends Disposable {
|
||||
public readonly sectionFilterFunc: Observable<RowFilterFunc<RowId>>;
|
||||
public readonly sectionFilterFunc: Observable<RowFilterFunc<UIRowId>>;
|
||||
|
||||
private _openFilterOverride: Observable<OpenColumnFilter|null> = Observable.create(this, null);
|
||||
private _tempRows: MutableObsArray<RowId> = obsArray();
|
||||
private _tempRows: MutableObsArray<UIRowId> = obsArray();
|
||||
|
||||
constructor(public viewSection: ViewSectionRec, private _tableData: TableData) {
|
||||
super();
|
||||
@@ -89,8 +89,8 @@ export class SectionFilter extends Disposable {
|
||||
return this._addRowsToFilter(this._buildPlainFilterFunc(getFilterFunc, use), this._tempRows.get());
|
||||
}
|
||||
|
||||
private _addRowsToFilter(filterFunc: RowFilterFunc<RowId>, rows: RowId[]) {
|
||||
return (rowId: RowId) => rows.includes(rowId) || (typeof rowId !== 'number') || filterFunc(rowId);
|
||||
private _addRowsToFilter(filterFunc: RowFilterFunc<UIRowId>, rows: UIRowId[]) {
|
||||
return (rowId: UIRowId) => rows.includes(rowId) || (typeof rowId !== 'number') || filterFunc(rowId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,9 +98,9 @@ export class SectionFilter extends Disposable {
|
||||
* columns. You can use `getFilterFunc(column, colFilter)` to customize the filter func for each
|
||||
* column. It calls `getFilterFunc` right away.
|
||||
*/
|
||||
private _buildPlainFilterFunc(getFilterFunc: ColFilterCB, use: UseCB): RowFilterFunc<RowId> {
|
||||
private _buildPlainFilterFunc(getFilterFunc: ColFilterCB, use: UseCB): RowFilterFunc<UIRowId> {
|
||||
const filters = use(this.viewSection.filters);
|
||||
const funcs: Array<RowFilterFunc<RowId> | null> = filters.map(({filter, fieldOrColumn}) => {
|
||||
const funcs: Array<RowFilterFunc<UIRowId> | null> = filters.map(({filter, fieldOrColumn}) => {
|
||||
const colFilter = buildColFilter(use(filter), use(use(fieldOrColumn.origCol).type));
|
||||
const filterFunc = getFilterFunc(fieldOrColumn, colFilter);
|
||||
|
||||
@@ -108,9 +108,9 @@ export class SectionFilter extends Disposable {
|
||||
|
||||
if (!filterFunc || !getter) { return null; }
|
||||
|
||||
return buildRowFilter(getter as RowValueFunc<RowId>, filterFunc);
|
||||
return buildRowFilter(getter as RowValueFunc<UIRowId>, filterFunc);
|
||||
}).filter(f => f !== null); // Filter out columns that don't have a filter
|
||||
|
||||
return (rowId: RowId) => funcs.every(f => Boolean(f && f(rowId)));
|
||||
return (rowId: UIRowId) => funcs.every(f => Boolean(f && f(rowId)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +62,9 @@ export function createViewRec(this: ViewRec, docModel: DocModel): void {
|
||||
// Default to the first leaf from layoutSpec (which corresponds to the top-left section), or
|
||||
// fall back to the first item in the list if anything goes wrong (previous behavior).
|
||||
const firstLeaf = getFirstLeaf(this.layoutSpecObj.peek());
|
||||
return visible.find(s => s.getRowId() === firstLeaf) ? firstLeaf as number :
|
||||
(visible[0]?.getRowId() || 0);
|
||||
const result = visible.find(s => s.id() === firstLeaf) ? firstLeaf as number :
|
||||
(visible[0]?.id() || 0);
|
||||
return result;
|
||||
});
|
||||
|
||||
this.activeSection = refRecord(docModel.viewSections, this.activeSectionId);
|
||||
|
||||
@@ -15,13 +15,13 @@ import {
|
||||
ViewRec
|
||||
} from 'app/client/models/DocModel';
|
||||
import * as modelUtil from 'app/client/models/modelUtil';
|
||||
import {RowId} from 'app/client/models/rowset';
|
||||
import {LinkConfig} from 'app/client/ui/selectBy';
|
||||
import {getWidgetTypes} from 'app/client/ui/widgetTypes';
|
||||
import {AccessLevel, ICustomWidget} from 'app/common/CustomWidget';
|
||||
import {UserAction} from 'app/common/DocActions';
|
||||
import {arrayRepeat} from 'app/common/gutil';
|
||||
import {Sort} from 'app/common/SortSpec';
|
||||
import {UIRowId} from 'app/common/TableData';
|
||||
import {ColumnsToMap, WidgetColumnMap} from 'app/plugin/CustomSectionAPI';
|
||||
import {ColumnToMapImpl} from 'app/client/models/ColumnToMap';
|
||||
import {BEHAVIOR} from 'app/client/models/entities/ColumnRec';
|
||||
@@ -120,19 +120,30 @@ export interface ViewSectionRec extends IRowModel<"_grist_Views_section">, RuleO
|
||||
|
||||
hasFocus: ko.Computed<boolean>;
|
||||
|
||||
activeLinkSrcSectionRef: modelUtil.CustomComputed<number>;
|
||||
activeLinkSrcColRef: modelUtil.CustomComputed<number>;
|
||||
activeLinkTargetColRef: modelUtil.CustomComputed<number>;
|
||||
|
||||
// Whether current linking state is as saved. It may be different during editing.
|
||||
isActiveLinkSaved: ko.Computed<boolean>;
|
||||
|
||||
// Section-linking affects table if linkSrcSection is set. The controller value of the
|
||||
// link is the value of srcCol at activeRowId of linkSrcSection, or activeRowId itself when
|
||||
// srcCol is unset. If targetCol is set, we filter for all rows whose targetCol is equal to
|
||||
// the controller value. Otherwise, the controller value determines the rowId of the cursor.
|
||||
|
||||
/**
|
||||
* Section selected in the `Select By` dropdown. Used for filtering this section.
|
||||
*/
|
||||
linkSrcSection: ko.Computed<ViewSectionRec>;
|
||||
/**
|
||||
* Column selected in the `Select By` dropdown in the remote section. It points to a column in remote section
|
||||
* that contains a reference to this table (or common table - because we can be linked by having the same reference
|
||||
* to some other section).
|
||||
* Used for filtering this section. Can be empty as user can just link by section.
|
||||
* Watch out, it is not cleared, so it is only valid when we have linkSrcSection.
|
||||
* In UI it is shown as Target Section (dot) Target Column.
|
||||
*/
|
||||
linkSrcCol: ko.Computed<ColumnRec>;
|
||||
/**
|
||||
* In case we have multiple reference columns, that are shown as
|
||||
* Target Section -> My Column or
|
||||
* Target Section . Target Column -> My Column
|
||||
* store the reference to the column (my column) to use.
|
||||
*/
|
||||
linkTargetCol: ko.Computed<ColumnRec>;
|
||||
|
||||
// Linking state maintains .filterFunc and .cursorPos observables which we use for
|
||||
@@ -142,7 +153,7 @@ export interface ViewSectionRec extends IRowModel<"_grist_Views_section">, RuleO
|
||||
|
||||
linkingFilter: ko.Computed<FilterColValues>;
|
||||
|
||||
activeRowId: ko.Observable<RowId | null>; // May be null when there are no rows.
|
||||
activeRowId: ko.Observable<UIRowId | null>; // May be null when there are no rows.
|
||||
|
||||
// If the view instance for section is instantiated, it will be accessible here.
|
||||
viewInstance: ko.Observable<BaseView | null>;
|
||||
@@ -594,30 +605,20 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel):
|
||||
write: (val) => { this.view().activeSectionId(val ? this.id() : 0); }
|
||||
});
|
||||
|
||||
this.activeLinkSrcSectionRef = modelUtil.customValue(this.linkSrcSectionRef);
|
||||
this.activeLinkSrcColRef = modelUtil.customValue(this.linkSrcColRef);
|
||||
this.activeLinkTargetColRef = modelUtil.customValue(this.linkTargetColRef);
|
||||
|
||||
// Whether current linking state is as saved. It may be different during editing.
|
||||
this.isActiveLinkSaved = this.autoDispose(ko.pureComputed(() =>
|
||||
this.activeLinkSrcSectionRef.isSaved() &&
|
||||
this.activeLinkSrcColRef.isSaved() &&
|
||||
this.activeLinkTargetColRef.isSaved()));
|
||||
|
||||
// Section-linking affects this table if linkSrcSection is set. The controller value of the
|
||||
// link is the value of srcCol at activeRowId of linkSrcSection, or activeRowId itself when
|
||||
// srcCol is unset. If targetCol is set, we filter for all rows whose targetCol is equal to
|
||||
// the controller value. Otherwise, the controller value determines the rowId of the cursor.
|
||||
this.linkSrcSection = refRecord(docModel.viewSections, this.activeLinkSrcSectionRef);
|
||||
this.linkSrcCol = refRecord(docModel.columns, this.activeLinkSrcColRef);
|
||||
this.linkTargetCol = refRecord(docModel.columns, this.activeLinkTargetColRef);
|
||||
this.linkSrcSection = refRecord(docModel.viewSections, this.linkSrcSectionRef);
|
||||
this.linkSrcCol = refRecord(docModel.columns, this.linkSrcColRef);
|
||||
this.linkTargetCol = refRecord(docModel.columns, this.linkTargetColRef);
|
||||
|
||||
this.activeRowId = ko.observable<RowId|null>(null);
|
||||
this.activeRowId = ko.observable<UIRowId|null>(null);
|
||||
|
||||
this._linkingState = Holder.create(this);
|
||||
this.linkingState = this.autoDispose(ko.pureComputed(() => {
|
||||
if (!this.activeLinkSrcSectionRef()) {
|
||||
// This view section isn't selecting by anything.
|
||||
if (!this.linkSrcSectionRef()) {
|
||||
// This view section isn't selected by anything.
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
import koArray, {KoArray} from 'app/client/lib/koArray';
|
||||
import {DisposableWithEvents} from 'app/common/DisposableWithEvents';
|
||||
import {CompareFunc, sortedIndex} from 'app/common/gutil';
|
||||
import {SkippableRows} from 'app/common/TableData';
|
||||
import {SkippableRows, UIRowId} from 'app/common/TableData';
|
||||
import {RowFilterFunc} from "app/common/RowFilterFunc";
|
||||
import {Observable} from 'grainjs';
|
||||
|
||||
@@ -36,8 +36,7 @@ export const ALL: unique symbol = Symbol("ALL");
|
||||
|
||||
export type ChangeType = 'add' | 'remove' | 'update';
|
||||
export type ChangeMethod = 'onAddRows' | 'onRemoveRows' | 'onUpdateRows';
|
||||
export type RowId = number | 'new';
|
||||
export type RowList = Iterable<RowId>;
|
||||
export type RowList = Iterable<UIRowId>;
|
||||
export type RowsChanged = RowList | typeof ALL;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -132,7 +131,7 @@ export class RowListener extends DisposableWithEvents {
|
||||
* A trivial RowSource returning a fixed list of rows.
|
||||
*/
|
||||
export abstract class ArrayRowSource extends RowSource {
|
||||
constructor(private _rows: RowId[]) { super(); }
|
||||
constructor(private _rows: UIRowId[]) { super(); }
|
||||
public getAllRows(): RowList { return this._rows; }
|
||||
public getNumRows(): number { return this._rows.length; }
|
||||
}
|
||||
@@ -146,11 +145,11 @@ export abstract class ArrayRowSource extends RowSource {
|
||||
* TODO: This class is not used anywhere at the moment, and is a candidate for removal.
|
||||
*/
|
||||
export class MappedRowSource extends RowSource {
|
||||
private _mapperFunc: (row: RowId) => RowId;
|
||||
private _mapperFunc: (row: UIRowId) => UIRowId;
|
||||
|
||||
constructor(
|
||||
public parentRowSource: RowSource,
|
||||
mapperFunc: (row: RowId) => RowId,
|
||||
mapperFunc: (row: UIRowId) => UIRowId,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -182,7 +181,7 @@ export class ExtendedRowSource extends RowSource {
|
||||
|
||||
constructor(
|
||||
public parentRowSource: RowSource,
|
||||
public extras: RowId[]
|
||||
public extras: UIRowId[]
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -209,9 +208,9 @@ export class ExtendedRowSource extends RowSource {
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
interface FilterRowChanges {
|
||||
adds?: RowId[];
|
||||
updates?: RowId[];
|
||||
removes?: RowId[];
|
||||
adds?: UIRowId[];
|
||||
updates?: UIRowId[];
|
||||
removes?: UIRowId[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,9 +218,9 @@ interface FilterRowChanges {
|
||||
* does not maintain excluded rows, and does not allow changes to filterFunc.
|
||||
*/
|
||||
export class BaseFilteredRowSource extends RowListener implements RowSource {
|
||||
protected _matchingRows: Set<RowId> = new Set(); // Set of rows matching the filter.
|
||||
protected _matchingRows: Set<UIRowId> = new Set(); // Set of rows matching the filter.
|
||||
|
||||
constructor(protected _filterFunc: RowFilterFunc<RowId>) {
|
||||
constructor(protected _filterFunc: RowFilterFunc<UIRowId>) {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -309,8 +308,8 @@ export class BaseFilteredRowSource extends RowListener implements RowSource {
|
||||
}
|
||||
|
||||
// These are implemented by FilteredRowSource, but the base class doesn't need to do anything.
|
||||
protected _addExcludedRow(row: RowId): void { /* no-op */ }
|
||||
protected _deleteExcludedRow(row: RowId): boolean { return true; }
|
||||
protected _addExcludedRow(row: UIRowId): void { /* no-op */ }
|
||||
protected _deleteExcludedRow(row: UIRowId): boolean { return true; }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,13 +320,13 @@ export class BaseFilteredRowSource extends RowListener implements RowSource {
|
||||
* FilteredRowSource is also a RowListener, so to subscribe to a rowSource, use `subscribeTo()`.
|
||||
*/
|
||||
export class FilteredRowSource extends BaseFilteredRowSource {
|
||||
private _excludedRows: Set<RowId> = new Set(); // Set of rows NOT matching the filter.
|
||||
private _excludedRows: Set<UIRowId> = new Set(); // Set of rows NOT matching the filter.
|
||||
|
||||
/**
|
||||
* Change the filter function. This may trigger 'remove' and 'add' events as necessary to indicate
|
||||
* that rows stopped or started matching the new filter.
|
||||
*/
|
||||
public updateFilter(filterFunc: RowFilterFunc<RowId>) {
|
||||
public updateFilter(filterFunc: RowFilterFunc<UIRowId>) {
|
||||
this._filterFunc = filterFunc;
|
||||
const changes: FilterRowChanges = {};
|
||||
// After the first call, _excludedRows may have additional rows, but there is no harm in it,
|
||||
@@ -356,8 +355,8 @@ export class FilteredRowSource extends BaseFilteredRowSource {
|
||||
return this._excludedRows.values();
|
||||
}
|
||||
|
||||
protected _addExcludedRow(row: RowId): void { this._excludedRows.add(row); }
|
||||
protected _deleteExcludedRow(row: RowId): boolean { return this._excludedRows.delete(row); }
|
||||
protected _addExcludedRow(row: UIRowId): void { this._excludedRows.add(row); }
|
||||
protected _deleteExcludedRow(row: UIRowId): boolean { return this._excludedRows.delete(row); }
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -368,7 +367,7 @@ export class FilteredRowSource extends BaseFilteredRowSource {
|
||||
* Private helper object that maintains a set of rows for a particular group.
|
||||
*/
|
||||
class RowGroupHelper<Value> extends RowSource {
|
||||
private _rows: Set<RowId> = new Set();
|
||||
private _rows: Set<UIRowId> = new Set();
|
||||
constructor(public readonly groupValue: Value) {
|
||||
super();
|
||||
}
|
||||
@@ -411,12 +410,12 @@ function _addToMapOfArrays<K, V>(map: Map<K, V[]>, key: K, r: V): void {
|
||||
*/
|
||||
export class RowGrouping<Value> extends RowListener {
|
||||
// Maps row identifiers to groupValues.
|
||||
private _rowsToValues: Map<RowId, Value> = new Map();
|
||||
private _rowsToValues: Map<UIRowId, Value> = new Map();
|
||||
|
||||
// Maps group values to RowGroupHelpers
|
||||
private _valuesToGroups: Map<Value, RowGroupHelper<Value>> = new Map();
|
||||
|
||||
constructor(private _groupFunc: (row: RowId) => Value) {
|
||||
constructor(private _groupFunc: (row: UIRowId) => Value) {
|
||||
super();
|
||||
|
||||
// On disposal, dispose all RowGroupHelpers that we maintain.
|
||||
@@ -538,15 +537,15 @@ export class RowGrouping<Value> extends RowListener {
|
||||
* SortedRowSet re-emits 'rowNotify(rows, value)' events from RowSources that it subscribes to.
|
||||
*/
|
||||
export class SortedRowSet extends RowListener {
|
||||
private _allRows: Set<RowId> = new Set();
|
||||
private _allRows: Set<UIRowId> = new Set();
|
||||
private _isPaused: boolean = false;
|
||||
private _koArray: KoArray<RowId>;
|
||||
private _koArray: KoArray<UIRowId>;
|
||||
private _keepFunc?: (rowId: number|'new') => boolean;
|
||||
|
||||
constructor(private _compareFunc: CompareFunc<RowId>,
|
||||
constructor(private _compareFunc: CompareFunc<UIRowId>,
|
||||
private _skippableRows?: SkippableRows) {
|
||||
super();
|
||||
this._koArray = this.autoDispose(koArray<RowId>());
|
||||
this._koArray = this.autoDispose(koArray<UIRowId>());
|
||||
this._keepFunc = _skippableRows?.getKeepFunc();
|
||||
}
|
||||
|
||||
@@ -572,7 +571,7 @@ export class SortedRowSet extends RowListener {
|
||||
/**
|
||||
* Re-sorts the array according to the new compareFunc.
|
||||
*/
|
||||
public updateSort(compareFunc: CompareFunc<RowId>): void {
|
||||
public updateSort(compareFunc: CompareFunc<UIRowId>): void {
|
||||
this._compareFunc = compareFunc;
|
||||
if (!this._isPaused) {
|
||||
this._koArray.assign(Array.from(this._koArray.peek()).sort(this._compareFunc));
|
||||
@@ -650,7 +649,7 @@ export class SortedRowSet extends RowListener {
|
||||
|
||||
// Filter out any rows that should be skipped. This is a no-op if no _keepFunc was found.
|
||||
// All rows that sort within nContext rows of something meant to be kept are also kept.
|
||||
private _keep(rows: RowId[], nContext: number = 2) {
|
||||
private _keep(rows: UIRowId[], nContext: number = 2) {
|
||||
// Nothing to be done if there's no _keepFunc.
|
||||
if (!this._keepFunc) { return rows; }
|
||||
|
||||
@@ -706,7 +705,7 @@ export class SortedRowSet extends RowListener {
|
||||
}
|
||||
}
|
||||
|
||||
type RowTester = (rowId: RowId) => boolean;
|
||||
type RowTester = (rowId: UIRowId) => boolean;
|
||||
/**
|
||||
* RowWatcher is a RowListener that maintains an observable function that checks whether a row
|
||||
* is in the connected RowSource.
|
||||
@@ -718,7 +717,7 @@ export class RowWatcher extends RowListener {
|
||||
public rowFilter: Observable<RowTester> = Observable.create(this, () => false);
|
||||
// We count the number of times the row is added or removed from the source.
|
||||
// In most cases row is added and removed only once.
|
||||
private _rowCounter: Map<RowId, number> = new Map();
|
||||
private _rowCounter: Map<UIRowId, number> = new Map();
|
||||
|
||||
public clear() {
|
||||
this._rowCounter.clear();
|
||||
|
||||
Reference in New Issue
Block a user