mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
chore: improve the design of the create workspace page for dynamic parameters (#17654)
contributes to coder/preview#59 1. Improves the design and layout of the presets dropdown and switch 2. Improves the design for the immutable badge <img width="537" alt="Screenshot 2025-05-01 at 23 28 11" src="https://github.com/user-attachments/assets/f0967758-5ea7-4436-b44a-e014c048202c" /> <img width="714" alt="Screenshot 2025-05-01 at 23 28 34" src="https://github.com/user-attachments/assets/0bb091e1-611f-4a58-8f6f-b3bb027c6a10" />
This commit is contained in:
@@ -26,10 +26,15 @@ const badgeVariants = cva(
|
||||
sm: "text-2xs font-regular h-5.5 [&_svg]:size-icon-xs",
|
||||
md: "text-xs font-medium [&_svg]:size-icon-sm",
|
||||
},
|
||||
border: {
|
||||
none: "border-transparent",
|
||||
solid: "border border-solid",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "md",
|
||||
border: "solid",
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -41,14 +46,14 @@ export interface BadgeProps
|
||||
}
|
||||
|
||||
export const Badge = forwardRef<HTMLDivElement, BadgeProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
({ className, variant, size, border, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "div";
|
||||
|
||||
return (
|
||||
<Comp
|
||||
{...props}
|
||||
ref={ref}
|
||||
className={cn(badgeVariants({ variant, size }), className)}
|
||||
className={cn(badgeVariants({ variant, size, border }), className)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -106,7 +106,7 @@ const ParameterLabel: FC<ParameterLabelProps> = ({ parameter, isPreset }) => {
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="flex items-center">
|
||||
<Badge size="sm" variant="warning">
|
||||
<Badge size="sm" variant="warning" border="none">
|
||||
<TriangleAlert />
|
||||
Immutable
|
||||
</Badge>
|
||||
|
||||
@@ -5,12 +5,17 @@ import { ErrorAlert } from "components/Alert/ErrorAlert";
|
||||
import { Avatar } from "components/Avatar/Avatar";
|
||||
import { Button } from "components/Button/Button";
|
||||
import { FeatureStageBadge } from "components/FeatureStageBadge/FeatureStageBadge";
|
||||
import { SelectFilter } from "components/Filter/SelectFilter";
|
||||
import { Input } from "components/Input/Input";
|
||||
import { Label } from "components/Label/Label";
|
||||
import { Pill } from "components/Pill/Pill";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "components/Select/Select";
|
||||
import { Spinner } from "components/Spinner/Spinner";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { Switch } from "components/Switch/Switch";
|
||||
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete";
|
||||
import { type FormikContextType, useFormik } from "formik";
|
||||
@@ -153,11 +158,11 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
}, [form.submitCount, form.errors]);
|
||||
|
||||
const [presetOptions, setPresetOptions] = useState([
|
||||
{ label: "None", value: "" },
|
||||
{ label: "None", value: "None" },
|
||||
]);
|
||||
useEffect(() => {
|
||||
setPresetOptions([
|
||||
{ label: "None", value: "" },
|
||||
{ label: "None", value: "None" },
|
||||
...presets.map((preset) => ({
|
||||
label: preset.Name,
|
||||
value: preset.ID,
|
||||
@@ -421,7 +426,7 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
)}
|
||||
|
||||
{parameters.length > 0 && (
|
||||
<section className="flex flex-col gap-6">
|
||||
<section className="flex flex-col gap-9">
|
||||
<hgroup>
|
||||
<h2 className="text-xl font-semibold m-0">Parameters</h2>
|
||||
<p className="text-sm text-content-secondary m-0">
|
||||
@@ -429,30 +434,39 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
parameters cannot be modified once the workspace is created.
|
||||
</p>
|
||||
</hgroup>
|
||||
<Diagnostics diagnostics={diagnostics} />
|
||||
{diagnostics.length > 0 && (
|
||||
<Diagnostics diagnostics={diagnostics} />
|
||||
)}
|
||||
{presets.length > 0 && (
|
||||
<Stack direction="column" spacing={2}>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex gap-2 items-center">
|
||||
<Label className="text-sm">Preset</Label>
|
||||
<FeatureStageBadge contentType={"beta"} size="md" />
|
||||
</div>
|
||||
<div className="flex">
|
||||
<SelectFilter
|
||||
label="Preset"
|
||||
options={presetOptions}
|
||||
onSelect={(option) => {
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex gap-2 items-center">
|
||||
<Label className="text-sm">Preset</Label>
|
||||
<FeatureStageBadge contentType={"beta"} size="md" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="max-w-lg">
|
||||
<Select
|
||||
onValueChange={(option) => {
|
||||
const index = presetOptions.findIndex(
|
||||
(preset) => preset.value === option?.value,
|
||||
(preset) => preset.value === option,
|
||||
);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
setSelectedPresetIndex(index);
|
||||
}}
|
||||
placeholder="Select a preset"
|
||||
selectedOption={presetOptions[selectedPresetIndex]}
|
||||
/>
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={"Select a preset"} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{presetOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<span className="flex items-center gap-3">
|
||||
<Switch
|
||||
@@ -465,7 +479,7 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
</Label>
|
||||
</span>
|
||||
</div>
|
||||
</Stack>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-9">
|
||||
|
||||
Reference in New Issue
Block a user