import React from "react"; import { useI18n } from "../application/i18n/I18nProvider"; import { DEFAULT_HOST_ICON_COLOR, DEFAULT_HOST_ICON_ID, HOST_ICON_COLORS, HOST_ICON_IDS, isHostIconColorId, isHostIconCustomColor, isHostIconId, resolveHostIconDefaultColorHex, } from "../domain/hostIcon"; import type { Host } from "../domain/models"; import type { HostIconColorId, HostIconColorMode, HostIconId, HostIconMode } from "../domain/models"; import { cn } from "../lib/utils"; import { DistroAvatar } from "./DistroAvatar"; import { renderHostIconGlyph } from "./hostIconRenderer"; import { Input } from "./ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"; type DistroOption = { value: string; label: string; icon?: string; bgClass: string; }; type HostIconPickerProps = { previewHost?: Host; distroMode?: "auto" | "manual"; manualDistro?: string; effectiveDistro?: string; distroOptions: DistroOption[]; getDistroOptionLabel: (value?: string) => string; iconMode?: HostIconMode; iconId?: HostIconId; iconColorMode?: HostIconColorMode; iconColor?: HostIconColorId; iconColorCustom?: string; manualIconMenuOpen?: boolean; onChange: (next: { distroMode?: "auto" | "manual"; manualDistro?: string; iconMode?: HostIconMode; iconId?: HostIconId; iconColorMode?: HostIconColorMode; iconColor?: HostIconColorId; iconColorCustom?: string; }) => void; }; const renderBrandOption = (option: DistroOption) => (
{option.icon ? ( {option.label} ) : (
)}
{option.label}
); const renderTypeOption = (iconId: HostIconId, label: string, colorHex: string) => (
{renderHostIconGlyph(iconId, "h-3 w-3")}
{label}
); const renderBrandColorPreview = (option: DistroOption, label: string) => (
{label}
); const renderTypeColorPreview = (label: string, colorHex: string) => (
{label}
); const colorHexForId = (colorId?: HostIconColorId) => HOST_ICON_COLORS.find((color) => color.id === colorId)?.hex || HOST_ICON_COLORS[0].hex; export const HostIconPicker: React.FC = ({ previewHost, distroMode, manualDistro, effectiveDistro, distroOptions, getDistroOptionLabel, iconMode, iconId, iconColorMode, iconColor, iconColorCustom, manualIconMenuOpen, onChange, }) => { const { t } = useI18n(); const manualSource = distroMode === "manual" || iconMode === "custom"; const selectedBrandId = manualDistro || effectiveDistro || "linux"; const selectedBrand = distroOptions.find((option) => option.value === selectedBrandId); const selectedIconId = isHostIconId(iconId) ? iconId : DEFAULT_HOST_ICON_ID; const selectedIconTab = iconMode === "custom" ? "type" : "brand"; const manualColor = iconColorMode === "manual" || (iconColorMode !== "auto" && (isHostIconColorId(iconColor) || isHostIconCustomColor(iconColorCustom))); const selectedPresetColor = isHostIconColorId(iconColor) ? iconColor : DEFAULT_HOST_ICON_COLOR; const selectedCustomColor = isHostIconCustomColor(iconColorCustom) ? iconColorCustom : colorHexForId(selectedPresetColor); const selectedColorValue = isHostIconCustomColor(iconColorCustom) ? "custom" : selectedPresetColor; const manualPreviewColor = isHostIconCustomColor(iconColorCustom) ? selectedCustomColor : colorHexForId(selectedPresetColor); const selectedTypePreviewColor = manualColor ? manualPreviewColor : resolveHostIconDefaultColorHex(selectedIconId); const autoColorPreviewLabel = selectedIconTab === "type" ? t(`hostDetails.icon.option.${selectedIconId}`) : getDistroOptionLabel(selectedBrandId); const selectedManualIconValue = selectedIconTab === "type" ? `type:${selectedIconId}` : `brand:${selectedBrandId}`; const [customColorDraft, setCustomColorDraft] = React.useState(selectedCustomColor); const [iconMenuTab, setIconMenuTab] = React.useState<"brand" | "type">(selectedIconTab); React.useEffect(() => { setCustomColorDraft(selectedCustomColor); }, [selectedCustomColor]); React.useEffect(() => { setIconMenuTab(selectedIconTab); }, [selectedIconTab]); const setSource = (value: "auto" | "manual") => { if (value === "auto") { onChange({ distroMode: "auto", iconMode: undefined, iconId: undefined }); return; } onChange({ distroMode: "manual", manualDistro: selectedBrandId, iconMode: undefined, iconId: undefined, }); }; const setManualIcon = (value: string) => { if (value.startsWith("type:")) { const nextIconId = value.slice("type:".length); if (isHostIconId(nextIconId)) { onChange({ distroMode: "manual", iconMode: "custom", iconId: nextIconId }); } return; } if (value.startsWith("brand:")) { onChange({ distroMode: "manual", manualDistro: value.slice("brand:".length), iconMode: undefined, iconId: undefined, }); } }; const switchIconMenuTab = (nextTab: "brand" | "type") => { setIconMenuTab(nextTab); }; const setColorMode = (value: "auto" | "manual") => { if (value === "auto") { onChange({ iconColorMode: "auto", iconColor: undefined, iconColorCustom: undefined }); return; } onChange({ iconColorMode: "manual", iconColor: selectedPresetColor, iconColorCustom: undefined }); }; const setColorChoice = (value: string) => { if (value === "custom") { onChange({ iconColorMode: "manual", iconColor: undefined, iconColorCustom: selectedCustomColor }); return; } if (isHostIconColorId(value)) { onChange({ iconColorMode: "manual", iconColor: value, iconColorCustom: undefined }); } }; const setCustomColor = (value: string) => { setCustomColorDraft(value); if (isHostIconCustomColor(value)) { onChange({ iconColorMode: "manual", iconColor: undefined, iconColorCustom: value, }); } }; return (
{previewHost ? (
) : null}
{t("hostDetails.icon.source")}
{manualSource ? (
{t("hostDetails.icon.manualLabel")}
) : (
{t("hostDetails.distro.detectedLabel")}
{effectiveDistro ? getDistroOptionLabel(effectiveDistro) : t("hostDetails.distro.unknown")}
)}
{t("hostDetails.icon.colorLabel")}
{manualColor ? (
{t("hostDetails.icon.colorChoice")}
) : (
{t("hostDetails.icon.autoColorLabel")}
{selectedIconTab === "type" ? renderTypeColorPreview(autoColorPreviewLabel, selectedTypePreviewColor) : selectedBrand ? renderBrandColorPreview(selectedBrand, autoColorPreviewLabel) : renderTypeColorPreview(t("hostDetails.distro.unknown"), colorHexForId(DEFAULT_HOST_ICON_COLOR))}
)}
{manualColor && selectedColorValue === "custom" && (
setCustomColor(event.target.value)} className="h-8 w-12 shrink-0 cursor-pointer p-1" aria-label={t("hostDetails.icon.color.custom")} /> setCustomColor(event.target.value)} className="h-8 font-mono text-xs" aria-label={t("hostDetails.icon.color.custom")} />
)}
); };