mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-28 13:21:42 +00:00
Compare commits
4 Commits
cloudshell
...
ashleyst/m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08d3318a87 | ||
|
|
8b6d857ddb | ||
|
|
2598760a11 | ||
|
|
44d886b4a0 |
@@ -2296,6 +2296,17 @@ a:link {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.monaco-editor .quick-input-list-label {
|
||||||
|
/* Restore some of Monaco's default styles that are clobbered by our global styles */
|
||||||
|
padding: 0;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monaco-editor .quick-input-list .highlight {
|
||||||
|
/* Padding in highlighted text within the quick input list breaks the flow of the text */
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
td a {
|
td a {
|
||||||
color: #393939;
|
color: #393939;
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/Common/KeyboardShortcuts.ts
Normal file
25
src/Common/KeyboardShortcuts.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { KeyMap } from "react-hotkeys";
|
||||||
|
|
||||||
|
export const keyMap: KeyMap = {
|
||||||
|
NEW_QUERY: {
|
||||||
|
name: "New Query",
|
||||||
|
sequence: "ctrl+j",
|
||||||
|
action: "keydown",
|
||||||
|
},
|
||||||
|
CANCEL_QUERY: {
|
||||||
|
name: "Cancel Query",
|
||||||
|
sequence: "f8",
|
||||||
|
action: "keydown",
|
||||||
|
},
|
||||||
|
DISCARD: {
|
||||||
|
name: "Discard Changes",
|
||||||
|
sequence: "ctrl+x",
|
||||||
|
action: "keydown"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export type KeyboardShortcutName = keyof typeof keyMap;
|
||||||
|
|
||||||
|
export type KeyboardShortcutHandlers = Partial<{
|
||||||
|
[key in KeyboardShortcutName]: (keyEvent?: KeyboardEvent) => void;
|
||||||
|
}>;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* React component for Command button component.
|
* React component for Command button component.
|
||||||
*/
|
*/
|
||||||
|
import { KeyboardShortcutName } from "Common/KeyboardShortcuts";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import CollapseChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
|
import CollapseChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
|
||||||
import { KeyCodes } from "../../../Common/Constants";
|
import { KeyCodes } from "../../../Common/Constants";
|
||||||
@@ -30,7 +31,7 @@ export interface CommandButtonComponentProps {
|
|||||||
/**
|
/**
|
||||||
* Click handler for command button click
|
* Click handler for command button click
|
||||||
*/
|
*/
|
||||||
onCommandClick: (e: React.SyntheticEvent) => void;
|
onCommandClick: (e: React.SyntheticEvent | KeyboardEvent) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label for the button
|
* Label for the button
|
||||||
@@ -107,10 +108,16 @@ export interface CommandButtonComponentProps {
|
|||||||
* Vertical bar to divide buttons
|
* Vertical bar to divide buttons
|
||||||
*/
|
*/
|
||||||
isDivider?: boolean;
|
isDivider?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aria-label for the button
|
* Aria-label for the button
|
||||||
*/
|
*/
|
||||||
ariaLabel: string;
|
ariaLabel: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A keyboard shortcut that can be used to activate this button.
|
||||||
|
*/
|
||||||
|
keyboardShortcut?: KeyboardShortcutName;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandButtonComponent extends React.Component<CommandButtonComponentProps> {
|
export class CommandButtonComponent extends React.Component<CommandButtonComponentProps> {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Spinner, SpinnerSize } from "@fluentui/react";
|
import { Spinner, SpinnerSize } from "@fluentui/react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { loadMonaco, monaco } from "../../LazyMonaco";
|
import { MonacoNamespace, loadMonaco, monaco } from "../../LazyMonaco";
|
||||||
// import "./EditorReact.less";
|
// import "./EditorReact.less";
|
||||||
|
|
||||||
interface EditorReactStates {
|
interface EditorReactStates {
|
||||||
@@ -21,6 +21,7 @@ export interface EditorReactProps {
|
|||||||
minimap?: monaco.editor.IEditorOptions["minimap"];
|
minimap?: monaco.editor.IEditorOptions["minimap"];
|
||||||
scrollBeyondLastLine?: monaco.editor.IEditorOptions["scrollBeyondLastLine"];
|
scrollBeyondLastLine?: monaco.editor.IEditorOptions["scrollBeyondLastLine"];
|
||||||
monacoContainerStyles?: React.CSSProperties;
|
monacoContainerStyles?: React.CSSProperties;
|
||||||
|
configureEditor?: (monaco: MonacoNamespace, editor: monaco.editor.IStandaloneCodeEditor) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EditorReact extends React.Component<EditorReactProps, EditorReactStates> {
|
export class EditorReact extends React.Component<EditorReactProps, EditorReactStates> {
|
||||||
@@ -69,7 +70,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected configureEditor(editor: monaco.editor.IStandaloneCodeEditor) {
|
protected configureEditor(monaco: MonacoNamespace, editor: monaco.editor.IStandaloneCodeEditor) {
|
||||||
this.editor = editor;
|
this.editor = editor;
|
||||||
const queryEditorModel = this.editor.getModel();
|
const queryEditorModel = this.editor.getModel();
|
||||||
if (!this.props.isReadOnly && this.props.onContentChanged) {
|
if (!this.props.isReadOnly && this.props.onContentChanged) {
|
||||||
@@ -87,12 +88,16 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.configureEditor) {
|
||||||
|
this.props.configureEditor(monaco, this.editor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the monaco editor and attach to DOM
|
* Create the monaco editor and attach to DOM
|
||||||
*/
|
*/
|
||||||
private async createEditor(createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
|
private async createEditor(createCallback: (monaco: MonacoNamespace, e: monaco.editor.IStandaloneCodeEditor) => void) {
|
||||||
const options: monaco.editor.IStandaloneEditorConstructionOptions = {
|
const options: monaco.editor.IStandaloneEditorConstructionOptions = {
|
||||||
language: this.props.language,
|
language: this.props.language,
|
||||||
value: this.props.content,
|
value: this.props.content,
|
||||||
@@ -111,7 +116,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
|||||||
|
|
||||||
this.rootNode.innerHTML = "";
|
this.rootNode.innerHTML = "";
|
||||||
const monaco = await loadMonaco();
|
const monaco = await loadMonaco();
|
||||||
createCallback(monaco?.editor?.create(this.rootNode, options));
|
createCallback(monaco, monaco?.editor?.create(this.rootNode, options));
|
||||||
|
|
||||||
if (this.rootNode.innerHTML) {
|
if (this.rootNode.innerHTML) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|||||||
@@ -754,6 +754,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
keyboardShortcut: "DISCARD",
|
||||||
onCommandClick: this.onRevertClick,
|
onCommandClick: this.onRevertClick,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ export type { monaco };
|
|||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export const loadMonaco = () => import(/* webpackChunkName: "lazy-monaco" */ "monaco-editor/esm/vs/editor/editor.api");
|
export const loadMonaco = () => import(/* webpackChunkName: "lazy-monaco" */ "monaco-editor/esm/vs/editor/editor.api");
|
||||||
|
export type MonacoNamespace = Awaited<ReturnType<typeof loadMonaco>>;
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
* and update any knockout observables passed from the parent.
|
* and update any knockout observables passed from the parent.
|
||||||
*/
|
*/
|
||||||
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
|
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
|
||||||
|
import { keyMap } from "Common/KeyboardShortcuts";
|
||||||
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { GlobalHotKeys } from "react-hotkeys";
|
||||||
import create, { UseStore } from "zustand";
|
import create, { UseStore } from "zustand";
|
||||||
import { ConnectionStatusType, PoolIdType } from "../../../Common/Constants";
|
import { ConnectionStatusType, PoolIdType } from "../../../Common/Constants";
|
||||||
import { StyleConstants } from "../../../Common/StyleConstants";
|
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||||
@@ -105,8 +107,13 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlers = CommandBarUtil.createKeyboardHandlers(staticButtons.concat(contextButtons).concat(controlButtons));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="commandBarContainer" style={{ display: isHidden ? "none" : "initial" }}>
|
<div className="commandBarContainer" style={{ display: isHidden ? "none" : "initial" }}>
|
||||||
|
{/* Handles keyboard shortcuts for command bar buttons when focus is OUTSIDE monaco. Even though it's placed here in the DOM, it hooks keydown on 'document' */}
|
||||||
|
<GlobalHotKeys keyMap={keyMap} handlers={handlers} allowChanges={true} />
|
||||||
|
|
||||||
<FluentCommandBar
|
<FluentCommandBar
|
||||||
ariaLabel="Use left and right arrow keys to navigate between commands"
|
ariaLabel="Use left and right arrow keys to navigate between commands"
|
||||||
items={uiFabricStaticButtons.concat(uiFabricTabsButtons)}
|
items={uiFabricStaticButtons.concat(uiFabricTabsButtons)}
|
||||||
|
|||||||
@@ -354,6 +354,7 @@ function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandB
|
|||||||
id: "newQueryBtn",
|
id: "newQueryBtn",
|
||||||
iconSrc: AddSqlQueryIcon,
|
iconSrc: AddSqlQueryIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
keyboardShortcut: "NEW_QUERY",
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
|
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
|
||||||
@@ -369,6 +370,7 @@ function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandB
|
|||||||
id: "newQueryBtn",
|
id: "newQueryBtn",
|
||||||
iconSrc: AddSqlQueryIcon,
|
iconSrc: AddSqlQueryIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
keyboardShortcut: "NEW_QUERY",
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection);
|
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
IDropdownOption,
|
IDropdownOption,
|
||||||
IDropdownStyles,
|
IDropdownStyles,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
|
import { KeyboardShortcutHandlers } from "Common/KeyboardShortcuts";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
@@ -233,3 +234,16 @@ export const createConnectionStatus = (container: Explorer, poolId: PoolIdType,
|
|||||||
onRender: () => <ConnectionStatus container={container} poolId={poolId} />,
|
onRender: () => <ConnectionStatus container={container} poolId={poolId} />,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createKeyboardHandlers = (buttons: CommandButtonComponentProps[]): KeyboardShortcutHandlers => {
|
||||||
|
const handlers: KeyboardShortcutHandlers = {};
|
||||||
|
buttons.forEach((button) => {
|
||||||
|
if (button.keyboardShortcut) {
|
||||||
|
handlers[button.keyboardShortcut] = (e) => {
|
||||||
|
button.onCommandClick(e);
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
|||||||
@@ -104,15 +104,16 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
|
|||||||
)}
|
)}
|
||||||
<Stack className="tabPaneContentContainer">
|
<Stack className="tabPaneContentContainer">
|
||||||
<SplitterLayout percentage={true} vertical={true} primaryIndex={0} primaryMinSize={30} secondaryMinSize={70}>
|
<SplitterLayout percentage={true} vertical={true} primaryIndex={0} primaryMinSize={30} secondaryMinSize={70}>
|
||||||
|
COPILOT
|
||||||
<EditorReact
|
<EditorReact
|
||||||
language={"sql"}
|
language={"sql"}
|
||||||
content={query}
|
content={query}
|
||||||
isReadOnly={false}
|
isReadOnly={false}
|
||||||
wordWrap={"on"}
|
wordWrap={"on"}
|
||||||
ariaLabel={"Editing Query"}
|
ariaLabel={"Editing Query"}
|
||||||
lineNumbers={"on"}
|
lineNumbers={"on"}
|
||||||
onContentChanged={(newQuery: string) => setQuery(newQuery)}
|
onContentChanged={(newQuery: string) => setQuery(newQuery)}
|
||||||
onContentSelected={(selectedQuery: string) => setSelectedQuery(selectedQuery)}
|
onContentSelected={(selectedQuery: string) => setSelectedQuery(selectedQuery)}
|
||||||
/>
|
/>
|
||||||
<QueryCopilotResults />
|
<QueryCopilotResults />
|
||||||
</SplitterLayout>
|
</SplitterLayout>
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ import DiscardIcon from "../../../images/discard.svg";
|
|||||||
import SaveIcon from "../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../images/save-cosmos.svg";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import { DocumentsGridMetrics, KeyCodes } from "../../Common/Constants";
|
import { DocumentsGridMetrics, KeyCodes } from "../../Common/Constants";
|
||||||
import { createDocument } from "../../Common/dataAccess/createDocument";
|
|
||||||
import { deleteConflict } from "../../Common/dataAccess/deleteConflict";
|
|
||||||
import { deleteDocument } from "../../Common/dataAccess/deleteDocument";
|
|
||||||
import { queryConflicts } from "../../Common/dataAccess/queryConflicts";
|
|
||||||
import { updateDocument } from "../../Common/dataAccess/updateDocument";
|
|
||||||
import editable from "../../Common/EditableUtility";
|
import editable from "../../Common/EditableUtility";
|
||||||
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
import * as HeadersUtility from "../../Common/HeadersUtility";
|
import * as HeadersUtility from "../../Common/HeadersUtility";
|
||||||
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
|
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
|
||||||
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
||||||
|
import { createDocument } from "../../Common/dataAccess/createDocument";
|
||||||
|
import { deleteConflict } from "../../Common/dataAccess/deleteConflict";
|
||||||
|
import { deleteDocument } from "../../Common/dataAccess/deleteDocument";
|
||||||
|
import { queryConflicts } from "../../Common/dataAccess/queryConflicts";
|
||||||
|
import { updateDocument } from "../../Common/dataAccess/updateDocument";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
@@ -621,6 +621,7 @@ export default class ConflictsTab extends TabsBase {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
keyboardShortcut: "DISCARD",
|
||||||
onCommandClick: this.onDiscardClick,
|
onCommandClick: this.onDiscardClick,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -921,6 +921,7 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
keyboardShortcut: "DISCARD",
|
||||||
onCommandClick: this.onRevertNewDocumentClick,
|
onCommandClick: this.onRevertNewDocumentClick,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
@@ -950,6 +951,7 @@ export default class DocumentsTab extends TabsBase {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
keyboardShortcut: "DISCARD",
|
||||||
onCommandClick: this.onRevertExisitingDocumentClick,
|
onCommandClick: this.onRevertExisitingDocumentClick,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
import { FeedOptions, QueryOperationOptions } from "@azure/cosmos";
|
import { FeedOptions, QueryOperationOptions } from "@azure/cosmos";
|
||||||
import { Platform, configContext } from "ConfigContext";
|
import { Platform, configContext } from "ConfigContext";
|
||||||
import { useDialog } from "Explorer/Controls/Dialog";
|
import { useDialog } from "Explorer/Controls/Dialog";
|
||||||
|
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
||||||
|
import { MonacoNamespace, monaco } from "Explorer/LazyMonaco";
|
||||||
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
|
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
|
||||||
import { useCopilotStore } from "Explorer/QueryCopilot/QueryCopilotContext";
|
import { useCopilotStore } from "Explorer/QueryCopilot/QueryCopilotContext";
|
||||||
import { QueryCopilotPromptbar } from "Explorer/QueryCopilot/QueryCopilotPromptbar";
|
import { QueryCopilotPromptbar } from "Explorer/QueryCopilot/QueryCopilotPromptbar";
|
||||||
@@ -39,7 +41,6 @@ import { userContext } from "../../../UserContext";
|
|||||||
import * as QueryUtils from "../../../Utils/QueryUtils";
|
import * as QueryUtils from "../../../Utils/QueryUtils";
|
||||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
import { EditorReact } from "../../Controls/Editor/EditorReact";
|
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter";
|
import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
import { BrowseQueriesPane } from "../../Panes/BrowseQueriesPane/BrowseQueriesPane";
|
import { BrowseQueriesPane } from "../../Panes/BrowseQueriesPane/BrowseQueriesPane";
|
||||||
@@ -468,6 +469,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: CancelQueryIcon,
|
iconSrc: CancelQueryIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
keyboardShortcut: "CANCEL_QUERY",
|
||||||
onCommandClick: () => this.queryAbortController.abort(),
|
onCommandClick: () => this.queryAbortController.abort(),
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
@@ -584,6 +586,15 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getEditorAndQueryResult(): JSX.Element {
|
private getEditorAndQueryResult(): JSX.Element {
|
||||||
|
const configureEditor = (monaco: MonacoNamespace, editor: monaco.editor.IStandaloneCodeEditor) => {
|
||||||
|
editor.addAction({
|
||||||
|
id: "execute-query",
|
||||||
|
label: "Execute Query",
|
||||||
|
keybindings: [monaco.KeyMod.Shift | monaco.KeyCode.Enter],
|
||||||
|
run: () => this.onExecuteQueryClick(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="tab-pane" id={this.props.tabId} role="tabpanel">
|
<div className="tab-pane" id={this.props.tabId} role="tabpanel">
|
||||||
@@ -608,7 +619,8 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
|||||||
lineNumbers={"on"}
|
lineNumbers={"on"}
|
||||||
onContentChanged={(newContent: string) => this.onChangeContent(newContent)}
|
onContentChanged={(newContent: string) => this.onChangeContent(newContent)}
|
||||||
onContentSelected={(selectedContent: string) => this.onSelectedContent(selectedContent)}
|
onContentSelected={(selectedContent: string) => this.onSelectedContent(selectedContent)}
|
||||||
/>
|
configureEditor={configureEditor}
|
||||||
|
/>;
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
{this.props.isSampleCopilotActive ? (
|
{this.props.isSampleCopilotActive ? (
|
||||||
|
|||||||
@@ -238,6 +238,7 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
keyboardShortcut: "DISCARD",
|
||||||
onCommandClick: this.onDiscard,
|
onCommandClick: this.onDiscard,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -347,6 +347,7 @@ export default class StoredProcedureTabComponent extends React.Component<
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
keyboardShortcut: "DISCARD",
|
||||||
onCommandClick: this.onDiscard,
|
onCommandClick: this.onDiscard,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
|
|||||||
...this,
|
...this,
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
keyboardShortcut: "DISCARD",
|
||||||
onCommandClick: this.onDiscard,
|
onCommandClick: this.onDiscard,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import React, { Component } from "react";
|
|||||||
import DiscardIcon from "../../../images/discard.svg";
|
import DiscardIcon from "../../../images/discard.svg";
|
||||||
import SaveIcon from "../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../images/save-cosmos.svg";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
import { createUserDefinedFunction } from "../../Common/dataAccess/createUserDefinedFunction";
|
import { createUserDefinedFunction } from "../../Common/dataAccess/createUserDefinedFunction";
|
||||||
import { updateUserDefinedFunction } from "../../Common/dataAccess/updateUserDefinedFunction";
|
import { updateUserDefinedFunction } from "../../Common/dataAccess/updateUserDefinedFunction";
|
||||||
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
@@ -109,6 +109,7 @@ export default class UserDefinedFunctionTabContent extends Component<
|
|||||||
...this,
|
...this,
|
||||||
iconSrc: DiscardIcon,
|
iconSrc: DiscardIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
keyboardShortcut: "DISCARD",
|
||||||
onCommandClick: this.onDiscard,
|
onCommandClick: this.onDiscard,
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
|
|||||||
23
src/Main.tsx
23
src/Main.tsx
@@ -18,6 +18,7 @@ import "../externals/jquery.typeahead.min.js";
|
|||||||
// Image Dependencies
|
// Image Dependencies
|
||||||
import { Platform } from "ConfigContext";
|
import { Platform } from "ConfigContext";
|
||||||
import { QueryCopilotCarousel } from "Explorer/QueryCopilot/CopilotCarousel";
|
import { QueryCopilotCarousel } from "Explorer/QueryCopilot/CopilotCarousel";
|
||||||
|
import * as ReactHotkeys from "react-hotkeys";
|
||||||
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
|
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
|
||||||
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
||||||
import "../images/favicon.ico";
|
import "../images/favicon.ico";
|
||||||
@@ -61,6 +62,28 @@ import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
|||||||
|
|
||||||
initializeIcons();
|
initializeIcons();
|
||||||
|
|
||||||
|
const tagsIgnoredByReactHotkeys = ["INPUT", "SELECT"];
|
||||||
|
ReactHotkeys.configure({
|
||||||
|
ignoreEventsCondition: (evt) => {
|
||||||
|
// The default react-hotkeys behavior is to ignore events targetting a textarea, but we want the monaco editor's key events to bubble up
|
||||||
|
// So, we configure it to ignore all events targetting a textarea except when the target is a monaco editor's text area
|
||||||
|
|
||||||
|
if (!(evt.target instanceof HTMLElement)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagsIgnoredByReactHotkeys.includes(evt.target.tagName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt.target.tagName === "TEXTAREA" && !evt.target.matches(".monaco-editor textarea")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const App: React.FunctionComponent = () => {
|
const App: React.FunctionComponent = () => {
|
||||||
const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState<boolean>(true);
|
const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState<boolean>(true);
|
||||||
const isCarouselOpen = useCarousel((state) => state.shouldOpen);
|
const isCarouselOpen = useCarousel((state) => state.shouldOpen);
|
||||||
|
|||||||
Reference in New Issue
Block a user