Files
kandev/apps/web/components/quick-chat/quick-terminal-tab-item.tsx
T
2026-08-27 16:33:46 +00:00

137 lines
3.8 KiB
TypeScript

"use client";
import { memo } from "react";
import { IconAlertCircle, IconTerminal2, IconX } from "@tabler/icons-react";
import { useTranslation } from "react-i18next";
import { useResponsiveBreakpoint } from "@/hooks/use-responsive-breakpoint";
import {
QuickChatTabActionMenu,
QuickChatTabMoveButtons,
type QuickChatTabDragProps,
} from "./quick-chat-tab-item";
type QuickTerminalTabItemProps = {
sequence: number;
isActive: boolean;
error?: string;
onActivate: () => void;
onClose: () => void;
onMoveLeft?: () => void;
onMoveRight?: () => void;
canMoveLeft?: boolean;
canMoveRight?: boolean;
dragProps?: QuickChatTabDragProps;
};
type QuickTerminalTabActionsProps = {
label: string;
closeLabel: string;
onClose: () => void;
onMoveLeft?: () => void;
onMoveRight?: () => void;
canMoveLeft: boolean;
canMoveRight: boolean;
};
function QuickTerminalTabActions({
label,
closeLabel,
onClose,
onMoveLeft,
onMoveRight,
canMoveLeft,
canMoveRight,
}: QuickTerminalTabActionsProps) {
const { isFinePointer } = useResponsiveBreakpoint();
return (
<div className="flex shrink-0 items-center">
{!isFinePointer && (
<QuickChatTabActionMenu
name={label}
closeLabel={closeLabel}
onMoveLeft={onMoveLeft}
onMoveRight={onMoveRight}
canMoveLeft={canMoveLeft}
canMoveRight={canMoveRight}
onClose={onClose}
/>
)}
{isFinePointer && onMoveLeft && onMoveRight && (
<QuickChatTabMoveButtons
name={label}
onMoveLeft={onMoveLeft}
onMoveRight={onMoveRight}
canMoveLeft={canMoveLeft}
canMoveRight={canMoveRight}
/>
)}
{isFinePointer && (
<button
type="button"
aria-label={closeLabel}
title={closeLabel}
className="flex h-11 w-11 shrink-0 cursor-pointer items-center justify-center opacity-60 hover:opacity-100 sm:h-6 sm:w-6"
onClick={onClose}
>
<IconX className="h-3 w-3" aria-hidden />
</button>
)}
</div>
);
}
/** Fixed-label tab for a browser-local quick terminal. */
export const QuickTerminalTabItem = memo(function QuickTerminalTabItem({
sequence,
isActive,
error,
onActivate,
onClose,
onMoveLeft,
onMoveRight,
canMoveLeft = true,
canMoveRight = true,
dragProps,
}: QuickTerminalTabItemProps) {
const { t } = useTranslation();
const label = t("sidebar:quickChatTerminalTab", { count: sequence });
const closeLabel = t("sidebar:quickChatCloseTerminal", { count: sequence });
return (
<div
ref={dragProps?.setActivatorNodeRef}
{...(dragProps?.attributes ?? {})}
{...(dragProps?.listeners ?? {})}
data-testid="quick-terminal-tab"
data-terminal-sequence={sequence}
className={`flex shrink-0 items-center gap-1 rounded transition-colors ${
isActive
? "bg-background text-foreground shadow-sm"
: "text-muted-foreground hover:bg-muted"
} ${dragProps ? "cursor-grab active:cursor-grabbing" : ""}`}
>
<button
type="button"
onClick={onActivate}
aria-label={label}
aria-current={isActive ? "page" : undefined}
className="flex h-11 min-w-0 cursor-pointer items-center gap-1.5 px-2.5 text-xs sm:h-6"
>
<IconTerminal2 className="h-3.5 w-3.5 shrink-0" aria-hidden />
<span>{label}</span>
{error && <IconAlertCircle className="h-3.5 w-3.5 shrink-0" aria-hidden />}
</button>
<QuickTerminalTabActions
label={label}
closeLabel={closeLabel}
onClose={onClose}
onMoveLeft={onMoveLeft}
onMoveRight={onMoveRight}
canMoveLeft={canMoveLeft}
canMoveRight={canMoveRight}
/>
</div>
);
});