Skip to main content
Glama
EditableBadge.tsx3.56 kB
import React, { useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Check, Edit3, X } from "lucide-react"; interface EditableBadgeProps { label?: React.ReactNode; value: string; onSave: (newValue: string) => void; onCancel?: () => void; badgeClassName?: string; // Tailwind classes to style the badge validate?: (nextValue: string) => string | null; // return error message or null if valid } export const EditableBadge: React.FC<EditableBadgeProps> = ({ label, value, onSave, onCancel, badgeClassName = "", validate, }) => { const [isEditing, setIsEditing] = useState(false); const [draft, setDraft] = useState(value); const [error, setError] = useState<string | null>(null); const handleStart = () => { setDraft(value); setError(null); setIsEditing(true); }; const handleConfirm = () => { const maybeError = validate ? validate(draft) : null; if (maybeError) { setError(maybeError); return; } onSave(draft); setIsEditing(false); setError(null); }; const handleCancel = () => { setIsEditing(false); setDraft(value); setError(null); onCancel?.(); }; const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { if (e.key === "Enter") handleConfirm(); else if (e.key === "Escape") handleCancel(); }; return ( <div className="flex items-center gap-2"> {label && ( <div className="text-xl font-semibold px-4 py-2 text-gray-900"> {label} </div> )} {isEditing ? ( <div className="inline-flex items-center gap-2 px-4 py-2 rounded-lg text-lg font-medium border bg-white text-gray-900 border-gray-300"> <Input value={draft} onChange={(e) => { setDraft(e.target.value); if (error) setError(null); }} onKeyDown={handleKeyDown} className=" font-medium text-gray-900 px-0 py-0 h-6 border-none shadow-none bg-transparent focus-visible:ring-0 focus-visible:outline-none" autoFocus aria-invalid={!!error} aria-describedby={error ? "editable-badge-error" : undefined} /> <Button variant="ghost" size="sm" onClick={handleConfirm} className="p-1 h-6 w-6" > <Check className="w-3 h-3 text-green-600" /> </Button> <Button variant="ghost" size="sm" onClick={handleCancel} className="p-1 h-6 w-6" > <X className="w-3 h-3 text-red-600" /> </Button> </div> ) : ( <> <div className={`inline-flex items-center px-4 py-2 rounded-lg text- font-medium border ${badgeClassName}`} > {value || "Unnamed"} </div> <button type="button" onClick={handleStart} className="inline-flex items-center justify-center w-6 h-6 rounded-md border border-gray-200 text-gray-600 hover:bg-gray-100" aria-label="Edit" > <Edit3 className="w-3 h-3" /> </button> </> )} {error && ( <div id="editable-badge-error" className="text-xs text-red-600 px-2 py-1 bg-red-50 border border-red-200 rounded" > {error} </div> )} </div> ); }; export default EditableBadge;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TheLunarCompany/lunar'

If you have feedback or need assistance with the MCP directory API, please join our Discord server