diff --git a/.eslintignore b/.eslintignore index 7a5d06bbf..e182bd2b1 100644 --- a/.eslintignore +++ b/.eslintignore @@ -107,8 +107,8 @@ src/Explorer/Tables/DataTable/CacheBase.ts src/Explorer/Tables/DataTable/DataTableBindingManager.ts src/Explorer/Tables/DataTable/DataTableBuilder.ts src/Explorer/Tables/DataTable/DataTableContextMenu.ts -src/Explorer/Tables/DataTable/DataTableOperationManager.ts -src/Explorer/Tables/DataTable/DataTableOperations.ts +# src/Explorer/Tables/DataTable/DataTableOperationManager.ts +# src/Explorer/Tables/DataTable/DataTableOperations.ts src/Explorer/Tables/DataTable/DataTableViewModel.ts src/Explorer/Tables/DataTable/TableCommands.ts src/Explorer/Tables/DataTable/TableEntityCache.ts diff --git a/less/TableStyles/queryBuilder.less b/less/TableStyles/queryBuilder.less index 5850e25be..50ea1f88b 100644 --- a/less/TableStyles/queryBuilder.less +++ b/less/TableStyles/queryBuilder.less @@ -94,12 +94,13 @@ overflow: hidden; .flex-display(); .flex-direction(); + position: relative; } .tablesQueryTab { padding-left: @MediumSpace; width: 100%; - margin-bottom: 100px; + margin-bottom: 60px; position: relative; height: 100%; } @@ -535,7 +536,6 @@ input::-webkit-inner-spin-button { } .query-document-detail-list { - // overflow-x: hidden; height: 100%; } .query-table-clause-container { @@ -545,14 +545,19 @@ input::-webkit-inner-spin-button { } .query-tab-document-pagination { display: flex; - flex-direction: column; + flex-direction: row; position: absolute; bottom: 0; - padding-left: @MediumSpace; - padding-bottom: 45px; + padding-left: 12px; + justify-content: space-between; + width: 100%; + align-items: center; + height: 60px; } .pagination { margin: 15px 0 !important; + order: 2; + padding-right: 15px; li > .item-link { position: relative; float: left; @@ -577,8 +582,6 @@ input::-webkit-inner-spin-button { height: 100%; width: 100%; } -// .pagination > li > div { -// } /* @media only screen and (max-width: 1200px) { diff --git a/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx b/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx index 492cc376e..70bb73dcc 100644 --- a/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx +++ b/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx @@ -1,7 +1,6 @@ import { IDropdownOption, Image, Label, Stack, Text, TextField } from "@fluentui/react"; import { useBoolean } from "@fluentui/react-hooks"; import React, { FunctionComponent, useEffect, useState } from "react"; -import * as _ from "underscore"; import AddPropertyIcon from "../../../../images/Add-property.svg"; import RevertBackIcon from "../../../../images/RevertBack.svg"; import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils"; @@ -13,7 +12,6 @@ import * as DataTableUtilities from "../../Tables/DataTable/DataTableUtilities"; import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListViewModel"; import * as Entities from "../../Tables/Entities"; import { CassandraAPIDataClient, CassandraTableKey, TableDataClient } from "../../Tables/TableDataClient"; -import * as TableEntityProcessor from "../../Tables/TableEntityProcessor"; import * as Utilities from "../../Tables/Utilities"; import NewQueryTablesTab from "../../Tabs/QueryTablesTab/QueryTablesTab"; import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm"; @@ -123,11 +121,8 @@ export const AddTableEntityPanel: FunctionComponent = const newEntity: Entities.ITableEntity = await tableDataClient.createDocument(queryTablesTab.collection, entity); try { await tableEntityListViewModel.addEntityToCache(newEntity); - // if (!tryInsertNewHeaders(tableEntityListViewModel, newEntity)) { - // tableEntityListViewModel.redrawTableThrottled(); reloadEntities(); setFormError(""); - // } } catch (error) { const errorMessage = getErrorMessage(error); setFormError(errorMessage); @@ -136,46 +131,6 @@ export const AddTableEntityPanel: FunctionComponent = } finally { setIsExecuting(false); } - // try { - // await tableDataClient.createDocument(queryTablesTab.collection, entity); - // reloadEntities(); - // setFormError(""); - // closeSidePanel(); - // } catch (error) { - // const errorMessage = getErrorMessage(error); - // setFormError(errorMessage); - // handleError(errorMessage, "AddTableRow"); - // } finally { - // setIsExecuting(false); - // } - }; - - const tryInsertNewHeaders = (viewModel: TableEntityListViewModel, newEntity: Entities.ITableEntity): boolean => { - let newHeaders: string[] = []; - const keys = Object.keys(newEntity); - keys && - keys.forEach((key: string) => { - if ( - !_.contains(viewModel.headers, key) && - key !== TableEntityProcessor.keyProperties.attachments && - key !== TableEntityProcessor.keyProperties.etag && - key !== TableEntityProcessor.keyProperties.resourceId && - key !== TableEntityProcessor.keyProperties.self && - (!(userContext.apiType === "Cassandra") || key !== TableConstants.EntityKeyNames.RowKey) - ) { - newHeaders.push(key); - } - }); - - let newHeadersInserted = false; - if (newHeaders.length) { - if (!DataTableUtilities.checkForDefaultHeader(viewModel.headers)) { - newHeaders = viewModel.headers.concat(newHeaders); - } - viewModel.updateHeaders(newHeaders, /* notifyColumnChanges */ true, /* enablePrompt */ false); - newHeadersInserted = true; - } - return newHeadersInserted; }; /* Add new entity row */ diff --git a/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx b/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx index a144d723c..c540f59ce 100644 --- a/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx +++ b/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx @@ -9,7 +9,6 @@ import { TableEntity } from "../../../Common/TableEntity"; import { useSidePanel } from "../../../hooks/useSidePanel"; import { userContext } from "../../../UserContext"; import * as TableConstants from "../../Tables/Constants"; -import * as DataTableUtilities from "../../Tables/DataTable/DataTableUtilities"; import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListViewModel"; import * as Entities from "../../Tables/Entities"; import { CassandraAPIDataClient, TableDataClient } from "../../Tables/TableDataClient"; @@ -215,7 +214,6 @@ export const EditTableEntityPanel: FunctionComponent const entity: Entities.ITableEntity = entityFromAttributes(entities); const newTableDataClient = userContext.apiType === "Cassandra" ? cassandraApiClient : tableDataClient; const originalDocumentData = userContext.apiType === "Cassandra" ? originalDocument[0] : originalDocument; - // await newTableDataClient.updateDocument(queryTablesTab.collection, originalDocumentData, entity); try { const newEntity: Entities.ITableEntity = await newTableDataClient.updateDocument( @@ -224,11 +222,8 @@ export const EditTableEntityPanel: FunctionComponent entity ); await tableEntityListViewModel.updateCachedEntity(newEntity); - // if (!tryInsertNewHeaders(tableEntityListViewModel, newEntity)) { - // tableEntityListViewModel.redrawTableThrottled(); reloadEntities(); closeSidePanel(); - // } tableEntityListViewModel.selected.removeAll(); tableEntityListViewModel.selected.push(newEntity); } catch (error) { @@ -238,36 +233,6 @@ export const EditTableEntityPanel: FunctionComponent } finally { setIsExecuting(false); } - // reloadEntities(); - // closeSidePanel(); - }; - - const tryInsertNewHeaders = (viewModel: TableEntityListViewModel, newEntity: Entities.ITableEntity): boolean => { - let newHeaders: string[] = []; - const keys = Object.keys(newEntity); - keys && - keys.forEach((key: string) => { - if ( - !_.contains(viewModel.headers, key) && - key !== TableEntityProcessor.keyProperties.attachments && - key !== TableEntityProcessor.keyProperties.etag && - key !== TableEntityProcessor.keyProperties.resourceId && - key !== TableEntityProcessor.keyProperties.self && - (!(userContext.apiType === "Cassandra") || key !== TableConstants.EntityKeyNames.RowKey) - ) { - newHeaders.push(key); - } - }); - - let newHeadersInserted = false; - if (newHeaders.length) { - if (!DataTableUtilities.checkForDefaultHeader(viewModel.headers)) { - newHeaders = viewModel.headers.concat(newHeaders); - } - viewModel.updateHeaders(newHeaders, /* notifyColumnChanges */ true, /* enablePrompt */ false); - newHeadersInserted = true; - } - return newHeadersInserted; }; // Add new entity row diff --git a/src/Explorer/Tables/DataTable/DataTableBuilder.ts b/src/Explorer/Tables/DataTable/DataTableBuilder.ts deleted file mode 100644 index 2fc3000ba..000000000 --- a/src/Explorer/Tables/DataTable/DataTableBuilder.ts +++ /dev/null @@ -1,52 +0,0 @@ -import * as Utilities from "../Utilities"; - -/** - * Wrapper function for creating data tables. Call this method, not the - * data tables constructor when you want to create a data table. This - * function makes sure that content without a render function is properly - * encoded to prevent XSS. - * @param{$dataTableElem} JQuery data table element - * @param{$settings} Settings to use when creating the data table - */ -export function createDataTable($dataTableElem: JQuery, settings: any): DataTables.DataTable { - return $dataTableElem.DataTable(applyDefaultRendering(settings)); -} - -/** - * Go through the settings for a data table and apply a simple HTML encode to any column - * without a render function to prevent XSS. - * @param{settings} The settings to check - * @return The given settings with all columns having a rendering function - */ -function applyDefaultRendering(settings: any): DataTables.SettingsLegacy { - var tableColumns: DataTables.ColumnLegacy[] = null; - - if (settings.aoColumns) { - tableColumns = settings.aoColumns; - } else if (settings.aoColumnDefs) { - // for tables we use aoColumnDefs instead of aoColumns - tableColumns = settings.aoColumnDefs; - } - - // either the settings had no columns defined, or they were called - // by a property name which we have not used before - if (!tableColumns) { - return settings; - } - - for (var i = 0; i < tableColumns.length; i++) { - // the column does not have a render function - if (!tableColumns[i].mRender) { - tableColumns[i].mRender = defaultDataRender; - } - } - return settings; -} - -/** - * Default data render function, whatever is done to data in here - * will be done to any data which we do not specify a render for. - */ -function defaultDataRender(data: any, type: string, full: any) { - return Utilities.htmlEncode(data); -} diff --git a/src/Explorer/Tables/DataTable/DataTableOperationManager.ts b/src/Explorer/Tables/DataTable/DataTableOperationManager.ts deleted file mode 100644 index 7be7c4aaa..000000000 --- a/src/Explorer/Tables/DataTable/DataTableOperationManager.ts +++ /dev/null @@ -1,300 +0,0 @@ -import ko from "knockout"; - -import * as DataTableOperations from "./DataTableOperations"; -import * as Constants from "../Constants"; -import TableCommands from "./TableCommands"; -import TableEntityListViewModel from "./TableEntityListViewModel"; -import * as Utilities from "../Utilities"; -import * as Entities from "../Entities"; - -/* - * Base class for data table row selection. - */ -export default class DataTableOperationManager { - private _tableEntityListViewModel: TableEntityListViewModel; - private _tableCommands: TableCommands; - private dataTable: JQuery; - - constructor(table: JQuery, viewModel: TableEntityListViewModel, tableCommands: TableCommands) { - this.dataTable = table; - this._tableEntityListViewModel = viewModel; - this._tableCommands = tableCommands; - - this.bind(); - this._tableEntityListViewModel.bind(this); - } - - private click = (event: JQueryEventObject) => { - var elem: JQuery = $(event.currentTarget); - this.updateLastSelectedItem(elem, event.shiftKey); - - if (Utilities.isEnvironmentCtrlPressed(event)) { - this.applyCtrlSelection(elem); - } else if (event.shiftKey) { - this.applyShiftSelection(elem); - } else { - this.applySingleSelection(elem); - } - }; - - private doubleClick = (event: JQueryEventObject) => { - this.tryOpenEditor(); - }; - - private keyDown = (event: JQueryEventObject): boolean => { - var isUpArrowKey: boolean = event.keyCode === Constants.keyCodes.UpArrow, - isDownArrowKey: boolean = event.keyCode === Constants.keyCodes.DownArrow, - handled: boolean = false; - - if (isUpArrowKey || isDownArrowKey) { - var lastSelectedItem: Entities.ITableEntity = this._tableEntityListViewModel.lastSelectedItem; - var dataTableRows: JQuery = $(Constants.htmlSelectors.dataTableAllRowsSelector); - var maximumIndex = dataTableRows.length - 1; - - // If can't find an index for lastSelectedItem, then either no item is previously selected or it goes across page. - // Simply select the first item in this case. - var lastSelectedItemIndex = lastSelectedItem - ? this._tableEntityListViewModel.getItemIndexFromCurrentPage( - this._tableEntityListViewModel.getTableEntityKeys(lastSelectedItem.RowKey._) - ) - : -1; - var nextIndex: number = isUpArrowKey ? lastSelectedItemIndex - 1 : lastSelectedItemIndex + 1; - var safeIndex: number = Utilities.ensureBetweenBounds(nextIndex, 0, maximumIndex); - var selectedRowElement: JQuery = dataTableRows.eq(safeIndex); - - if (selectedRowElement) { - if (event.shiftKey) { - this.applyShiftSelection(selectedRowElement); - } else { - this.applySingleSelection(selectedRowElement); - } - - this.updateLastSelectedItem(selectedRowElement, event.shiftKey); - handled = true; - DataTableOperations.scrollToRowIfNeeded(dataTableRows, safeIndex, isUpArrowKey); - } - } else if ( - Utilities.isEnvironmentCtrlPressed(event) && - !Utilities.isEnvironmentShiftPressed(event) && - !Utilities.isEnvironmentAltPressed(event) && - event.keyCode === Constants.keyCodes.A - ) { - this.applySelectAll(); - handled = true; - } - - return !handled; - }; - - // Note: There is one key up event each time a key is pressed; - // in contrast, there may be more than one key down and key - // pressed events. - private keyUp = (event: JQueryEventObject): boolean => { - var handled: boolean = false; - - switch (event.keyCode) { - case Constants.keyCodes.Enter: - handled = this.tryOpenEditor(); - break; - case Constants.keyCodes.Delete: - handled = this.tryHandleDeleteSelected(); - break; - } - - return !handled; - }; - - private itemDropped = (event: JQueryEventObject): boolean => { - var handled: boolean = false; - var items = (event.originalEvent).dataTransfer.items; - - if (!items) { - // On browsers outside of Chromium - // we can't discern between dirs and files - // so we will disable drag & drop for now - return null; - } - - for (var i = 0; i < items.length; i++) { - var item = items[i]; - var entry = item.webkitGetAsEntry(); - - if (entry.isFile) { - // TODO: parse the file and insert content as entities - } - } - - return !handled; - }; - - private tryOpenEditor(): boolean { - return this._tableCommands.tryOpenEntityEditor(this._tableEntityListViewModel); - } - - private tryHandleDeleteSelected(): boolean { - var selectedEntities: Entities.ITableEntity[] = this._tableEntityListViewModel.selected(); - var handled: boolean = false; - - if (selectedEntities && selectedEntities.length) { - this._tableCommands.deleteEntitiesCommand(this._tableEntityListViewModel); - handled = true; - } - - return handled; - } - - private getEntityIdentity($elem: JQuery): Entities.ITableEntityIdentity { - return { - RowKey: $elem.attr(Constants.htmlAttributeNames.dataTableRowKeyAttr), - }; - } - - private updateLastSelectedItem($elem: JQuery, isShiftSelect: boolean) { - var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem); - var entity = this._tableEntityListViewModel.getItemFromCurrentPage( - this._tableEntityListViewModel.getTableEntityKeys(entityIdentity.RowKey) - ); - - this._tableEntityListViewModel.lastSelectedItem = entity; - - if (!isShiftSelect) { - this._tableEntityListViewModel.lastSelectedAnchorItem = entity; - } - } - - private applySingleSelection($elem: JQuery) { - if ($elem) { - var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem); - - this._tableEntityListViewModel.clearSelection(); - this.addToSelection(entityIdentity.RowKey); - } - } - - private applySelectAll() { - this._tableEntityListViewModel.clearSelection(); - ko.utils.arrayPushAll( - this._tableEntityListViewModel.selected, - this._tableEntityListViewModel.getAllItemsInCurrentPage() - ); - } - - private applyCtrlSelection($elem: JQuery): void { - var koSelected: ko.ObservableArray = this._tableEntityListViewModel - ? this._tableEntityListViewModel.selected - : null; - - if (koSelected) { - var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem); - - if ( - !this._tableEntityListViewModel.isItemSelected( - this._tableEntityListViewModel.getTableEntityKeys(entityIdentity.RowKey) - ) - ) { - // Adding item not previously in selection - this.addToSelection(entityIdentity.RowKey); - } else { - koSelected.remove((item: Entities.ITableEntity) => item.RowKey._ === entityIdentity.RowKey); - } - } - } - - private applyShiftSelection($elem: JQuery): void { - var anchorItem = this._tableEntityListViewModel.lastSelectedAnchorItem; - - // If anchor item doesn't exist, use the first available item of current page instead - if (!anchorItem && this._tableEntityListViewModel.items().length > 0) { - anchorItem = this._tableEntityListViewModel.items()[0]; - } - - if (anchorItem) { - var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem); - var elementIndex = this._tableEntityListViewModel.getItemIndexFromAllPages( - this._tableEntityListViewModel.getTableEntityKeys(entityIdentity.RowKey) - ); - var anchorIndex = this._tableEntityListViewModel.getItemIndexFromAllPages( - this._tableEntityListViewModel.getTableEntityKeys(anchorItem.RowKey._) - ); - - var startIndex = Math.min(elementIndex, anchorIndex); - var endIndex = Math.max(elementIndex, anchorIndex); - - this._tableEntityListViewModel.clearSelection(); - ko.utils.arrayPushAll( - this._tableEntityListViewModel.selected, - this._tableEntityListViewModel.getItemsFromAllPagesWithinRange(startIndex, endIndex + 1) - ); - } - } - - private applyContextMenuSelection($elem: JQuery) { - var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem); - - if ( - !this._tableEntityListViewModel.isItemSelected( - this._tableEntityListViewModel.getTableEntityKeys(entityIdentity.RowKey) - ) - ) { - if (this._tableEntityListViewModel.selected().length) { - this._tableEntityListViewModel.clearSelection(); - } - this.addToSelection(entityIdentity.RowKey); - } - } - - private addToSelection(rowKey: string) { - var selectedEntity: Entities.ITableEntity = this._tableEntityListViewModel.getItemFromCurrentPage( - this._tableEntityListViewModel.getTableEntityKeys(rowKey) - ); - - if (selectedEntity != null) { - this._tableEntityListViewModel.selected.push(selectedEntity); - } - } - - // Selecting first row if the selection is empty. - public selectFirstIfNeeded(): void { - var koSelected: ko.ObservableArray = this._tableEntityListViewModel - ? this._tableEntityListViewModel.selected - : null; - var koEntities: ko.ObservableArray = this._tableEntityListViewModel - ? this._tableEntityListViewModel.items - : null; - - if (!koSelected().length && koEntities().length) { - var firstEntity: Entities.ITableEntity = koEntities()[0]; - - // Clear last selection: lastSelectedItem and lastSelectedAnchorItem - this._tableEntityListViewModel.clearLastSelected(); - - this.addToSelection(firstEntity.RowKey._); - - // Update last selection - this._tableEntityListViewModel.lastSelectedItem = firstEntity; - - // Finally, make sure first row is visible - DataTableOperations.scrollToTopIfNeeded(); - } - } - - public bind() { - this.dataTable.on("click", "tr", this.click); - this.dataTable.on("dblclick", "tr", this.doubleClick); - this.dataTable.on("keydown", "td", this.keyDown); - this.dataTable.on("keyup", "td", this.keyUp); - - // Keyboard navigation - selecting first row if the selection is empty when the table gains focus. - this.dataTable.on("focus", () => { - this.selectFirstIfNeeded(); - return true; - }); - - // Bind drag & drop behavior - $("body").on("drop", this.itemDropped); - } - - public focusTable(): void { - this.dataTable.focus(); - } -} diff --git a/src/Explorer/Tables/DataTable/DataTableOperations.ts b/src/Explorer/Tables/DataTable/DataTableOperations.ts deleted file mode 100644 index 0111787a3..000000000 --- a/src/Explorer/Tables/DataTable/DataTableOperations.ts +++ /dev/null @@ -1,192 +0,0 @@ -import _ from "underscore"; -import Q from "q"; - -import * as Entities from "../Entities"; -import * as QueryBuilderConstants from "../Constants"; -import * as Utilities from "../Utilities"; - -export function getRowSelector(selectorSchema: Entities.IProperty[]): string { - var selector: string = ""; - selectorSchema && - selectorSchema.forEach((p: Entities.IProperty) => { - selector += "[" + p.key + '="' + Utilities.jQuerySelectorEscape(p.value) + '"]'; - }); - return QueryBuilderConstants.htmlSelectors.dataTableAllRowsSelector + selector; -} - -export function isRowVisible(dataTableScrollBodyQuery: JQuery, element: HTMLElement): boolean { - var isVisible = false; - - if (dataTableScrollBodyQuery.length && element) { - var elementRect: ClientRect = element.getBoundingClientRect(), - dataTableScrollBodyRect: ClientRect = dataTableScrollBodyQuery.get(0).getBoundingClientRect(); - - isVisible = elementRect.bottom <= dataTableScrollBodyRect.bottom && dataTableScrollBodyRect.top <= elementRect.top; - } - - return isVisible; -} - -export function scrollToRowIfNeeded(dataTableRows: JQuery, currentIndex: number, isScrollUp: boolean): void { - if (dataTableRows.length) { - var dataTableScrollBodyQuery: JQuery = $(QueryBuilderConstants.htmlSelectors.dataTableScrollBodySelector), - selectedRowElement: HTMLElement = dataTableRows.get(currentIndex); - - if (dataTableScrollBodyQuery.length && selectedRowElement) { - var isVisible: boolean = isRowVisible(dataTableScrollBodyQuery, selectedRowElement); - - if (!isVisible) { - var selectedRowQuery: JQuery = $(selectedRowElement), - scrollPosition: number = dataTableScrollBodyQuery.scrollTop(), - selectedElementPosition: number = selectedRowQuery.position().top, - newScrollPosition: number = 0; - - if (isScrollUp) { - newScrollPosition = scrollPosition + selectedElementPosition; - } else { - newScrollPosition = - scrollPosition + (selectedElementPosition + selectedRowQuery.height() - dataTableScrollBodyQuery.height()); - } - - dataTableScrollBodyQuery.scrollTop(newScrollPosition); - } - } - } -} - -export function scrollToTopIfNeeded(): void { - var $dataTableRows: JQuery = $(QueryBuilderConstants.htmlSelectors.dataTableAllRowsSelector), - $dataTableScrollBody: JQuery = $(QueryBuilderConstants.htmlSelectors.dataTableScrollBodySelector); - - if ($dataTableRows.length && $dataTableScrollBody.length) { - $dataTableScrollBody.scrollTop(0); - } -} - -export function setPaginationButtonEventHandlers(): void { - $(QueryBuilderConstants.htmlSelectors.dataTablePaginationButtonSelector) - .on("mousedown", (event: JQueryEventObject) => { - // Prevents the table contents from briefly jumping when clicking on "Load more" - event.preventDefault(); - }) - .attr("role", "button"); -} - -export function filterColumns(table: DataTables.DataTable, settings: boolean[]): void { - settings && - settings.forEach((value: boolean, index: number) => { - table.column(index).visible(value, false); - }); - table.columns.adjust().draw(false); -} - -/** - * Reorder columns based on current order. - * If no current order is specified, reorder the columns based on intial order. - */ -export function reorderColumns( - table: DataTables.DataTable, - targetOrder: number[], - currentOrder?: number[] -): Q.Promise { - var columnsCount: number = targetOrder.length; - var isCurrentOrderPassedIn: boolean = !!currentOrder; - if (!isCurrentOrderPassedIn) { - currentOrder = getInitialOrder(columnsCount); - } - var isSameOrder: boolean = Utilities.isEqual(currentOrder, targetOrder); - - // if the targetOrder is the same as current order, do nothing. - if (!isSameOrder) { - // Otherwise, calculate the transformation order. - // If current order not specified, then it'll be set to initial order, - // i.e., either no reorder happened before or reordering to its initial order, - // Then the transformation order will be the same as target order. - // If current order is specified, then a transformation order is calculated. - // Refer to calculateTransformationOrder for details about transformation order. - var transformationOrder: number[] = isCurrentOrderPassedIn - ? calculateTransformationOrder(currentOrder, targetOrder) - : targetOrder; - try { - $.fn.dataTable.ColReorder(table).fnOrder(transformationOrder); - } catch (err) { - return Q.reject(err); - } - } - return Q.resolve(null); -} - -export function resetColumns(table: DataTables.DataTable): void { - $.fn.dataTable.ColReorder(table).fnReset(); -} - -/** - * A table's initial order is described in the form of a natural ascending order. - * E.g., for a table with 9 columns, the initial order will be: [0, 1, 2, 3, 4, 5, 6, 7, 8] - */ -export function getInitialOrder(columnsCount: number): number[] { - return _.range(columnsCount); -} - -/** - * Get current table's column order which is described based on initial table. E.g., - * Initial order: I = [0, 1, 2, 3, 4, 5, 6, 7, 8] <----> {prop0, prop1, prop2, prop3, prop4, prop5, prop6, prop7, prop8} - * Current order: C = [0, 1, 2, 6, 7, 3, 4, 5, 8] <----> {prop0, prop1, prop2, prop6, prop7, prop3, prop4, prop5, prop8} - */ -export function getCurrentOrder(table: DataTables.DataTable): number[] { - return $.fn.dataTable.ColReorder(table).fnOrder(); -} - -/** - * Switch the index and value for each element of an array. e.g., - * InputArray: [0, 1, 2, 6, 7, 3, 4, 5, 8] - * Result: [0, 1, 2, 5, 6, 7, 3, 4, 8] - */ -export function invertIndexValues(inputArray: number[]): number[] { - var invertedArray: number[] = []; - if (inputArray) { - inputArray.forEach((value: number, index: number) => { - invertedArray[inputArray[index]] = index; - }); - } - - return invertedArray; -} - -/** - * DataTable fnOrder API is based on the current table. So we need to map the order targeting original table to targeting current table. - * An detailed example for this. Assume the table has 9 columns. - * Initial order (order of the initial table): I = [0, 1, 2, 3, 4, 5, 6, 7, 8] <----> {prop0, prop1, prop2, prop3, prop4, prop5, prop6, prop7, prop8} - * Current order (order of the current table): C = [0, 1, 2, 6, 7, 3, 4, 5, 8] <----> {prop0, prop1, prop2, prop6, prop7, prop3, prop4, prop5, prop8} - * Target order (order of the targeting table): T = [0, 1, 2, 5, 6, 7, 8, 3, 4] <----> {prop0, prop1, prop2, prop5, prop6, prop7, prop8, prop3, prop4} - * Transformation order: an order passed to fnOrder API that transforms table from current order to target order. - * When the table is constructed, it has the intial order. After an reordering with current order array, now the table is shown in current order, e.g., - * column 3 in the current table is actually column C[3]=6 in the intial table, both indicate the column with header prop6. - * Now we want to continue to do another reorder to make the target table in the target order. Directly invoking API with the new order won't work as - * the API only do reorder based on the current table like the first time we invoke the API. So an order based on the current table needs to be calulated. - * Here is an example of how to calculate the transformation order: - * In target table, column 3 should be column T[3]=5 in the intial table with header prop5, while in current table, column with header prop5 is column 7 as C[7]=5. - * As a result, in transformation order, column 3 in the target table should be column 7 in the current table, Trans[3] = 7. In the same manner, we can get the - * transformation order: Trans = [0, 1, 2, 7, 3, 4, 8, 5, 6] - */ -export function calculateTransformationOrder(currentOrder: number[], targetOrder: number[]): number[] { - var transformationOrder: number[] = []; - if (currentOrder && targetOrder && currentOrder.length === targetOrder.length) { - var invertedCurrentOrder: number[] = invertIndexValues(currentOrder); - transformationOrder = targetOrder.map((value: number) => invertedCurrentOrder[value]); - } - return transformationOrder; -} - -export function getDataTableHeaders(table: DataTables.DataTable): string[] { - var columns: DataTables.ColumnsMethods = table.columns(); - var headers: string[] = []; - if (columns) { - // table.columns() return ColumnsMethods which is an array of arrays - var columnIndexes: number[] = (columns)[0]; - if (columnIndexes) { - headers = columnIndexes.map((value: number) => $(table.columns(value).header()).html()); - } - } - return headers; -} diff --git a/src/Explorer/Tables/DataTable/DataTableUtilities.ts b/src/Explorer/Tables/DataTable/DataTableUtilities.ts index 206e3bcef..f3aab5485 100644 --- a/src/Explorer/Tables/DataTable/DataTableUtilities.ts +++ b/src/Explorer/Tables/DataTable/DataTableUtilities.ts @@ -123,10 +123,3 @@ export function checkForDefaultHeader(headers: string[]): boolean { export function forceRecalculateTableSize(): void { $("body").trigger("resize"); } - -/** - * Turns off the spinning progress indicator on the data table. - */ -export function turnOffProgressIndicator(): void { - $("div.dataTables_processing").hide(); -} diff --git a/src/Explorer/Tables/DataTable/DataTableViewModel.ts b/src/Explorer/Tables/DataTable/DataTableViewModel.ts index 02b00ed35..1da9c0b44 100644 --- a/src/Explorer/Tables/DataTable/DataTableViewModel.ts +++ b/src/Explorer/Tables/DataTable/DataTableViewModel.ts @@ -5,7 +5,6 @@ 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"; @@ -46,19 +45,11 @@ abstract class DataTableViewModel { 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 { @@ -100,10 +91,6 @@ abstract class DataTableViewModel { } } - 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); @@ -170,10 +157,6 @@ abstract class DataTableViewModel { } 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); @@ -208,27 +191,6 @@ abstract class DataTableViewModel { 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 { diff --git a/src/Explorer/Tables/DataTable/TableCommands.ts b/src/Explorer/Tables/DataTable/TableCommands.ts index c5a80f65e..28aae0916 100644 --- a/src/Explorer/Tables/DataTable/TableCommands.ts +++ b/src/Explorer/Tables/DataTable/TableCommands.ts @@ -94,8 +94,4 @@ export default class TableCommands { return null; } - - public resetColumns(viewModel: TableEntityListViewModel): void { - viewModel.reloadTable(); - } } diff --git a/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts b/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts index b5665a81d..db442bb98 100644 --- a/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts +++ b/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts @@ -132,113 +132,10 @@ export default class TableEntityListViewModel extends DataTableViewModel { return [{ key: Constants.EntityKeyNames.RowKey, value: rowKey }]; } - public reloadTable(useSetting: boolean = true, resetHeaders: boolean = true): DataTables.DataTable { - this.clearCache(); - this.clearSelection(); - this.isCancelled = false; - - this.useSetting = useSetting; - if (resetHeaders) { - this.updateHeaders([Constants.defaultHeader]); - } - return this.table.ajax.reload(); - } - public updateHeaders(newHeaders: string[], notifyColumnChanges: boolean = false, enablePrompt: boolean = true): void { this.headers = newHeaders; } - public async a(): Promise { - const a = await this.b(); - console.log("🚀 ~ file: TableEntityListViewModel.ts ~ line 153 ~ TableEntityListViewModel ~ a ~ a", a); - return a; - } - - public async b(): Promise { - const b = await this.c(); - console.log("🚀 ~ file: TableEntityListViewModel.ts ~ line 157 ~ TableEntityListViewModel ~ b ~ b", b); - return b; - } - - public async c(tableQuery?: Entities.ITableQuery, downloadSize?: number): Promise { - var c: any; - var d: any; - if (this._documentIterator && this.continuationToken) { - // TODO handle Cassandra case - - d = await this._documentIterator.fetchNext(); - let entities: Entities.ITableEntity[] = TableEntityProcessor.convertDocumentsToEntities(d.resources); - let finalEntities: IListTableEntitiesSegmentedResult = { - Results: entities, - ContinuationToken: this._documentIterator.hasMoreResults(), - }; - c = finalEntities; - - // .fetchNext() - // .then((response) => response.resources) - // .then((documents: any[]) => { - // let entities: Entities.ITableEntity[] = TableEntityProcessor.convertDocumentsToEntities(documents); - // let finalEntities: IListTableEntitiesSegmentedResult = { - // Results: entities, - // ContinuationToken: this._documentIterator.hasMoreResults(), - // }; - // return Promise.resolve(finalEntities); - // }); - } else { - c = await this.queryTablesTab.container.tableDataClient.queryDocuments( - this.queryTablesTab.collection, - this.sqlQuery(), - true - ); - } - - // const c = await this.queryTablesTab.container.tableDataClient.queryDocuments( - // this.queryTablesTab.collection, - // this.sqlQuery(), - // true - // ); - const result = c; - if (result) { - if (!this._documentIterator) { - this._documentIterator = result.iterator; - } - var actualDownloadSize: number = 0; - var entities = result.Results; - actualDownloadSize = entities.length; - - this.continuationToken = this.isCancelled ? null : result.ContinuationToken; - - if (!this.continuationToken) { - this.allDownloaded = true; - } - if (this.isCacheValid(this.tableQuery)) { - // Append to cache. - this.cache.data = this.cache.data.concat(entities.slice(0)); - } else { - // Create cache. - this.cache.data = entities; - console.log( - "🚀 ~ file: TableEntityListViewModel.ts ~ line 797 ~ TableEntityListViewModel ~ .then ~ this.cache.data", - this.cache.data - ); - } - - this.cache.tableQuery = this.tableQuery; - this.cache.serverCallInProgress = false; - - var nextDownloadSize: number = this.downloadSize - actualDownloadSize; - if (nextDownloadSize === 0 && this.tableQuery.top) { - this.allDownloaded = true; - } - if (this.allDownloaded || nextDownloadSize === 0) { - return Promise.resolve(this.cache.data); - } - return this.c(this.tableQuery, nextDownloadSize); - } - console.log("🚀 ~ file: TableEntityListViewModel.ts ~ line 161 ~ TableEntityListViewModel ~ c ~ data", c); - return this.cache.data; - } - /** * This callback function called by datatable to fetch the next page of data and render. * sSource - ajax URL of data source, ignored in our case as we are not using ajax. @@ -440,7 +337,6 @@ export default class TableEntityListViewModel extends DataTableViewModel { tablePageSize: number, downloadSize: number ): Promise { - console.log("🚀 ~ file: TableEntityListViewModel.ts ~ line 366 ~ TableEntityListViewModel ~ prefetchAndRender"); this.queryErrorMessage(null); if (this.cache.serverCallInProgress) { return undefined; @@ -452,27 +348,15 @@ export default class TableEntityListViewModel extends DataTableViewModel { } // Cache is assigned using prefetchData var entities = this.cache.data; - console.log( - "🚀 ~ file: TableEntityListViewModel.ts ~ line 430 ~ TableEntityListViewModel ~ .then ~ entities outside", - entities - ); if (userContext.apiType === "Cassandra" && DataTableUtilities.checkForDefaultHeader(this.headers)) { (this.queryTablesTab.container.tableDataClient) .getTableSchema(this.queryTablesTab.collection) .then((headers: CassandraTableKey[]) => { - console.log( - "🚀 ~ file: TableEntityListViewModel.ts ~ line 438 ~ TableEntityListViewModel ~ .then ~ headers", - headers - ); this.updateHeaders( headers.map((header) => header.property), true ); }); - console.log( - "🚀 ~ file: TableEntityListViewModel.ts ~ line 430 ~ TableEntityListViewModel ~ .then ~ entities inside", - entities - ); } else { var selectedHeadersUnion: string[] = DataTableUtilities.getPropertyIntersectionFromTableEntities( entities, @@ -514,9 +398,8 @@ export default class TableEntityListViewModel extends DataTableViewModel { ); this.queryTablesTab.onLoadStartKey = null; } - DataTableUtilities.turnOffProgressIndicator(); } - // return undefined; + return undefined; } /** @@ -536,7 +419,6 @@ export default class TableEntityListViewModel extends DataTableViewModel { downloadSize: number, currentRetry: number = 0 ): Promise { - console.log("🚀 ~ file: TableEntityListViewModel.ts ~ line 456 ~ TableEntityListViewModel ~ prefetchData"); var entities: any; if (!this.cache.serverCallInProgress) { this.cache.serverCallInProgress = true; @@ -544,7 +426,6 @@ export default class TableEntityListViewModel extends DataTableViewModel { this.lastPrefetchTime = new Date().getTime(); var time = this.lastPrefetchTime; - var promise: Promise; try { if (this._documentIterator && this.continuationToken) { // TODO handle Cassandra case @@ -558,7 +439,7 @@ export default class TableEntityListViewModel extends DataTableViewModel { }; entities = finalEntities; } else if (this.continuationToken && userContext.apiType === "Cassandra") { - entities = this.queryTablesTab.container.tableDataClient.queryDocuments( + entities = await this.queryTablesTab.container.tableDataClient.queryDocuments( this.queryTablesTab.collection, this.cqlQuery(), true, @@ -603,10 +484,6 @@ export default class TableEntityListViewModel extends DataTableViewModel { if (this.isCacheValid(tableQuery)) { // Append to cache. this.cache.data = this.cache.data.concat(entities.slice(0)); - console.log( - "🚀 ~ file: TableEntityListViewModel.ts ~ line 667 ~ TableEntityListViewModel ~ .then ~ this.cache.data", - this.cache.data - ); var p = new Promise((resolve) => { if (this.cache.data) { resolve(this.cache.data); @@ -616,10 +493,6 @@ export default class TableEntityListViewModel extends DataTableViewModel { } else { // Create cache. this.cache.data = entities; - console.log( - "🚀 ~ file: TableEntityListViewModel.ts ~ line 671 ~ TableEntityListViewModel ~ .then ~ this.cache.data", - this.cache.data - ); } this.cache.tableQuery = tableQuery; diff --git a/src/Explorer/Tables/QueryBuilder/QueryViewModel.tsx b/src/Explorer/Tables/QueryBuilder/QueryViewModel.tsx index c9481ff8c..b0fbcbd55 100644 --- a/src/Explorer/Tables/QueryBuilder/QueryViewModel.tsx +++ b/src/Explorer/Tables/QueryBuilder/QueryViewModel.tsx @@ -170,17 +170,13 @@ export default class QueryViewModel { this._tableEntityListViewModel.oDataQuery(filter); this._tableEntityListViewModel.sqlQuery(this.setSqlFilter(queryTableRows)); this._tableEntityListViewModel.cqlQuery(filter); - console.log( - "🚀 ~ file: QueryViewModel.tsx ~ line 165 ~ QueryViewModel ~ this._tableEntityListViewModel.sqlQuery()", - this._tableEntityListViewModel.sqlQuery() - ); return userContext.apiType !== "Cassandra" ? this._tableEntityListViewModel.sqlQuery() : this._tableEntityListViewModel.cqlQuery(); }; - public clearQuery = (): DataTables.DataTable => { + public clearQuery = (): void => { this.queryText(); this.topValue(); this.selectText(); @@ -198,7 +194,6 @@ export default class QueryViewModel { this.queryTablesTab.collection.id() )}` ); - return this._tableEntityListViewModel.reloadTable(false); }; public selectQueryOptions(headers: string[], getSelectMessage: (selectMessage: string) => void): void { diff --git a/src/Explorer/Tabs/QueryTablesTab/QueryTablesTabComponent.tsx b/src/Explorer/Tabs/QueryTablesTab/QueryTablesTabComponent.tsx index 70ad1d854..406cdaaaa 100644 --- a/src/Explorer/Tabs/QueryTablesTab/QueryTablesTabComponent.tsx +++ b/src/Explorer/Tabs/QueryTablesTab/QueryTablesTabComponent.tsx @@ -243,7 +243,6 @@ class QueryTablesTabComponent extends Component { - // console.log("Items > ", this.state.tableEntityListViewModel.items()); - // }, 10000); - // await tableEntityListViewModel.renderNextPageAndupdateCache(selectedQueryText); - // setTimeout(() => { - // // console.log("Processing..."); - // this.isEntitiesAvailable(isInitialLoad); - // }, 0); if (!isRunQuery) { try { documents = await tableEntityListViewModel.renderNextPageAndupdateCache(); - // setTimeout(() => { - // // console.log("Processing..."); - // this.isEntitiesAvailable(isInitialLoad); - // }, 0); - // const data = await tableEntityListViewModel.a(); if (userContext.apiType === "Cassandra") { - // console.log( - // "🚀 ~ file: QueryTablesTabComponent.tsx ~ line 311 ~ QueryTablesTabComponent ~ loadEntities ~ data", - // documents.Results - // ); - headers = documents.Results?.length - ? this.getFormattedHeaders(documents.Results) - : ["userid", "name", "email"]; - this.setupIntialEntities(headers, documents.Results, isInitialLoad); + headers = documents?.length ? this.getFormattedHeaders(documents) : ["userid", "name", "email"]; + this.setupIntialEntities(headers, documents, isInitialLoad); } else { - // console.log( - // "🚀 ~ file: QueryTablesTabComponent.tsx ~ line 311 ~ QueryTablesTabComponent ~ loadEntities ~ data", - // documents - // ); - headers = documents.Results?.length - ? this.getFormattedHeaders(documents.Results) - : ["RowKey", "PartitionKey", "Timestamp"]; + headers = documents?.length ? this.getFormattedHeaders(documents) : ["RowKey", "PartitionKey", "Timestamp"]; this.setupIntialEntities(headers, documents, isInitialLoad); } - // this.isEntitiesAvailable(isInitialLoad, data); } catch (error) { this.setState({ queryErrorMessage: error.responseText, @@ -415,9 +382,6 @@ class QueryTablesTabComponent extends Component { - // return
{item[header] ? item[header] : " "}
; - // }, }); }); @@ -455,36 +419,6 @@ class QueryTablesTabComponent extends Component { - // if (isInitialLoad && headers.length > 0) { - // this.loadFilterExample(); - // this.onItemsSelectionChanged(true); - // } - // } - // ); - // } - private onColumnClick = (ev: React.MouseEvent, column: IColumn): void => { const { columns, items } = this.state; const newColumns: IColumn[] = columns.slice(); @@ -640,10 +574,6 @@ class QueryTablesTabComponent extends Component { - // console.log( - // "🚀 ~ file: QueryTablesTabComponent.tsx ~ line 651 ~ QueryTablesTabComponent ~ runQuery ~ selectedQueryText", - // this.state.selectedQueryText - // ); this.loadEntities(false, queryTableRows.length > 0 ? true : false); }, 2000); this.setState({ @@ -1147,53 +1077,55 @@ class QueryTablesTabComponent extends Component {this.state.items.length > 0 && !this.state.isLoading && ( <> -
    -
  • -
    - this.handlePagination("FIRST", this.state.originalItems, this.state.currentStartIndex, PAGESIZE) - } - > - First -
    -
  • -
  • -
    - this.handlePagination( - "PREVIOUS", - this.state.originalItems, - this.state.currentStartIndex, - PAGESIZE - ) - } - > - Previous -
    -
  • -
  • -
    - this.handlePagination("NEXT", this.state.originalItems, this.state.currentStartIndex, PAGESIZE) - } - > - Next -
    -
  • -
  • -
    - this.handlePagination("LAST", this.state.originalItems, this.state.currentStartIndex, PAGESIZE) - } - > - Last -
    -
  • -
+ {this.state.originalItems.length > PAGESIZE && ( +
    +
  • +
    + this.handlePagination("FIRST", this.state.originalItems, this.state.currentStartIndex, PAGESIZE) + } + > + First +
    +
  • +
  • +
    + this.handlePagination( + "PREVIOUS", + this.state.originalItems, + this.state.currentStartIndex, + PAGESIZE + ) + } + > + Previous +
    +
  • +
  • +
    + this.handlePagination("NEXT", this.state.originalItems, this.state.currentStartIndex, PAGESIZE) + } + > + Next +
    +
  • +
  • +
    + this.handlePagination("LAST", this.state.originalItems, this.state.currentStartIndex, PAGESIZE) + } + > + Last +
    +
  • +
+ )} Results {this.state.fromDocument + 1} -{" "} {this.state.originalItems.length >= this.state.toDocument