Compare commits

..

3 Commits

Author SHA1 Message Date
Ashley Stanton-Nurse
7ba3f39d10 fix copilot insertion so it doesn't wipe existing query text 2024-04-17 10:36:28 -07:00
JustinKol
e0cb3da6aa Is executing is false (#1801) 2024-04-16 18:47:43 -04:00
JustinKol
6c9673975a Added hyphen to prohibited characters in keyspace name title (#1800) 2024-04-16 09:24:14 -04:00
11 changed files with 9 additions and 199 deletions

13
package-lock.json generated
View File

@@ -82,7 +82,6 @@
"knockout": "3.5.1",
"mkdirp": "1.0.4",
"monaco-editor": "0.44.0",
"mousetrap": "1.6.5",
"ms": "2.1.3",
"p-retry": "4.6.2",
"patch-package": "8.0.0",
@@ -129,7 +128,6 @@
"@types/hasher": "0.0.31",
"@types/jest": "26.0.20",
"@types/jquery": "3.5.29",
"@types/mousetrap": "1.6.15",
"@types/node": "12.11.1",
"@types/post-robot": "10.0.1",
"@types/q": "1.5.1",
@@ -12794,12 +12792,6 @@
"@types/node": "*"
}
},
"node_modules/@types/mousetrap": {
"version": "1.6.15",
"resolved": "https://registry.npmjs.org/@types/mousetrap/-/mousetrap-1.6.15.tgz",
"integrity": "sha512-qL0hyIMNPow317QWW/63RvL1x5MVMV+Ru3NaY9f/CuEpCqrmb7WeuK2071ZY5hczOnm38qExWM2i2WtkXLSqFw==",
"dev": true
},
"node_modules/@types/node": {
"version": "12.11.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz",
@@ -31639,11 +31631,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"node_modules/mousetrap": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz",
"integrity": "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA=="
},
"node_modules/mrmime": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz",

View File

@@ -77,7 +77,6 @@
"knockout": "3.5.1",
"mkdirp": "1.0.4",
"monaco-editor": "0.44.0",
"mousetrap": "1.6.5",
"ms": "2.1.3",
"p-retry": "4.6.2",
"patch-package": "8.0.0",
@@ -124,7 +123,6 @@
"@types/hasher": "0.0.31",
"@types/jest": "26.0.20",
"@types/jquery": "3.5.29",
"@types/mousetrap": "1.6.15",
"@types/node": "12.11.1",
"@types/post-robot": "10.0.1",
"@types/q": "1.5.1",

View File

@@ -1,8 +1,6 @@
/**
* React component for Command button component.
*/
import { KeyboardShortcutAction } from "KeyboardShortcuts";
import { ExtendedKeyboardEvent } from "mousetrap";
import * as React from "react";
import CollapseChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
import { KeyCodes } from "../../../Common/Constants";
@@ -32,7 +30,7 @@ export interface CommandButtonComponentProps {
/**
* Click handler for command button click
*/
onCommandClick: (e: React.SyntheticEvent | ExtendedKeyboardEvent) => void;
onCommandClick: (e: React.SyntheticEvent) => void;
/**
* Label for the button
@@ -109,13 +107,10 @@ export interface CommandButtonComponentProps {
* Vertical bar to divide buttons
*/
isDivider?: boolean;
/**
* Aria-label for the button
*/
ariaLabel: string;
keyboardShortcut?: KeyboardShortcutAction;
}
export class CommandButtonComponent extends React.Component<CommandButtonComponentProps> {

View File

@@ -5,7 +5,6 @@
*/
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
import { useNotebook } from "Explorer/Notebook/useNotebook";
import { KeyboardShortcutAction, KeyboardShortcutContributor, KeyboardShortcutHandler, useKeyboardShortcutContributor } from "KeyboardShortcuts";
import { userContext } from "UserContext";
import * as React from "react";
import create, { UseStore } from "zustand";
@@ -41,7 +40,6 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
const buttons = useCommandBar((state) => state.contextButtons);
const isHidden = useCommandBar((state) => state.isHidden);
const backgroundColor = StyleConstants.BaseLight;
const setKeyboardShortcutHandlers = useKeyboardShortcutContributor(KeyboardShortcutContributor.COMMAND_BAR);
if (userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") {
const buttons =
@@ -107,18 +105,6 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
},
};
const allButtons = staticButtons.concat(contextButtons).concat(controlButtons);
const handlers: Partial<Record<KeyboardShortcutAction, KeyboardShortcutHandler>> = {};
allButtons.forEach((button) => {
if(button.keyboardShortcut) {
handlers[button.keyboardShortcut] = (e) => {
button.onCommandClick(e);
return false;
}
}
});
setKeyboardShortcutHandlers(handlers);
return (
<div className="commandBarContainer" style={{ display: isHidden ? "none" : "initial" }}>
<FluentCommandBar

View File

@@ -1,4 +1,3 @@
import { KeyboardShortcutAction } from "KeyboardShortcuts";
import { ReactTabKind, useTabs } from "hooks/useTabs";
import * as React from "react";
import AddCollectionIcon from "../../../../images/AddCollection.svg";
@@ -298,7 +297,6 @@ function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandB
id: "newQueryBtn",
iconSrc: AddSqlQueryIcon,
iconAlt: label,
keyboardShortcut: KeyboardShortcutAction.NEW_QUERY,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
@@ -314,7 +312,6 @@ function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandB
id: "newQueryBtn",
iconSrc: AddSqlQueryIcon,
iconAlt: label,
keyboardShortcut: KeyboardShortcutAction.NEW_QUERY,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection);

View File

@@ -202,8 +202,8 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
required={true}
autoComplete="off"
styles={getTextFieldStyles()}
pattern="[^/?#\\]*[^/?# \\]"
title="May not end with space nor contain characters '\' '/' '#' '?'"
pattern="[^/?#\\-]*[^/?#- \\]"
title="May not end with space nor contain characters '\' '/' '#' '?' '-'"
placeholder="Type a new keyspace id"
size={40}
value={newKeyspaceId}
@@ -292,8 +292,8 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
required={true}
ariaLabel="addCollection-table Id Create table"
autoComplete="off"
pattern="[^/?#\\]*[^/?# \\]"
title="May not end with space nor contain characters '\' '/' '#' '?'"
pattern="[^/?#\\-]*[^/?#- \\]"
title="May not end with space nor contain characters '\' '/' '#' '?' '-'"
placeholder="Enter table Id"
size={20}
value={tableId}

View File

@@ -124,8 +124,8 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
setIsExecuting(true);
const entity: Entities.ITableEntity = entityFromAttributes(entities);
const newEntity: Entities.ITableEntity = await tableDataClient.createDocument(queryTablesTab.collection, entity);
try {
const newEntity: Entities.ITableEntity = await tableDataClient.createDocument(queryTablesTab.collection, entity);
await tableEntityListViewModel.addEntityToCache(newEntity);
if (!tryInsertNewHeaders(tableEntityListViewModel, newEntity)) {
tableEntityListViewModel.redrawTableThrottled();

View File

@@ -218,7 +218,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
generateSQLQueryResponse.explanation ? generateSQLQueryResponse.explanation : "N/A"
}\r\n`;
const currentGeneratedQuery = queryExplanation + generateSQLQueryResponse.sql;
const lastQuery = generatedQuery && query ? `${query}\r\n` : "";
const lastQuery = query ? `${query}\r\n` : "";
setQuery(`${lastQuery}${currentGeneratedQuery}`);
setGeneratedQuery(generateSQLQueryResponse.sql);
setGeneratedQueryComments(generateSQLQueryResponse.explanation);

View File

@@ -10,7 +10,6 @@ import { OnExecuteQueryClick, QueryDocumentsPerPage } from "Explorer/QueryCopilo
import { QueryCopilotSidebar } from "Explorer/QueryCopilot/V2/Sidebar/QueryCopilotSidebar";
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
import { useSelectedNode } from "Explorer/useSelectedNode";
import { KeyboardShortcutAction } from "KeyboardShortcuts";
import { QueryConstants } from "Shared/Constants";
import { LocalStorageUtility, StorageKey, getRUThreshold, ruThresholdEnabled } from "Shared/StorageUtility";
import { Action } from "Shared/Telemetry/TelemetryConstants";
@@ -394,7 +393,6 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
buttons.push({
iconSrc: ExecuteQueryIcon,
iconAlt: label,
keyboardShortcut: KeyboardShortcutAction.EXECUTE_ITEM,
onCommandClick: this.props.isSampleCopilotActive
? () => OnExecuteQueryClick(this.props.copilotStore)
: this.onExecuteQueryClick,

View File

@@ -1,150 +0,0 @@
import Mousetrap, { ExtendedKeyboardEvent } from "mousetrap";
import * as React from "react";
import create, { UseStore } from "zustand";
// Provides a system of Keyboard Shortcuts that can be contributed to by different parts of the application.
//
// The goals of this system are:
// * Shortcuts can be contributed from different parts of the application (e.g. the command bar, specified editor tabs, etc.)
// * Contributors may only provide some of their shortcuts, others may be out-of-scope for the current context.
// * Contributors don't have to add/remove handlers individually, they can use a declarative pattern to set all their handlers at once.
//
// So, in order to do that, we store handlers in a two-level hierarchy:
// 1. We store a mapping of contributors to their Contributions.
// 2. Each Contribution is a mapping of actions to their handlers.
//
// Thus, a Contributor sets all its handlers at once, replacing all handlers previously contributed by that Contributor.
// The system then merges all Contributions into a single set of handlers, with duplicate handlers being handled in the order that the Contributors are processed.
export type KeyboardShortcutHandler = (e: ExtendedKeyboardEvent, combo: string) => boolean | void;
/**
* Lists all the possible contributors to keyboard shortcut handlers.
*
* A "Contributor" is a part of the application that can contribute keyboard shortcut handlers.
* The contributor must set all it's keyboard shortcut handlers at once.
* This allows the contributor to easily replace all it's keyboard shortcuts at once.
*
* For example, the command bar adds/removes keyboard shortcut handlers based on the current context, using the existing logic that determines which buttons are shown.
* Isolating contributors like this allow the command bar to easily replace all it's keyboard shortcuts when the context changes without breaking keyboard shortcuts contributed by other parts of the application.
*/
export enum KeyboardShortcutContributor {
COMMAND_BAR = "COMMAND_BAR",
}
/**
* The order in which contributors are processed.
* This is important because the last contributor to set a handler for a given action will be the one that is used.
*/
const contributorOrder: KeyboardShortcutContributor[] = [
KeyboardShortcutContributor.COMMAND_BAR,
];
/**
* The possible actions that can be triggered by keyboard shortcuts.
*/
export enum KeyboardShortcutAction {
NEW_QUERY = "NEW_QUERY",
EXECUTE_ITEM = "EXECUTE_ITEM",
}
/**
* The default keyboard shortcuts for the application.
* This record maps each action to the keyboard shortcuts that trigger the action.
* Even if an action is specified here though, it will not be triggered unless a handler is set for it.
*/
const bindings: Record<KeyboardShortcutAction, string[]> = {
[KeyboardShortcutAction.NEW_QUERY]: ["ctrl+j"],
[KeyboardShortcutAction.EXECUTE_ITEM]: ["shift+enter"],
};
/**
* Represents all the handlers provided by a contributor.
*/
export type KeyboardShortcutContribution = Partial<Record<KeyboardShortcutAction, KeyboardShortcutHandler>>;
interface KeyboardShortcutState {
/**
* Collects all the contributions from different contributors.
*/
contributions: Partial<Record<KeyboardShortcutContributor, KeyboardShortcutContribution>>;
/**
* A merged set of all the handlers from all contributors.
*/
allHandlers: KeyboardShortcutContribution;
/**
* Sets the keyboard shortcut handlers for a given contributor.
*/
setHandlers: (contributor: KeyboardShortcutContributor, handlers: Partial<Record<KeyboardShortcutAction, KeyboardShortcutHandler>>) => void;
}
/**
* Gets the setHandlers function for a given contributor.
* @param contributor The contributor to get the setHandlers function for.
* @returns A function that sets the keyboard shortcut handlers for the given contributor.
*/
export const useKeyboardShortcutContributor = (contributor: KeyboardShortcutContributor) => {
const setHandlers = useKeyboardShortcutHandlers.getState().setHandlers;
return (handlers: Partial<Record<KeyboardShortcutAction, KeyboardShortcutHandler>>) => {
setHandlers(contributor, handlers);
};
}
const useKeyboardShortcutHandlers: UseStore<KeyboardShortcutState> = create((set, get) => ({
contributions: {},
allHandlers: {},
setHandlers: (contributor: KeyboardShortcutContributor, handlers: Partial<Record<KeyboardShortcutAction, KeyboardShortcutHandler>>) => {
const current = get();
// Update the list of contributions.
const newContributions = { ...current.contributions, [contributor]: handlers };
// Merge all the contributions into a single set of handlers.
const allHandlers: KeyboardShortcutContribution = {};
contributorOrder.forEach((contributor) => {
const contribution = newContributions[contributor];
if (contribution) {
(Object.keys(contribution) as KeyboardShortcutAction[]).forEach((action) => {
allHandlers[action] = contribution[action];
});
}
});
set({ contributions: newContributions, allHandlers })
}
}));
function createHandler(action: KeyboardShortcutAction): KeyboardShortcutHandler {
return (e, combo) => {
const handlers = useKeyboardShortcutHandlers.getState().allHandlers;
const handler = handlers[action];
if (handler) {
return handler(e, combo);
}
};
}
export function KeyboardShortcutRoot(props: React.HTMLProps<HTMLDivElement>) {
const ref = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
const m = new Mousetrap(ref.current);
const existingStopCallback = m.stopCallback;
m.stopCallback = (e, element, combo) => {
// Don't block mousetrap callback in the Monaco editor.
if (element.matches(".monaco-editor textarea")) {
return false;
}
return existingStopCallback(e, element, combo);
};
(Object.keys(bindings) as KeyboardShortcutAction[]).forEach((action) => {
m.bind(bindings[action], createHandler(action));
});
}, [ref]); // We only need to re-render the component when the ref changes.
return <div ref={ref} {...props}>
</div>;
}

View File

@@ -21,7 +21,6 @@ import "../externals/jquery.typeahead.min.js";
// Image Dependencies
import { Platform } from "ConfigContext";
import { QueryCopilotCarousel } from "Explorer/QueryCopilot/CopilotCarousel";
import { KeyboardShortcutRoot } from "KeyboardShortcuts";
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
import "../images/favicon.ico";
@@ -92,7 +91,7 @@ const App: React.FunctionComponent = () => {
}
return (
<KeyboardShortcutRoot className="flexContainer" aria-hidden="false">
<div className="flexContainer" aria-hidden="false">
<div id="divExplorer" className="flexContainer hideOverflows">
<div id="freeTierTeachingBubble"> </div>
{/* Main Command Bar - Start */}
@@ -137,7 +136,7 @@ const App: React.FunctionComponent = () => {
{<SQLQuickstartTutorial />}
{<MongoQuickstartTutorial />}
{<QueryCopilotCarousel isOpen={isCopilotCarouselOpen} explorer={explorer} />}
</KeyboardShortcutRoot>
</div>
);
};