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} + );