mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-18 16:31:31 +00:00
Add nav buttons
This commit is contained in:
@@ -5,19 +5,27 @@ import { KeyCodes, QueryCopilotSampleContainerId, QueryCopilotSampleDatabaseId }
|
||||
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||
import { queryDocuments } from "Common/dataAccess/queryDocuments";
|
||||
import { readDocument } from "Common/dataAccess/readDocument";
|
||||
import { Platform, configContext } from "ConfigContext";
|
||||
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||
import { useDialog } from "Explorer/Controls/Dialog";
|
||||
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
||||
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import { querySampleDocuments, readSampleDocument } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||
import DocumentsTab from "Explorer/Tabs/DocumentsTab";
|
||||
import { dataExplorerLightTheme } from "Explorer/Theme/ThemeUtil";
|
||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||
import { QueryConstants } from "Shared/Constants";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { userContext } from "UserContext";
|
||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||
import React, { KeyboardEventHandler, useEffect, useMemo, useRef, useState } from "react";
|
||||
import React, { KeyboardEventHandler, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { format } from "react-string-format";
|
||||
import DeleteDocumentIcon from "../../../../images/DeleteDocument.svg";
|
||||
import NewDocumentIcon from "../../../../images/NewDocument.svg";
|
||||
import CloseIcon from "../../../../images/close-black.svg";
|
||||
import DiscardIcon from "../../../../images/discard.svg";
|
||||
import SaveIcon from "../../../../images/save-cosmos.svg";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import * as HeadersUtility from "../../../Common/HeadersUtility";
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
@@ -42,6 +50,10 @@ export class DocumentsTabV2 extends TabsBase {
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
if (!this.isActive) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<DocumentsTabComponent
|
||||
isPreferredApiMongoDB={undefined}
|
||||
@@ -61,6 +73,10 @@ export class DocumentsTabV2 extends TabsBase {
|
||||
}
|
||||
}
|
||||
|
||||
// From TabsBase.renderObjectForEditor()
|
||||
const renderObjectForEditor = (value: unknown, replacer: unknown, space: string | number): string =>
|
||||
JSON.stringify(value, replacer, space);
|
||||
|
||||
const DocumentsTabComponent: React.FunctionComponent<{
|
||||
isPreferredApiMongoDB: boolean;
|
||||
documentIds: DocumentId[]; // TODO: this contains ko observables. We need to convert them to React state.
|
||||
@@ -97,11 +113,145 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
||||
const [isExecutionError, setIsExecutionError] = useState<boolean>(false);
|
||||
const [onLoadStartKey, setOnLoadStartKey] = useState<number>(props.onLoadStartKey);
|
||||
|
||||
const [currentDocument, setCurrentDocument] = useState<unknown>(undefined);
|
||||
const [selectedDocumentContent, setSelectedDocumentContent] = useState<unknown>(undefined);
|
||||
const [selectedDocumentContentBaseline, setSelectedDocumentContentBaseline] = useState<unknown>(undefined);
|
||||
|
||||
// Command buttons
|
||||
const [editorState, setEditorState] = useState<ViewModels.DocumentExplorerState>(
|
||||
ViewModels.DocumentExplorerState.noDocumentSelected,
|
||||
);
|
||||
|
||||
// Editor
|
||||
const [initialDocumentContent, setInitialDocumentContent] = useState<string>(undefined);
|
||||
|
||||
const getNewDocumentButtonState = () => ({
|
||||
enabled: (() => {
|
||||
switch (editorState) {
|
||||
case ViewModels.DocumentExplorerState.noDocumentSelected:
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentNoEdits:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
visible: true,
|
||||
});
|
||||
|
||||
const getSaveNewDocumentButtonState = () => ({
|
||||
enabled: (() => {
|
||||
switch (editorState) {
|
||||
case ViewModels.DocumentExplorerState.newDocumentValid:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
|
||||
visible: (() => {
|
||||
switch (editorState) {
|
||||
case ViewModels.DocumentExplorerState.newDocumentValid:
|
||||
case ViewModels.DocumentExplorerState.newDocumentInvalid:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
});
|
||||
|
||||
const getDiscardNewDocumentChangesButtonState = () => ({
|
||||
enabled: (() => {
|
||||
switch (editorState) {
|
||||
case ViewModels.DocumentExplorerState.newDocumentValid:
|
||||
case ViewModels.DocumentExplorerState.newDocumentInvalid:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
|
||||
visible: (() => {
|
||||
switch (editorState) {
|
||||
case ViewModels.DocumentExplorerState.newDocumentValid:
|
||||
case ViewModels.DocumentExplorerState.newDocumentInvalid:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
});
|
||||
|
||||
const getSaveExistingDocumentButtonState = () => ({
|
||||
enabled: (() => {
|
||||
switch (editorState) {
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyValid:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
|
||||
visible: (() => {
|
||||
switch (editorState) {
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentNoEdits:
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyInvalid:
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyValid:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
});
|
||||
|
||||
const getDiscardExisitingDocumentChangesButtonState = () => ({
|
||||
enabled: (() => {
|
||||
switch (editorState) {
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyInvalid:
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyValid:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
|
||||
visible: (() => {
|
||||
switch (editorState) {
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentNoEdits:
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyInvalid:
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyValid:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
});
|
||||
|
||||
const getDeleteExisitingDocumentButtonState = () => ({
|
||||
enabled: (() => {
|
||||
switch (editorState) {
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentNoEdits:
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyInvalid:
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyValid:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
|
||||
visible: (() => {
|
||||
switch (editorState) {
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentNoEdits:
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyInvalid:
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyValid:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
});
|
||||
|
||||
// TODO remove this?
|
||||
const applyFilterButton = {
|
||||
enabled: true,
|
||||
visible: true,
|
||||
};
|
||||
|
||||
const documentContentsContainerId = `documentContentsContainer${props.tabId}`;
|
||||
@@ -119,6 +269,12 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
||||
[props.isPreferredApiMongoDB],
|
||||
);
|
||||
|
||||
const updateNavbarWithTabsButtons = (): void => {
|
||||
// if (this.isActive()) {
|
||||
useCommandBar.getState().setContextButtons(getTabsButtons());
|
||||
// }
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setDocumentIds(props.documentIds);
|
||||
}, [props.documentIds]);
|
||||
@@ -152,14 +308,48 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateNavbarWithTabsButtons();
|
||||
}, []);
|
||||
|
||||
// If editor state changes, update the nav
|
||||
useEffect(() => updateNavbarWithTabsButtons(), [editorState]);
|
||||
|
||||
useEffect(() => {
|
||||
if (documentsIterator) {
|
||||
loadNextPage(documentsIterator.applyFilterButtonPressed);
|
||||
}
|
||||
}, [documentsIterator]);
|
||||
|
||||
useEffect(() => {
|
||||
setShouldShowEditor(!!selectedDocumentContent);
|
||||
}, [selectedDocumentContent]);
|
||||
|
||||
const onNewDocumentClick = useCallback((): void => {
|
||||
if (isEditorDirty()) {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkCancelModalDialog(
|
||||
"Unsaved changes",
|
||||
"Changes will be lost. Do you want to continue?",
|
||||
"OK",
|
||||
() => initializeNewDocument(),
|
||||
"Cancel",
|
||||
undefined,
|
||||
);
|
||||
} else {
|
||||
initializeNewDocument();
|
||||
}
|
||||
}, [editorState /* TODO isEditorDirty depends on more than just editorState */]);
|
||||
|
||||
const initializeNewDocument = (): void => {
|
||||
this.selectedDocumentId(null);
|
||||
const defaultDocument: string = renderObjectForEditor({ id: "replace_with_new_document_id" }, null, 4);
|
||||
this.initialDocumentContent(defaultDocument);
|
||||
this.selectedDocumentContent.setBaseline(defaultDocument);
|
||||
this.editorState(ViewModels.DocumentExplorerState.newDocumentValid);
|
||||
};
|
||||
|
||||
const onShowFilterClick = () => {
|
||||
setIsFilterCreated(true);
|
||||
setIsFilterExpanded(true);
|
||||
@@ -423,6 +613,108 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
||||
return true;
|
||||
})();
|
||||
|
||||
const getTabsButtons = useCallback((): CommandButtonComponentProps[] => {
|
||||
if (configContext.platform === Platform.Fabric && userContext.fabricContext?.isReadOnly) {
|
||||
// All the following buttons require write access
|
||||
return [];
|
||||
}
|
||||
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
const label = !isPreferredApiMongoDB ? "New Item" : "New Document";
|
||||
if (getNewDocumentButtonState().visible) {
|
||||
buttons.push({
|
||||
iconSrc: NewDocumentIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: onNewDocumentClick,
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: false,
|
||||
disabled: !getNewDocumentButtonState().enabled || useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||
id: "mongoNewDocumentBtn",
|
||||
});
|
||||
}
|
||||
|
||||
if (getSaveNewDocumentButtonState().visible) {
|
||||
const label = "Save";
|
||||
buttons.push({
|
||||
iconSrc: SaveIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: undefined, // TODO implement: onSaveNewDocumentClick,
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: false,
|
||||
disabled:
|
||||
getSaveNewDocumentButtonState().enabled || useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||
});
|
||||
}
|
||||
|
||||
if (getDiscardNewDocumentChangesButtonState().visible) {
|
||||
const label = "Discard";
|
||||
buttons.push({
|
||||
iconSrc: DiscardIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: undefined, // TODO implement: onRevertNewDocumentClick,
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: false,
|
||||
disabled:
|
||||
!getDiscardNewDocumentChangesButtonState().enabled ||
|
||||
useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||
});
|
||||
}
|
||||
|
||||
if (getSaveExistingDocumentButtonState().visible) {
|
||||
const label = "Update";
|
||||
buttons.push({
|
||||
iconSrc: SaveIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: undefined, // TODO implement: onSaveExistingDocumentClick,
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: false,
|
||||
disabled:
|
||||
!getSaveExistingDocumentButtonState().enabled ||
|
||||
useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||
});
|
||||
}
|
||||
|
||||
if (getDiscardExisitingDocumentChangesButtonState().visible) {
|
||||
const label = "Discard";
|
||||
buttons.push({
|
||||
iconSrc: DiscardIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: undefined, // TODO implement: onRevertExisitingDocumentClick,
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: false,
|
||||
disabled:
|
||||
!getDiscardExisitingDocumentChangesButtonState().enabled ||
|
||||
useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||
});
|
||||
}
|
||||
|
||||
if (getDeleteExisitingDocumentButtonState().visible) {
|
||||
const label = "Delete";
|
||||
buttons.push({
|
||||
iconSrc: DeleteDocumentIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: undefined, // TODO implement: onDeleteExisitingDocumentClick,
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: false,
|
||||
disabled:
|
||||
!getDeleteExisitingDocumentButtonState().enabled ||
|
||||
useSelectedNode.getState().isQueryCopilotCollectionSelected(),
|
||||
});
|
||||
}
|
||||
|
||||
if (!isPreferredApiMongoDB) {
|
||||
buttons.push(DocumentsTab._createUploadButton(props.collection.container));
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}, [editorState]);
|
||||
|
||||
const _isQueryCopilotSampleContainer =
|
||||
props.collection?.isSampleCollection &&
|
||||
props.collection?.databaseId === QueryCopilotSampleDatabaseId &&
|
||||
@@ -441,22 +733,76 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
||||
}
|
||||
|
||||
return item;
|
||||
// TODO: for now, merge all the pk values into a single string/column
|
||||
// type: documentId.partitionKeyProperties ? documentId.stringPartitionKeyValues.join(",") : undefined,
|
||||
});
|
||||
|
||||
const onSelectedDocument = (index: number) => readSingleDocument(documentIds[index]);
|
||||
const isEditorDirty = (): boolean => {
|
||||
switch (editorState) {
|
||||
case ViewModels.DocumentExplorerState.noDocumentSelected:
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentNoEdits:
|
||||
return false;
|
||||
|
||||
// TODO: replicate logic of selectedDocument.click();
|
||||
// TODO: Check if editor is dirty
|
||||
const readSingleDocument = (documentId: DocumentId) =>
|
||||
case ViewModels.DocumentExplorerState.newDocumentValid:
|
||||
case ViewModels.DocumentExplorerState.newDocumentInvalid:
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyInvalid:
|
||||
return true;
|
||||
|
||||
case ViewModels.DocumentExplorerState.exisitingDocumentDirtyValid:
|
||||
return (
|
||||
this.selectedDocumentContent.getEditableOriginalValue() !==
|
||||
this.selectedDocumentContent.getEditableCurrentValue()
|
||||
);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* replicate logic of selectedDocument.click();
|
||||
* Document has been clicked on in table
|
||||
* @param index
|
||||
*/
|
||||
const onDocumentClicked = (index: number) => {
|
||||
if (isEditorDirty()) {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkCancelModalDialog(
|
||||
"Unsaved changes",
|
||||
"Your unsaved changes will be lost. Do you want to continue?",
|
||||
"OK",
|
||||
() => loadDocument(documentIds[index]),
|
||||
"Cancel",
|
||||
undefined,
|
||||
);
|
||||
} else {
|
||||
loadDocument(documentIds[index]);
|
||||
}
|
||||
};
|
||||
|
||||
const onDocumentsSelectionChange = (selectedItemsIndices: Set<number>) => {
|
||||
// TODO: Update some state?
|
||||
};
|
||||
|
||||
const loadDocument = (documentId: DocumentId) =>
|
||||
(_isQueryCopilotSampleContainer ? readSampleDocument(documentId) : readDocument(props.collection, documentId)).then(
|
||||
(content) => {
|
||||
// this.initDocumentEditor(documentId, content);
|
||||
setCurrentDocument(content);
|
||||
initDocumentEditor(documentId, content);
|
||||
setSelectedDocumentContent(content);
|
||||
},
|
||||
);
|
||||
|
||||
const initDocumentEditor = (documentId: DocumentId, documentContent: unknown): void => {
|
||||
if (documentId) {
|
||||
const content: string = renderObjectForEditor(documentContent, null, 4);
|
||||
setSelectedDocumentContentBaseline(content);
|
||||
setInitialDocumentContent(content);
|
||||
const newState = documentId
|
||||
? ViewModels.DocumentExplorerState.exisitingDocumentNoEdits
|
||||
: ViewModels.DocumentExplorerState.newDocumentValid;
|
||||
setEditorState(newState);
|
||||
}
|
||||
};
|
||||
|
||||
const tableContainerRef = useRef(null);
|
||||
const [tableContainerSizePx, setTableContainerSizePx] = useState<{ height: number; width: number }>(undefined);
|
||||
useEffect(() => {
|
||||
@@ -505,7 +851,7 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
||||
<button
|
||||
className="filterbtnstyle queryButton"
|
||||
onClick={onShowFilterClick}
|
||||
/*data-bind="click: onShowFilterClick"*/
|
||||
/*data-bind="click: onShowFilterClick"*/
|
||||
>
|
||||
Edit Filter
|
||||
</button>
|
||||
@@ -555,7 +901,7 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
||||
}
|
||||
value={filterContent}
|
||||
onChange={(e) => setFilterContent(e.target.value)}
|
||||
/*
|
||||
/*
|
||||
data-bind="
|
||||
W attr:{
|
||||
placeholder:isPreferredApiMongoDB?'Type a query predicate (e.g., {´a´:´foo´}), or choose one from the drop down list, or leave empty to query all documents.':'Type a query predicate (e.g., WHERE c.id=´1´), or choose one from the drop down list, or leave empty to query all documents.'
|
||||
@@ -608,7 +954,7 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
||||
tabIndex={0}
|
||||
onClick={() => onHideFilterClick()}
|
||||
onKeyDown={onCloseButtonKeyDown}
|
||||
/*data-bind="click: onHideFilterClick, event: { keydown: onCloseButtonKeyDown }"*/
|
||||
/*data-bind="click: onHideFilterClick, event: { keydown: onCloseButtonKeyDown }"*/
|
||||
>
|
||||
<img src={CloseIcon} style={{ height: 14, width: 14 }} alt="Hide filter" />
|
||||
</span>
|
||||
@@ -624,11 +970,12 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
||||
{/* <Split> doesn't like to be a flex child */}
|
||||
<div style={{ overflow: "hidden", height: "100%" }}>
|
||||
<Split>
|
||||
<div style={{ minWidth: 480, width: "20%" }} ref={tableContainerRef}>
|
||||
<div style={{ minWidth: 200, width: "20%" }} ref={tableContainerRef}>
|
||||
<DocumentsTableComponent
|
||||
style={{ width: 200 }}
|
||||
items={tableItems}
|
||||
onSelectedItem={onSelectedDocument}
|
||||
onItemClicked={onDocumentClicked}
|
||||
onSelectedItemsChange={onDocumentsSelectionChange}
|
||||
size={tableContainerSizePx}
|
||||
columnHeaders={columnHeaders}
|
||||
/>
|
||||
@@ -637,15 +984,17 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
||||
</a>
|
||||
</div>
|
||||
<div style={{ minWidth: "20%", width: "100%" }}>
|
||||
<EditorReact
|
||||
language={"json"}
|
||||
content={JSON.stringify(currentDocument, undefined, " ")}
|
||||
isReadOnly={false}
|
||||
ariaLabel={"Stored procedure body"}
|
||||
lineNumbers={"on"}
|
||||
theme={"_theme"}
|
||||
onContentChanged={(newContent: string) => { }}
|
||||
/>
|
||||
{shouldShowEditor && (
|
||||
<EditorReact
|
||||
language={"json"}
|
||||
content={initialDocumentContent}
|
||||
isReadOnly={false}
|
||||
ariaLabel={"Document editor"}
|
||||
lineNumbers={"on"}
|
||||
theme={"_theme"}
|
||||
onContentChanged={(newContent: string) => {}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Split>
|
||||
</div>
|
||||
|
||||
@@ -37,7 +37,8 @@ export type ColumnHeaders = {
|
||||
};
|
||||
export interface IDocumentsTableComponentProps {
|
||||
items: DocumentsTableComponentItem[];
|
||||
onSelectedItem: (index: number) => void;
|
||||
onItemClicked: (index: number) => void;
|
||||
onSelectedItemsChange: (selectedItemsIndices: Set<number>) => void;
|
||||
size: { height: number; width: number };
|
||||
columnHeaders: ColumnHeaders;
|
||||
style?: React.CSSProperties;
|
||||
@@ -55,7 +56,8 @@ interface ReactWindowRenderFnProps extends ListChildComponentProps {
|
||||
|
||||
export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> = ({
|
||||
items,
|
||||
onSelectedItem,
|
||||
onItemClicked,
|
||||
onSelectedItemsChange,
|
||||
style,
|
||||
size,
|
||||
columnHeaders,
|
||||
@@ -65,7 +67,6 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
|
||||
|
||||
const [activeItemIndex, setActiveItemIndex] = React.useState<number>(undefined);
|
||||
|
||||
|
||||
const initialSizingOptions: TableColumnSizingOptions = {
|
||||
id: {
|
||||
idealWidth: 280,
|
||||
@@ -93,6 +94,13 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
|
||||
|
||||
const [selectedRows, setSelectedRows] = React.useState<Set<TableRowId>>(() => new Set<TableRowId>([0]));
|
||||
|
||||
// If selected rows change, call props
|
||||
useEffect(() => {
|
||||
if (onSelectedItemsChange) {
|
||||
onSelectedItemsChange(selectedRows);
|
||||
}
|
||||
}, [selectedRows, onSelectedItemsChange]);
|
||||
|
||||
// Columns must be a static object and cannot change on re-renders otherwise React will complain about too many refreshes
|
||||
const columns: TableColumnDefinition<DocumentsTableComponentItem>[] = useMemo(
|
||||
() =>
|
||||
@@ -203,7 +211,7 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
|
||||
if (selectedRows.size === 1 && items.length > 0) {
|
||||
const newActiveItemIndex = selectedRows.values().next().value;
|
||||
if (newActiveItemIndex !== activeItemIndex) {
|
||||
onSelectedItem(newActiveItemIndex);
|
||||
onItemClicked(newActiveItemIndex);
|
||||
setActiveItemIndex(newActiveItemIndex);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user