Convert dropdown to a full component
This commit is contained in:
@@ -1,16 +1,8 @@
|
||||
import * as React from "react";
|
||||
import { ChangeEvent } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
/**
|
||||
* Styled to look like a `VSCodeDropdown`.
|
||||
*
|
||||
* The reason for doing this is that `VSCodeDropdown` doesn't handle fitting into
|
||||
* available space and truncating content, and this leads to breaking the
|
||||
* `VSCodeDataGrid` layout. This version using `select` directly will truncate the
|
||||
* content as necessary and fit into whatever space is available.
|
||||
* See https://github.com/github/vscode-codeql/pull/2582#issuecomment-1622164429
|
||||
* for more info on the problem and other potential solutions.
|
||||
*/
|
||||
export const Dropdown = styled.select`
|
||||
const StyledDropdown = styled.select`
|
||||
width: 100%;
|
||||
height: calc(var(--input-height) * 1px);
|
||||
background: var(--dropdown-background);
|
||||
@@ -19,3 +11,40 @@ export const Dropdown = styled.select`
|
||||
border: none;
|
||||
padding: 2px 6px 2px 8px;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
value: string | undefined;
|
||||
options: Array<{ value: string; label: string }>;
|
||||
disabled?: boolean;
|
||||
onChange: (event: ChangeEvent<HTMLSelectElement>) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* A dropdown implementation styled to look like `VSCodeDropdown`.
|
||||
*
|
||||
* The reason for doing this is that `VSCodeDropdown` doesn't handle fitting into
|
||||
* available space and truncating content, and this leads to breaking the
|
||||
* `VSCodeDataGrid` layout. This version using `select` directly will truncate the
|
||||
* content as necessary and fit into whatever space is available.
|
||||
* See https://github.com/github/vscode-codeql/pull/2582#issuecomment-1622164429
|
||||
* for more info on the problem and other potential solutions.
|
||||
*/
|
||||
export function Dropdown({ value, options, disabled, onChange }: Props) {
|
||||
return (
|
||||
<StyledDropdown
|
||||
value={disabled ? undefined : value}
|
||||
disabled={disabled}
|
||||
onChange={onChange}
|
||||
>
|
||||
{!disabled && (
|
||||
<>
|
||||
{options.map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</StyledDropdown>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { ChangeEvent, useCallback, useEffect } from "react";
|
||||
import { ChangeEvent, useCallback, useEffect, useMemo } from "react";
|
||||
|
||||
import type { ModeledMethod } from "../../data-extensions-editor/modeled-method";
|
||||
import { Dropdown } from "../common/Dropdown";
|
||||
@@ -13,6 +13,11 @@ type Props = {
|
||||
};
|
||||
|
||||
export const KindInput = ({ kinds, value, disabled, onChange }: Props) => {
|
||||
const options = useMemo(
|
||||
() => kinds.map((kind) => ({ value: kind, label: kind })),
|
||||
[kinds],
|
||||
);
|
||||
|
||||
const handleInput = useCallback(
|
||||
(e: ChangeEvent<HTMLSelectElement>) => {
|
||||
const target = e.target as HTMLSelectElement;
|
||||
@@ -33,16 +38,11 @@ export const KindInput = ({ kinds, value, disabled, onChange }: Props) => {
|
||||
}, [value, kinds, onChange]);
|
||||
|
||||
return (
|
||||
<Dropdown value={value} disabled={disabled} onChange={handleInput}>
|
||||
{!disabled && (
|
||||
<>
|
||||
{kinds.map((kind) => (
|
||||
<option key={kind} value={kind}>
|
||||
{kind}
|
||||
</option>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Dropdown>
|
||||
<Dropdown
|
||||
value={value}
|
||||
options={options}
|
||||
disabled={disabled}
|
||||
onChange={handleInput}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -141,10 +141,44 @@ export const MethodRow = ({
|
||||
const showModelTypeCell =
|
||||
!externalApiUsage.supported ||
|
||||
(modeledMethod && modeledMethod?.type !== "none");
|
||||
const modelTypeOptions = useMemo(
|
||||
() => [
|
||||
{ value: "none", label: "Unmodeled" },
|
||||
{ value: "source", label: "Source" },
|
||||
{ value: "sink", label: "Sink" },
|
||||
{ value: "summary", label: "Flow summary" },
|
||||
{ value: "neutral", label: "Neutral" },
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const showInputCell =
|
||||
modeledMethod?.type && ["sink", "summary"].includes(modeledMethod?.type);
|
||||
const inputOptions = useMemo(
|
||||
() => [
|
||||
{ value: "Argument[this]", label: "Argument[this]" },
|
||||
...argumentsList.map((argument, index) => ({
|
||||
value: `Argument[${index}]`,
|
||||
label: `Argument[${index}]: ${argument}`,
|
||||
})),
|
||||
],
|
||||
[argumentsList],
|
||||
);
|
||||
|
||||
const showOutputCell =
|
||||
modeledMethod?.type && ["source", "summary"].includes(modeledMethod?.type);
|
||||
const outputOptions = useMemo(
|
||||
() => [
|
||||
{ value: "ReturnValue", label: "ReturnValue" },
|
||||
{ value: "Argument[this]", label: "Argument[this]" },
|
||||
...argumentsList.map((argument, index) => ({
|
||||
value: `Argument[${index}]`,
|
||||
label: `Argument[${index}]: ${argument}`,
|
||||
})),
|
||||
],
|
||||
[argumentsList],
|
||||
);
|
||||
|
||||
const showKindCell = predicate?.supportedKinds;
|
||||
|
||||
return (
|
||||
@@ -165,62 +199,32 @@ export const MethodRow = ({
|
||||
</ApiOrMethodCell>
|
||||
<VSCodeDataGridCell gridColumn={2}>
|
||||
<Dropdown
|
||||
value={showModelTypeCell ? modeledMethod?.type ?? "none" : undefined}
|
||||
value={modeledMethod?.type ?? "none"}
|
||||
options={modelTypeOptions}
|
||||
disabled={!showModelTypeCell}
|
||||
onChange={handleTypeInput}
|
||||
>
|
||||
{showModelTypeCell && (
|
||||
<>
|
||||
<option value="none">Unmodeled</option>
|
||||
<option value="source">Source</option>
|
||||
<option value="sink">Sink</option>
|
||||
<option value="summary">Flow summary</option>
|
||||
<option value="neutral">Neutral</option>
|
||||
</>
|
||||
)}
|
||||
</Dropdown>
|
||||
/>
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={3}>
|
||||
<Dropdown
|
||||
value={showInputCell ? modeledMethod?.input : undefined}
|
||||
value={modeledMethod?.input}
|
||||
options={inputOptions}
|
||||
disabled={!showInputCell}
|
||||
onChange={handleInputInput}
|
||||
>
|
||||
{showInputCell && (
|
||||
<>
|
||||
<option value="Argument[this]">Argument[this]</option>
|
||||
{argumentsList.map((argument, index) => (
|
||||
<option key={argument} value={`Argument[${index}]`}>
|
||||
Argument[{index}]: {argument}
|
||||
</option>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Dropdown>
|
||||
/>
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={4}>
|
||||
<Dropdown
|
||||
value={showOutputCell ? modeledMethod?.output : undefined}
|
||||
value={modeledMethod?.output}
|
||||
options={outputOptions}
|
||||
disabled={!showOutputCell}
|
||||
onChange={handleOutputInput}
|
||||
>
|
||||
{showOutputCell && (
|
||||
<>
|
||||
<option value="ReturnValue">ReturnValue</option>
|
||||
<option value="Argument[this]">Argument[this]</option>
|
||||
{argumentsList.map((argument, index) => (
|
||||
<option key={argument} value={`Argument[${index}]`}>
|
||||
Argument[{index}]: {argument}
|
||||
</option>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Dropdown>
|
||||
/>
|
||||
</VSCodeDataGridCell>
|
||||
<VSCodeDataGridCell gridColumn={5}>
|
||||
<KindInput
|
||||
kinds={predicate?.supportedKinds || []}
|
||||
value={showKindCell && modeledMethod?.kind}
|
||||
value={modeledMethod?.kind}
|
||||
disabled={!showKindCell}
|
||||
onChange={handleKindChange}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user