CodeQL model editor: Use suggest box for type path suggestions (#3316)
This commit is contained in:
@@ -278,6 +278,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
|
||||
<ModelInputSuggestBox
|
||||
modeledMethod={modeledMethod}
|
||||
suggestions={inputAccessPathSuggestions}
|
||||
typePathSuggestions={outputAccessPathSuggestions ?? []}
|
||||
onChange={modeledMethodChangedHandlers[index]}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
calculateNewProvenance,
|
||||
modeledMethodSupportsInput,
|
||||
} from "../../model-editor/modeled-method";
|
||||
import { ReadonlyDropdown } from "../common/ReadonlyDropdown";
|
||||
import type { AccessPathOption } from "../../model-editor/suggestions";
|
||||
import { SuggestBox } from "../common/SuggestBox";
|
||||
import { useDebounceCallback } from "../common/useDebounceCallback";
|
||||
@@ -14,10 +13,12 @@ import {
|
||||
validateAccessPath,
|
||||
} from "../../model-editor/shared/access-paths";
|
||||
import { ModelSuggestionIcon } from "./ModelSuggestionIcon";
|
||||
import { ModelTypePathSuggestBox } from "./ModelTypePathSuggestBox";
|
||||
|
||||
type Props = {
|
||||
modeledMethod: ModeledMethod | undefined;
|
||||
suggestions: AccessPathOption[];
|
||||
typePathSuggestions: AccessPathOption[];
|
||||
onChange: (modeledMethod: ModeledMethod) => void;
|
||||
};
|
||||
|
||||
@@ -33,6 +34,7 @@ const getDetails = (option: AccessPathOption) => option.details;
|
||||
export const ModelInputSuggestBox = ({
|
||||
modeledMethod,
|
||||
suggestions,
|
||||
typePathSuggestions,
|
||||
onChange,
|
||||
}: Props) => {
|
||||
const [value, setValue] = useState<string | undefined>(
|
||||
@@ -75,7 +77,13 @@ export const ModelInputSuggestBox = ({
|
||||
);
|
||||
|
||||
if (modeledMethod?.type === "type") {
|
||||
return <ReadonlyDropdown value={modeledMethod.path} aria-label="Path" />;
|
||||
return (
|
||||
<ModelTypePathSuggestBox
|
||||
modeledMethod={modeledMethod}
|
||||
suggestions={typePathSuggestions}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
calculateNewProvenance,
|
||||
modeledMethodSupportsOutput,
|
||||
} from "../../model-editor/modeled-method";
|
||||
import { ReadonlyDropdown } from "../common/ReadonlyDropdown";
|
||||
import type { AccessPathOption } from "../../model-editor/suggestions";
|
||||
import { SuggestBox } from "../common/SuggestBox";
|
||||
import { useDebounceCallback } from "../common/useDebounceCallback";
|
||||
@@ -14,6 +13,7 @@ import {
|
||||
validateAccessPath,
|
||||
} from "../../model-editor/shared/access-paths";
|
||||
import { ModelSuggestionIcon } from "./ModelSuggestionIcon";
|
||||
import { ModelTypeTextbox } from "./ModelTypeTextbox";
|
||||
|
||||
type Props = {
|
||||
modeledMethod: ModeledMethod | undefined;
|
||||
@@ -76,8 +76,10 @@ export const ModelOutputSuggestBox = ({
|
||||
|
||||
if (modeledMethod?.type === "type") {
|
||||
return (
|
||||
<ReadonlyDropdown
|
||||
value={modeledMethod.relatedTypeName}
|
||||
<ModelTypeTextbox
|
||||
modeledMethod={modeledMethod}
|
||||
typeInfo="relatedTypeName"
|
||||
onChange={onChange}
|
||||
aria-label="Related type name"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import type { TypeModeledMethod } from "../../model-editor/modeled-method";
|
||||
import type { AccessPathOption } from "../../model-editor/suggestions";
|
||||
import { SuggestBox } from "../common/SuggestBox";
|
||||
import { useDebounceCallback } from "../common/useDebounceCallback";
|
||||
import type { AccessPathDiagnostic } from "../../model-editor/shared/access-paths";
|
||||
import {
|
||||
parseAccessPathTokens,
|
||||
validateAccessPath,
|
||||
} from "../../model-editor/shared/access-paths";
|
||||
import { ModelSuggestionIcon } from "./ModelSuggestionIcon";
|
||||
|
||||
type Props = {
|
||||
modeledMethod: TypeModeledMethod;
|
||||
suggestions: AccessPathOption[];
|
||||
onChange: (modeledMethod: TypeModeledMethod) => void;
|
||||
};
|
||||
|
||||
const parseValueToTokens = (value: string) =>
|
||||
parseAccessPathTokens(value).map((t) => t.text);
|
||||
|
||||
const getIcon = (option: AccessPathOption) => (
|
||||
<ModelSuggestionIcon name={option.icon} />
|
||||
);
|
||||
|
||||
const getDetails = (option: AccessPathOption) => option.details;
|
||||
|
||||
export const ModelTypePathSuggestBox = ({
|
||||
modeledMethod,
|
||||
suggestions,
|
||||
onChange,
|
||||
}: Props) => {
|
||||
const [value, setValue] = useState<string | undefined>(modeledMethod.path);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(modeledMethod.path);
|
||||
}, [modeledMethod]);
|
||||
|
||||
// Debounce the callback to avoid updating the model too often.
|
||||
// Not doing this results in a lot of lag when typing.
|
||||
useDebounceCallback(
|
||||
value,
|
||||
(path: string | undefined) => {
|
||||
if (path === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
onChange({
|
||||
...modeledMethod,
|
||||
path,
|
||||
});
|
||||
},
|
||||
500,
|
||||
);
|
||||
|
||||
return (
|
||||
<SuggestBox<AccessPathOption, AccessPathDiagnostic>
|
||||
value={value}
|
||||
options={suggestions}
|
||||
onChange={setValue}
|
||||
parseValueToTokens={parseValueToTokens}
|
||||
validateValue={validateAccessPath}
|
||||
getIcon={getIcon}
|
||||
getDetails={getDetails}
|
||||
aria-label="Path"
|
||||
/>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user