148 lines · 5.3 KB · TypeScript
RawDownload
import type { EnvironmentId } from "@t3tools/contracts";import { ScaleIcon } from "lucide-react";import { memo, useMemo } from "react"; import type { EnvironmentOption } from "./BranchToolbar.logic";import { EnvironmentMachineIcon } from "./EnvironmentMachineIcon";import { composerFloatingLayerProps } from "./chat/composerEventScope";import {  Select,  SelectGroup,  SelectGroupLabel,  SelectItem,  SelectPopup,  SelectTrigger,  SelectValue,} from "./ui/select"; interface BranchToolbarEnvironmentSelectorProps {  autoEnvironmentLabel?: string | undefined;  onAutoEnvironment?: (() => void) | undefined;  envLocked: boolean;  environmentId: EnvironmentId;  availableEnvironments: readonly EnvironmentOption[];  // Absent when there is only one environment to show: the indicator still  // renders (as a static label) so remote projects are always identifiable.  onEnvironmentChange?: (environmentId: EnvironmentId) => void;} export const BranchToolbarEnvironmentSelector = memo(function BranchToolbarEnvironmentSelector({  autoEnvironmentLabel,  onAutoEnvironment,  envLocked,  environmentId,  availableEnvironments,  onEnvironmentChange,}: BranchToolbarEnvironmentSelectorProps) {  const activeEnvironment = useMemo(() => {    return availableEnvironments.find((env) => env.environmentId === environmentId) ?? null;  }, [availableEnvironments, environmentId]);   const environmentItems = useMemo(    () => [      ...(onAutoEnvironment        ? [{ value: "auto", label: autoEnvironmentLabel ?? "Auto balance" }]        : []),      ...availableEnvironments.map((env) => ({        value: env.environmentId,        label: env.label,      })),    ],    [availableEnvironments, autoEnvironmentLabel, onAutoEnvironment],  );   // The static label carries the xs control's height (h-7 sm:h-6) as well as  // its padding: the composer context strip has no min-height of its own, and  // the glass seam joining it to the composer assumes a fixed strip height, so  // a shorter label would drag the seam out of line whenever this label is the  // only thing in the strip.  if (envLocked || onEnvironmentChange === undefined) {    return (      <span        className="inline-flex h-7 min-w-0 max-w-full items-center gap-1 border border-transparent px-[calc(--spacing(2)-1px)] font-normal text-muted-foreground/70 text-xs sm:h-6"        data-composer-context-control      >        <EnvironmentMachineIcon          kind={activeEnvironment?.machine ?? "server"}          className="size-3 shrink-0"        />        <span          data-composer-label          className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"        >          <span            data-composer-label-motion            className="block w-full min-w-0 max-w-[240px] truncate transition-opacity duration-180 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[compact]/composer-context:opacity-0 motion-reduce:transition-none"          >            {activeEnvironment?.label ?? "Run on"}          </span>        </span>      </span>    );  }   return (    <Select      modal={false}      value={autoEnvironmentLabel ? "auto" : environmentId}      onValueChange={(value) =>        value === "auto" ? onAutoEnvironment?.() : onEnvironmentChange(value as EnvironmentId)      }      items={environmentItems}    >      <SelectTrigger        variant="ghost"        size="xs"        className="min-w-0 max-w-full font-normal text-xs!"        aria-label="Run on"        data-composer-context-control      >        {autoEnvironmentLabel ? (          <ScaleIcon className="size-3 shrink-0" aria-hidden="true" />        ) : (          <EnvironmentMachineIcon            kind={activeEnvironment?.machine ?? "server"}            className="size-3 shrink-0"          />        )}        <span          data-composer-label          className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"        >          <span            data-composer-label-motion            className="block w-full min-w-0 max-w-[240px] truncate transition-opacity duration-180 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[compact]/composer-context:opacity-0 motion-reduce:transition-none"          >            <SelectValue />          </span>        </span>      </SelectTrigger>      <SelectPopup alignItemWithTrigger={false} {...composerFloatingLayerProps}>        <SelectGroup>          <SelectGroupLabel>Run on</SelectGroupLabel>          {onAutoEnvironment && (            <SelectItem              value="auto"              onClick={() => {                if (autoEnvironmentLabel) onAutoEnvironment?.();              }}            >              <span className="inline-flex items-center gap-1.5">                <ScaleIcon className="size-3" aria-hidden="true" />                {autoEnvironmentLabel ?? "Auto balance"}              </span>            </SelectItem>          )}          {availableEnvironments.map((env) => (            <SelectItem key={env.environmentId} value={env.environmentId}>              <span className="inline-flex items-center gap-1.5">                <EnvironmentMachineIcon kind={env.machine} className="size-3" />                {env.label}              </span>            </SelectItem>          ))}        </SelectGroup>      </SelectPopup>    </Select>  );});