mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-05-04 15:35:10 +01:00
239 lines
7.6 KiB
TypeScript
239 lines
7.6 KiB
TypeScript
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
|
import * as ko from "knockout";
|
|
import * as _ from "underscore";
|
|
import * as CommonConstants from "../../../Common/Constants";
|
|
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
|
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
|
import NewQueryTablesTab from "../../Tabs/QueryTablesTab/QueryTablesTab";
|
|
import * as Constants from "../Constants";
|
|
import * as Entities from "../Entities";
|
|
import CacheBase from "./CacheBase";
|
|
|
|
// This is the format of the data we will have to pass to Datatable render callback,
|
|
// and property names are defined by Datatable as well.
|
|
export interface IDataTableRenderData {
|
|
draw: number;
|
|
aaData: any;
|
|
recordsTotal: number;
|
|
recordsFiltered: number;
|
|
}
|
|
|
|
abstract class DataTableViewModel {
|
|
private static lastPageLabel = ">>"; // Localize
|
|
private static loadMoreLabel = "Load more"; // Localize
|
|
|
|
/* Observables */
|
|
public items = ko.observableArray<Entities.ITableEntity>();
|
|
public selected = ko.observableArray<Entities.ITableEntity>();
|
|
|
|
public table: DataTables.DataTable;
|
|
|
|
// The anchor item is for shift selection. i.e., select all items between anchor item and a give item.
|
|
public lastSelectedAnchorItem: Entities.ITableEntity;
|
|
public lastSelectedItem: Entities.ITableEntity;
|
|
|
|
public cache: CacheBase<Entities.ITableEntity>;
|
|
|
|
protected continuationToken: any;
|
|
protected allDownloaded: boolean;
|
|
protected lastPrefetchTime: number;
|
|
protected downloadSize = 300;
|
|
protected _documentIterator: QueryIterator<ItemDefinition & Resource>;
|
|
|
|
// Used by table redraw throttling
|
|
protected pollingInterval = 1000;
|
|
private redrawInterval = 500;
|
|
private pendingRedraw = false;
|
|
private lastRedrawTime = new Date().getTime();
|
|
|
|
private dataTableOperationManager: IDataTableOperation;
|
|
|
|
public queryTablesTab: NewQueryTablesTab;
|
|
|
|
constructor() {
|
|
this.items([]);
|
|
this.selected([]);
|
|
// Late bound
|
|
this.dataTableOperationManager = null;
|
|
}
|
|
|
|
public bind(dataTableOperationManager: IDataTableOperation): void {
|
|
this.dataTableOperationManager = dataTableOperationManager;
|
|
}
|
|
|
|
public clearLastSelected(): void {
|
|
this.lastSelectedItem = null;
|
|
this.lastSelectedAnchorItem = null;
|
|
}
|
|
|
|
public clearCache(): void {
|
|
this.cache.clear();
|
|
this._documentIterator = null;
|
|
this.continuationToken = null;
|
|
this.allDownloaded = false;
|
|
}
|
|
|
|
public clearSelection(): void {
|
|
this.selected.removeAll();
|
|
}
|
|
|
|
// Redraws the table, but guarantees that multiple sequential calls will not incur
|
|
// another redraw until a certain time interval has passed.
|
|
public redrawTableThrottled() {
|
|
if (!this.pendingRedraw) {
|
|
this.pendingRedraw = true;
|
|
|
|
var current = new Date().getTime();
|
|
var timeSinceLastRedraw = current - this.lastRedrawTime;
|
|
var redraw = () => {
|
|
this.table.draw(false /*reset*/);
|
|
this.lastRedrawTime = new Date().getTime();
|
|
this.pendingRedraw = false;
|
|
};
|
|
|
|
if (timeSinceLastRedraw > this.redrawInterval) {
|
|
redraw();
|
|
} else {
|
|
var timeUntilNextRedraw = this.redrawInterval - timeSinceLastRedraw;
|
|
setTimeout(() => redraw(), timeUntilNextRedraw);
|
|
}
|
|
}
|
|
}
|
|
|
|
public focusDataTable(): void {
|
|
this.dataTableOperationManager.focusTable();
|
|
}
|
|
|
|
public getItemFromSelectedItems(itemKeys: Entities.IProperty[]): Entities.ITableEntity {
|
|
return _.find(this.selected(), (item: Entities.ITableEntity) => {
|
|
return this.matchesKeys(item, itemKeys);
|
|
});
|
|
}
|
|
|
|
public getItemFromCurrentPage(itemKeys: Entities.IProperty[]): Entities.ITableEntity {
|
|
return _.find(this.items(), (item: Entities.ITableEntity) => {
|
|
return this.matchesKeys(item, itemKeys);
|
|
});
|
|
}
|
|
|
|
public getItemIndexFromCurrentPage(itemKeys: Entities.IProperty[]): number {
|
|
return _.findIndex(this.items(), (item: Entities.ITableEntity) => {
|
|
return this.matchesKeys(item, itemKeys);
|
|
});
|
|
}
|
|
|
|
public getItemIndexFromAllPages(itemKeys: Entities.IProperty[]): number {
|
|
return _.findIndex(this.cache.data, (item: Entities.ITableEntity) => {
|
|
return this.matchesKeys(item, itemKeys);
|
|
});
|
|
}
|
|
|
|
public getItemsFromAllPagesWithinRange(start: number, end: number): Entities.ITableEntity[] {
|
|
return this.cache.data.slice(start, end);
|
|
}
|
|
|
|
public isItemSelected(itemKeys: Entities.IProperty[]): boolean {
|
|
return _.some(this.selected(), (item: Entities.ITableEntity) => {
|
|
return this.matchesKeys(item, itemKeys);
|
|
});
|
|
}
|
|
|
|
public isItemCached(itemKeys: Entities.IProperty[]): boolean {
|
|
return _.some(this.cache.data, (item: Entities.ITableEntity) => {
|
|
return this.matchesKeys(item, itemKeys);
|
|
});
|
|
}
|
|
|
|
public getAllItemsInCurrentPage(): Entities.ITableEntity[] {
|
|
return this.items();
|
|
}
|
|
|
|
public getAllItemsInCache(): Entities.ITableEntity[] {
|
|
return this.cache.data;
|
|
}
|
|
|
|
protected abstract dataComparer(
|
|
item1: Entities.ITableEntity,
|
|
item2: Entities.ITableEntity,
|
|
sortOrder: any,
|
|
oSettings: any
|
|
): number;
|
|
protected abstract isCacheValid(validator: any): boolean;
|
|
|
|
protected sortColumns(sortOrder: any, oSettings: any) {
|
|
var self = this;
|
|
this.clearSelection();
|
|
this.cache.data.sort(function (a: any, b: any) {
|
|
return self.dataComparer(a, b, sortOrder, oSettings);
|
|
});
|
|
this.cache.sortOrder = sortOrder;
|
|
}
|
|
|
|
protected renderPage(startIndex: number, pageSize: number) {
|
|
console.log(
|
|
"🚀 ~ file: DataTableViewModel.ts ~ line 179 ~ DataTableViewModel ~ renderPage ~ this.cache.data",
|
|
this.cache.data
|
|
);
|
|
var endIndex = pageSize < 0 ? this.cache.length : startIndex + pageSize;
|
|
var renderData = this.cache.data.slice(startIndex, endIndex);
|
|
|
|
this.items(renderData);
|
|
|
|
if (this.queryTablesTab.onLoadStartKey != null && this.queryTablesTab.onLoadStartKey != undefined) {
|
|
TelemetryProcessor.traceSuccess(
|
|
Action.Tab,
|
|
{
|
|
databaseName: this.queryTablesTab.collection.databaseId,
|
|
collectionName: this.queryTablesTab.collection.id(),
|
|
dataExplorerArea: CommonConstants.Areas.Tab,
|
|
tabTitle: this.queryTablesTab.tabTitle(),
|
|
},
|
|
this.queryTablesTab.onLoadStartKey
|
|
);
|
|
this.queryTablesTab.onLoadStartKey = null;
|
|
}
|
|
}
|
|
|
|
protected matchesKeys(item: Entities.ITableEntity, itemKeys: Entities.IProperty[]): boolean {
|
|
return itemKeys.every((property: Entities.IProperty) => {
|
|
var itemValue = item[property.key];
|
|
return this.stringCompare(itemValue._, property.value);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Default string comparison is case sensitive as most Azure resources' names are case sensitive.
|
|
* Override this if a name, i.e., Azure File/Directory name, is case insensitive.
|
|
*/
|
|
protected stringCompare(s1: string, s2: string): boolean {
|
|
return s1 === s2;
|
|
}
|
|
|
|
private updatePaginationControls(oSettings: any) {
|
|
var pageInfo = this.table.page.info();
|
|
var pageSize = pageInfo.length;
|
|
var paginateElement = $(oSettings.nTableWrapper).find(Constants.htmlSelectors.paginateSelector);
|
|
|
|
if (this.allDownloaded) {
|
|
if (this.cache.length <= pageSize) {
|
|
// Hide pagination controls if everything fits in one page!.
|
|
paginateElement.hide();
|
|
} else {
|
|
// Enable pagination controls.
|
|
paginateElement.show();
|
|
oSettings.oLanguage.oPaginate.sLast = DataTableViewModel.lastPageLabel;
|
|
}
|
|
} else {
|
|
// Enable pagination controls and show load more button.
|
|
paginateElement.show();
|
|
oSettings.oLanguage.oPaginate.sLast = DataTableViewModel.loadMoreLabel;
|
|
}
|
|
}
|
|
}
|
|
|
|
interface IDataTableOperation {
|
|
focusTable(): void;
|
|
}
|
|
|
|
export default DataTableViewModel;
|