mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-19 00:41: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 { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||||
import { queryDocuments } from "Common/dataAccess/queryDocuments";
|
import { queryDocuments } from "Common/dataAccess/queryDocuments";
|
||||||
import { readDocument } from "Common/dataAccess/readDocument";
|
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 { useDialog } from "Explorer/Controls/Dialog";
|
||||||
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
||||||
|
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
import { querySampleDocuments, readSampleDocument } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
import { querySampleDocuments, readSampleDocument } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||||
import DocumentsTab from "Explorer/Tabs/DocumentsTab";
|
import DocumentsTab from "Explorer/Tabs/DocumentsTab";
|
||||||
import { dataExplorerLightTheme } from "Explorer/Theme/ThemeUtil";
|
import { dataExplorerLightTheme } from "Explorer/Theme/ThemeUtil";
|
||||||
|
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||||
import { QueryConstants } from "Shared/Constants";
|
import { QueryConstants } from "Shared/Constants";
|
||||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
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 { 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 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 Constants from "../../../Common/Constants";
|
||||||
import * as HeadersUtility from "../../../Common/HeadersUtility";
|
import * as HeadersUtility from "../../../Common/HeadersUtility";
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
@@ -42,6 +50,10 @@ export class DocumentsTabV2 extends TabsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
|
if (!this.isActive) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DocumentsTabComponent
|
<DocumentsTabComponent
|
||||||
isPreferredApiMongoDB={undefined}
|
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<{
|
const DocumentsTabComponent: React.FunctionComponent<{
|
||||||
isPreferredApiMongoDB: boolean;
|
isPreferredApiMongoDB: boolean;
|
||||||
documentIds: DocumentId[]; // TODO: this contains ko observables. We need to convert them to React state.
|
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 [isExecutionError, setIsExecutionError] = useState<boolean>(false);
|
||||||
const [onLoadStartKey, setOnLoadStartKey] = useState<number>(props.onLoadStartKey);
|
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 = {
|
const applyFilterButton = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
visible: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const documentContentsContainerId = `documentContentsContainer${props.tabId}`;
|
const documentContentsContainerId = `documentContentsContainer${props.tabId}`;
|
||||||
@@ -119,6 +269,12 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
|||||||
[props.isPreferredApiMongoDB],
|
[props.isPreferredApiMongoDB],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const updateNavbarWithTabsButtons = (): void => {
|
||||||
|
// if (this.isActive()) {
|
||||||
|
useCommandBar.getState().setContextButtons(getTabsButtons());
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDocumentIds(props.documentIds);
|
setDocumentIds(props.documentIds);
|
||||||
}, [props.documentIds]);
|
}, [props.documentIds]);
|
||||||
@@ -152,14 +308,48 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateNavbarWithTabsButtons();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// If editor state changes, update the nav
|
||||||
|
useEffect(() => updateNavbarWithTabsButtons(), [editorState]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (documentsIterator) {
|
if (documentsIterator) {
|
||||||
loadNextPage(documentsIterator.applyFilterButtonPressed);
|
loadNextPage(documentsIterator.applyFilterButtonPressed);
|
||||||
}
|
}
|
||||||
}, [documentsIterator]);
|
}, [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 = () => {
|
const onShowFilterClick = () => {
|
||||||
setIsFilterCreated(true);
|
setIsFilterCreated(true);
|
||||||
setIsFilterExpanded(true);
|
setIsFilterExpanded(true);
|
||||||
@@ -423,6 +613,108 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
|||||||
return true;
|
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 =
|
const _isQueryCopilotSampleContainer =
|
||||||
props.collection?.isSampleCollection &&
|
props.collection?.isSampleCollection &&
|
||||||
props.collection?.databaseId === QueryCopilotSampleDatabaseId &&
|
props.collection?.databaseId === QueryCopilotSampleDatabaseId &&
|
||||||
@@ -441,22 +733,76 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
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();
|
case ViewModels.DocumentExplorerState.newDocumentValid:
|
||||||
// TODO: Check if editor is dirty
|
case ViewModels.DocumentExplorerState.newDocumentInvalid:
|
||||||
const readSingleDocument = (documentId: DocumentId) =>
|
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(
|
(_isQueryCopilotSampleContainer ? readSampleDocument(documentId) : readDocument(props.collection, documentId)).then(
|
||||||
(content) => {
|
(content) => {
|
||||||
// this.initDocumentEditor(documentId, content);
|
initDocumentEditor(documentId, content);
|
||||||
setCurrentDocument(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 tableContainerRef = useRef(null);
|
||||||
const [tableContainerSizePx, setTableContainerSizePx] = useState<{ height: number; width: number }>(undefined);
|
const [tableContainerSizePx, setTableContainerSizePx] = useState<{ height: number; width: number }>(undefined);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -624,11 +970,12 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
|||||||
{/* <Split> doesn't like to be a flex child */}
|
{/* <Split> doesn't like to be a flex child */}
|
||||||
<div style={{ overflow: "hidden", height: "100%" }}>
|
<div style={{ overflow: "hidden", height: "100%" }}>
|
||||||
<Split>
|
<Split>
|
||||||
<div style={{ minWidth: 480, width: "20%" }} ref={tableContainerRef}>
|
<div style={{ minWidth: 200, width: "20%" }} ref={tableContainerRef}>
|
||||||
<DocumentsTableComponent
|
<DocumentsTableComponent
|
||||||
style={{ width: 200 }}
|
style={{ width: 200 }}
|
||||||
items={tableItems}
|
items={tableItems}
|
||||||
onSelectedItem={onSelectedDocument}
|
onItemClicked={onDocumentClicked}
|
||||||
|
onSelectedItemsChange={onDocumentsSelectionChange}
|
||||||
size={tableContainerSizePx}
|
size={tableContainerSizePx}
|
||||||
columnHeaders={columnHeaders}
|
columnHeaders={columnHeaders}
|
||||||
/>
|
/>
|
||||||
@@ -637,15 +984,17 @@ const DocumentsTabComponent: React.FunctionComponent<{
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ minWidth: "20%", width: "100%" }}>
|
<div style={{ minWidth: "20%", width: "100%" }}>
|
||||||
|
{shouldShowEditor && (
|
||||||
<EditorReact
|
<EditorReact
|
||||||
language={"json"}
|
language={"json"}
|
||||||
content={JSON.stringify(currentDocument, undefined, " ")}
|
content={initialDocumentContent}
|
||||||
isReadOnly={false}
|
isReadOnly={false}
|
||||||
ariaLabel={"Stored procedure body"}
|
ariaLabel={"Document editor"}
|
||||||
lineNumbers={"on"}
|
lineNumbers={"on"}
|
||||||
theme={"_theme"}
|
theme={"_theme"}
|
||||||
onContentChanged={(newContent: string) => { }}
|
onContentChanged={(newContent: string) => {}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Split>
|
</Split>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ export type ColumnHeaders = {
|
|||||||
};
|
};
|
||||||
export interface IDocumentsTableComponentProps {
|
export interface IDocumentsTableComponentProps {
|
||||||
items: DocumentsTableComponentItem[];
|
items: DocumentsTableComponentItem[];
|
||||||
onSelectedItem: (index: number) => void;
|
onItemClicked: (index: number) => void;
|
||||||
|
onSelectedItemsChange: (selectedItemsIndices: Set<number>) => void;
|
||||||
size: { height: number; width: number };
|
size: { height: number; width: number };
|
||||||
columnHeaders: ColumnHeaders;
|
columnHeaders: ColumnHeaders;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
@@ -55,7 +56,8 @@ interface ReactWindowRenderFnProps extends ListChildComponentProps {
|
|||||||
|
|
||||||
export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> = ({
|
export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> = ({
|
||||||
items,
|
items,
|
||||||
onSelectedItem,
|
onItemClicked,
|
||||||
|
onSelectedItemsChange,
|
||||||
style,
|
style,
|
||||||
size,
|
size,
|
||||||
columnHeaders,
|
columnHeaders,
|
||||||
@@ -65,7 +67,6 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
|
|||||||
|
|
||||||
const [activeItemIndex, setActiveItemIndex] = React.useState<number>(undefined);
|
const [activeItemIndex, setActiveItemIndex] = React.useState<number>(undefined);
|
||||||
|
|
||||||
|
|
||||||
const initialSizingOptions: TableColumnSizingOptions = {
|
const initialSizingOptions: TableColumnSizingOptions = {
|
||||||
id: {
|
id: {
|
||||||
idealWidth: 280,
|
idealWidth: 280,
|
||||||
@@ -93,6 +94,13 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
|
|||||||
|
|
||||||
const [selectedRows, setSelectedRows] = React.useState<Set<TableRowId>>(() => new Set<TableRowId>([0]));
|
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
|
// 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(
|
const columns: TableColumnDefinition<DocumentsTableComponentItem>[] = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -203,7 +211,7 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
|
|||||||
if (selectedRows.size === 1 && items.length > 0) {
|
if (selectedRows.size === 1 && items.length > 0) {
|
||||||
const newActiveItemIndex = selectedRows.values().next().value;
|
const newActiveItemIndex = selectedRows.values().next().value;
|
||||||
if (newActiveItemIndex !== activeItemIndex) {
|
if (newActiveItemIndex !== activeItemIndex) {
|
||||||
onSelectedItem(newActiveItemIndex);
|
onItemClicked(newActiveItemIndex);
|
||||||
setActiveItemIndex(newActiveItemIndex);
|
setActiveItemIndex(newActiveItemIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user