diff --git a/extensions/ql-vscode/src/view/model-editor/AccessPathSuggestBox.tsx b/extensions/ql-vscode/src/view/model-editor/AccessPathSuggestBox.tsx
new file mode 100644
index 000000000..8280b7cf9
--- /dev/null
+++ b/extensions/ql-vscode/src/view/model-editor/AccessPathSuggestBox.tsx
@@ -0,0 +1,70 @@
+import { useEffect, useState } from "react";
+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 = {
+ value: string | undefined;
+ onChange: (value: string) => void;
+ suggestions: AccessPathOption[];
+ disabled?: boolean;
+
+ "aria-label": string;
+};
+
+const parseValueToTokens = (value: string) =>
+ parseAccessPathTokens(value).map((t) => t.text);
+
+const getIcon = (option: AccessPathOption) => (
+
+);
+
+const getDetails = (option: AccessPathOption) => option.details;
+
+export const AccessPathSuggestBox = ({
+ value: givenValue,
+ suggestions,
+ onChange,
+ disabled,
+ "aria-label": ariaLabel,
+}: Props) => {
+ const [value, setValue] = useState(givenValue);
+
+ useEffect(() => {
+ setValue(givenValue);
+ }, [givenValue]);
+
+ // Debounce the callback to avoid updating the model too often.
+ // Not doing this results in a lot of lag when typing.
+ useDebounceCallback(
+ value,
+ (newValue: string | undefined) => {
+ if (newValue === undefined) {
+ return;
+ }
+
+ onChange(newValue);
+ },
+ 500,
+ );
+
+ return (
+
+ value={value}
+ onChange={setValue}
+ options={suggestions}
+ parseValueToTokens={parseValueToTokens}
+ validateValue={validateAccessPath}
+ getIcon={getIcon}
+ getDetails={getDetails}
+ disabled={disabled}
+ aria-label={ariaLabel}
+ />
+ );
+};
diff --git a/extensions/ql-vscode/src/view/model-editor/ModelInputSuggestBox.tsx b/extensions/ql-vscode/src/view/model-editor/ModelInputSuggestBox.tsx
index b290dff43..a6ac75ff0 100644
--- a/extensions/ql-vscode/src/view/model-editor/ModelInputSuggestBox.tsx
+++ b/extensions/ql-vscode/src/view/model-editor/ModelInputSuggestBox.tsx
@@ -1,19 +1,12 @@
-import { useEffect, useMemo, useState } from "react";
+import { useCallback, useMemo } from "react";
import type { ModeledMethod } from "../../model-editor/modeled-method";
import {
calculateNewProvenance,
modeledMethodSupportsInput,
} 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";
import { ModelTypePathSuggestBox } from "./ModelTypePathSuggestBox";
+import { AccessPathSuggestBox } from "./AccessPathSuggestBox";
type Props = {
modeledMethod: ModeledMethod | undefined;
@@ -22,37 +15,13 @@ type Props = {
onChange: (modeledMethod: ModeledMethod) => void;
};
-const parseValueToTokens = (value: string) =>
- parseAccessPathTokens(value).map((t) => t.text);
-
-const getIcon = (option: AccessPathOption) => (
-
-);
-
-const getDetails = (option: AccessPathOption) => option.details;
-
export const ModelInputSuggestBox = ({
modeledMethod,
suggestions,
typePathSuggestions,
onChange,
}: Props) => {
- const [value, setValue] = useState(
- modeledMethod && modeledMethodSupportsInput(modeledMethod)
- ? modeledMethod.input
- : undefined,
- );
-
- useEffect(() => {
- if (modeledMethod && modeledMethodSupportsInput(modeledMethod)) {
- setValue(modeledMethod.input);
- }
- }, [modeledMethod]);
-
- // Debounce the callback to avoid updating the model too often.
- // Not doing this results in a lot of lag when typing.
- useDebounceCallback(
- value,
+ const handleChange = useCallback(
(input: string | undefined) => {
if (
!modeledMethod ||
@@ -68,7 +37,7 @@ export const ModelInputSuggestBox = ({
input,
});
},
- 500,
+ [onChange, modeledMethod],
);
const enabled = useMemo(
@@ -87,14 +56,14 @@ export const ModelInputSuggestBox = ({
}
return (
-
- value={value}
- onChange={setValue}
- options={suggestions}
- parseValueToTokens={parseValueToTokens}
- validateValue={validateAccessPath}
- getIcon={getIcon}
- getDetails={getDetails}
+
diff --git a/extensions/ql-vscode/src/view/model-editor/ModelOutputSuggestBox.tsx b/extensions/ql-vscode/src/view/model-editor/ModelOutputSuggestBox.tsx
index 0aeb3d6de..8d61781fd 100644
--- a/extensions/ql-vscode/src/view/model-editor/ModelOutputSuggestBox.tsx
+++ b/extensions/ql-vscode/src/view/model-editor/ModelOutputSuggestBox.tsx
@@ -1,19 +1,12 @@
-import { useEffect, useMemo, useState } from "react";
+import { useCallback, useMemo } from "react";
import type { ModeledMethod } from "../../model-editor/modeled-method";
import {
calculateNewProvenance,
modeledMethodSupportsOutput,
} 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";
import { ModelTypeTextbox } from "./ModelTypeTextbox";
+import { AccessPathSuggestBox } from "./AccessPathSuggestBox";
type Props = {
modeledMethod: ModeledMethod | undefined;
@@ -21,36 +14,12 @@ type Props = {
onChange: (modeledMethod: ModeledMethod) => void;
};
-const parseValueToTokens = (value: string) =>
- parseAccessPathTokens(value).map((t) => t.text);
-
-const getIcon = (option: AccessPathOption) => (
-
-);
-
-const getDetails = (option: AccessPathOption) => option.details;
-
export const ModelOutputSuggestBox = ({
modeledMethod,
suggestions,
onChange,
}: Props) => {
- const [value, setValue] = useState(
- modeledMethod && modeledMethodSupportsOutput(modeledMethod)
- ? modeledMethod.output
- : undefined,
- );
-
- useEffect(() => {
- if (modeledMethod && modeledMethodSupportsOutput(modeledMethod)) {
- setValue(modeledMethod.output);
- }
- }, [modeledMethod]);
-
- // Debounce the callback to avoid updating the model too often.
- // Not doing this results in a lot of lag when typing.
- useDebounceCallback(
- value,
+ const handleChange = useCallback(
(output: string | undefined) => {
if (
!modeledMethod ||
@@ -66,7 +35,7 @@ export const ModelOutputSuggestBox = ({
output,
});
},
- 500,
+ [modeledMethod, onChange],
);
const enabled = useMemo(
@@ -86,15 +55,15 @@ export const ModelOutputSuggestBox = ({
}
return (
-
- value={value}
- options={suggestions}
+
);
diff --git a/extensions/ql-vscode/src/view/model-editor/ModelTypePathSuggestBox.tsx b/extensions/ql-vscode/src/view/model-editor/ModelTypePathSuggestBox.tsx
index 044c65766..38d625d94 100644
--- a/extensions/ql-vscode/src/view/model-editor/ModelTypePathSuggestBox.tsx
+++ b/extensions/ql-vscode/src/view/model-editor/ModelTypePathSuggestBox.tsx
@@ -1,14 +1,7 @@
-import { useEffect, useState } from "react";
+import { useCallback } 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";
+import { AccessPathSuggestBox } from "./AccessPathSuggestBox";
type Props = {
modeledMethod: TypeModeledMethod;
@@ -16,30 +9,12 @@ type Props = {
onChange: (modeledMethod: TypeModeledMethod) => void;
};
-const parseValueToTokens = (value: string) =>
- parseAccessPathTokens(value).map((t) => t.text);
-
-const getIcon = (option: AccessPathOption) => (
-
-);
-
-const getDetails = (option: AccessPathOption) => option.details;
-
export const ModelTypePathSuggestBox = ({
modeledMethod,
suggestions,
onChange,
}: Props) => {
- const [value, setValue] = useState(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,
+ const handleChange = useCallback(
(path: string | undefined) => {
if (path === undefined) {
return;
@@ -50,18 +25,14 @@ export const ModelTypePathSuggestBox = ({
path,
});
},
- 500,
+ [modeledMethod, onChange],
);
return (
-
- value={value}
- options={suggestions}
- onChange={setValue}
- parseValueToTokens={parseValueToTokens}
- validateValue={validateAccessPath}
- getIcon={getIcon}
- getDetails={getDetails}
+
);