Add empty states for modeling panel (#2914)
This commit is contained in:
@@ -323,6 +323,7 @@ export type PackagingCommands = {
|
|||||||
|
|
||||||
export type ModelEditorCommands = {
|
export type ModelEditorCommands = {
|
||||||
"codeQL.openModelEditor": () => Promise<void>;
|
"codeQL.openModelEditor": () => Promise<void>;
|
||||||
|
"codeQL.openModelEditorFromModelingPanel": () => Promise<void>;
|
||||||
"codeQLModelEditor.jumpToUsageLocation": (
|
"codeQLModelEditor.jumpToUsageLocation": (
|
||||||
method: Method,
|
method: Method,
|
||||||
usage: Usage,
|
usage: Usage,
|
||||||
|
|||||||
@@ -610,10 +610,15 @@ interface RevealInEditorMessage {
|
|||||||
method: Method;
|
method: Method;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface StartModelingMessage {
|
||||||
|
t: "startModeling";
|
||||||
|
}
|
||||||
|
|
||||||
export type FromMethodModelingMessage =
|
export type FromMethodModelingMessage =
|
||||||
| CommonFromViewMessages
|
| CommonFromViewMessages
|
||||||
| SetModeledMethodMessage
|
| SetModeledMethodMessage
|
||||||
| RevealInEditorMessage;
|
| RevealInEditorMessage
|
||||||
|
| StartModelingMessage;
|
||||||
|
|
||||||
interface SetMethodMessage {
|
interface SetMethodMessage {
|
||||||
t: "setMethod";
|
t: "setMethod";
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export abstract class AbstractWebviewViewProvider<
|
|||||||
private disposables: Disposable[] = [];
|
private disposables: Disposable[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly app: App,
|
protected readonly app: App,
|
||||||
private readonly webviewKind: WebviewKind,
|
private readonly webviewKind: WebviewKind,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,12 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
|||||||
await this.revealInModelEditor(msg.method);
|
await this.revealInModelEditor(msg.method);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "startModeling":
|
||||||
|
await this.app.commands.execute(
|
||||||
|
"codeQL.openModelEditorFromModelingPanel",
|
||||||
|
);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assertNever(msg);
|
assertNever(msg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,43 @@ export class ModelEditorModule extends DisposableObject {
|
|||||||
|
|
||||||
public getCommands(): ModelEditorCommands {
|
public getCommands(): ModelEditorCommands {
|
||||||
return {
|
return {
|
||||||
"codeQL.openModelEditor": async () => {
|
"codeQL.openModelEditor": this.openModelEditor.bind(this),
|
||||||
|
"codeQL.openModelEditorFromModelingPanel":
|
||||||
|
this.openModelEditor.bind(this),
|
||||||
|
"codeQLModelEditor.jumpToUsageLocation": async (
|
||||||
|
method: Method,
|
||||||
|
usage: Usage,
|
||||||
|
databaseItem: DatabaseItem,
|
||||||
|
) => {
|
||||||
|
this.modelingStore.setSelectedMethod(databaseItem, method, usage);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initialize(): Promise<void> {
|
||||||
|
await ensureDir(this.queryStorageDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerToModelingStoreEvents(): void {
|
||||||
|
this.push(
|
||||||
|
this.modelingStore.onSelectedMethodChanged(async (event) => {
|
||||||
|
await this.showMethod(event.databaseItem, event.method, event.usage);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async showMethod(
|
||||||
|
databaseItem: DatabaseItem,
|
||||||
|
method: Method,
|
||||||
|
usage: Usage,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.methodsUsagePanel.revealItem(usage);
|
||||||
|
await this.methodModelingPanel.setMethod(method);
|
||||||
|
await showResolvableLocation(usage.url, databaseItem, this.app.logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async openModelEditor(): Promise<void> {
|
||||||
|
{
|
||||||
const db = this.databaseManager.currentDatabaseItem;
|
const db = this.databaseManager.currentDatabaseItem;
|
||||||
if (!db) {
|
if (!db) {
|
||||||
void showAndLogErrorMessage(this.app.logger, "No database selected");
|
void showAndLogErrorMessage(this.app.logger, "No database selected");
|
||||||
@@ -181,36 +217,6 @@ export class ModelEditorModule extends DisposableObject {
|
|||||||
title: "Opening CodeQL Model Editor",
|
title: "Opening CodeQL Model Editor",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
"codeQLModelEditor.jumpToUsageLocation": async (
|
|
||||||
method: Method,
|
|
||||||
usage: Usage,
|
|
||||||
databaseItem: DatabaseItem,
|
|
||||||
) => {
|
|
||||||
this.modelingStore.setSelectedMethod(databaseItem, method, usage);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async initialize(): Promise<void> {
|
|
||||||
await ensureDir(this.queryStorageDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
private registerToModelingStoreEvents(): void {
|
|
||||||
this.push(
|
|
||||||
this.modelingStore.onSelectedMethodChanged(async (event) => {
|
|
||||||
await this.showMethod(event.databaseItem, event.method, event.usage);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async showMethod(
|
|
||||||
databaseItem: DatabaseItem,
|
|
||||||
method: Method,
|
|
||||||
usage: Usage,
|
|
||||||
): Promise<void> {
|
|
||||||
await this.methodsUsagePanel.revealItem(usage);
|
|
||||||
await this.methodModelingPanel.setMethod(method);
|
|
||||||
await showResolvableLocation(usage.url, databaseItem, this.app.logger);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { Meta, StoryFn } from "@storybook/react";
|
||||||
|
|
||||||
|
import { ResponsiveContainer as ResponsiveContainerComponent } from "../../view/common/ResponsiveContainer";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Responsive Container",
|
||||||
|
component: ResponsiveContainerComponent,
|
||||||
|
} as Meta<typeof ResponsiveContainerComponent>;
|
||||||
|
|
||||||
|
const Template: StoryFn<typeof ResponsiveContainerComponent> = (args) => (
|
||||||
|
<ResponsiveContainerComponent>
|
||||||
|
<span>Hello</span>
|
||||||
|
</ResponsiveContainerComponent>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ResponsiveContainer = Template.bind({});
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { Meta, StoryFn } from "@storybook/react";
|
||||||
|
|
||||||
|
import { NoMethodSelected as NoMethodSelectedComponent } from "../../view/method-modeling/NoMethodSelected";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Method Modeling/No Method Selected",
|
||||||
|
component: NoMethodSelectedComponent,
|
||||||
|
} as Meta<typeof NoMethodSelectedComponent>;
|
||||||
|
|
||||||
|
const Template: StoryFn<typeof NoMethodSelectedComponent> = () => (
|
||||||
|
<NoMethodSelectedComponent />
|
||||||
|
);
|
||||||
|
|
||||||
|
export const NoMethodSelected = Template.bind({});
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { Meta, StoryFn } from "@storybook/react";
|
||||||
|
|
||||||
|
import { NotInModelingMode as NotInModelingModeComponent } from "../../view/method-modeling/NotInModelingMode";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Method Modeling/Not In Modeling Mode",
|
||||||
|
component: NotInModelingModeComponent,
|
||||||
|
} as Meta<typeof NotInModelingModeComponent>;
|
||||||
|
|
||||||
|
const Template: StoryFn<typeof NotInModelingModeComponent> = () => (
|
||||||
|
<NotInModelingModeComponent />
|
||||||
|
);
|
||||||
|
|
||||||
|
export const NotInModelingMode = Template.bind({});
|
||||||
15
extensions/ql-vscode/src/view/common/ResponsiveContainer.tsx
Normal file
15
extensions/ql-vscode/src/view/common/ResponsiveContainer.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { styled } from "styled-components";
|
||||||
|
|
||||||
|
export const ResponsiveContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
@media (min-height: 300px) {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { ResponsiveContainer } from "../common/ResponsiveContainer";
|
||||||
|
|
||||||
|
export const NoMethodSelected = () => {
|
||||||
|
return (
|
||||||
|
<ResponsiveContainer>Select an API or method to model</ResponsiveContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
import { vscode } from "../vscode-api";
|
||||||
|
import { styled } from "styled-components";
|
||||||
|
import TextButton from "../common/TextButton";
|
||||||
|
import { ResponsiveContainer } from "../common/ResponsiveContainer";
|
||||||
|
|
||||||
|
const Button = styled(TextButton)`
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const NotInModelingMode = () => {
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
vscode.postMessage({
|
||||||
|
t: "startModeling",
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ResponsiveContainer>
|
||||||
|
<span>Not in modeling mode</span>
|
||||||
|
<Button onClick={handleClick}>Start modeling</Button>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user