| 1 | import { X } from "lucide-react"; |
| 2 | import type React from "react"; |
| 3 | import { useEffect, useState } from "react"; |
| 4 | |
| 5 | import { ChartTypePicker } from "./chart-type-picker"; |
| 6 | import { type SeriesChartType } from "./types"; |
| 7 | |
| 8 | interface EditableHeaderProps { |
| 9 | value: string; |
| 10 | onRename: (newName: string) => void; |
| 11 | onRemove: () => void; |
| 12 | canRemove: boolean; |
| 13 | colorIndex?: number; |
| 14 | chartType?: SeriesChartType; |
| 15 | onChartTypeChange?: (type: SeriesChartType) => void; |
| 16 | showChartTypePicker?: boolean; |
| 17 | } |
| 18 | |
| 19 | export function EditableHeader({ |
| 20 | value, |
| 21 | onRename, |
| 22 | onRemove, |
| 23 | canRemove, |
| 24 | colorIndex = 0, |
| 25 | chartType = "bar", |
| 26 | onChartTypeChange, |
| 27 | showChartTypePicker = false, |
| 28 | }: EditableHeaderProps) { |
| 29 | const [localValue, setLocalValue] = useState(value); |
| 30 | const [isEditing, setIsEditing] = useState(false); |
| 31 | |
| 32 | useEffect(() => { |
| 33 | setLocalValue(value); |
| 34 | }, [value]); |
| 35 | |
| 36 | const commitHeaderRename = () => { |
| 37 | setIsEditing(false); |
| 38 | if (localValue.trim() && localValue !== value) { |
| 39 | onRename(localValue); |
| 40 | } else if (!localValue.trim()) { |
| 41 | setLocalValue(value); |
| 42 | } |
| 43 | }; |
| 44 | |
| 45 | const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { |
| 46 | if (e.key === "Enter") { |
| 47 | e.currentTarget.blur(); |
| 48 | } |
| 49 | if (e.key === "Escape") { |
| 50 | setLocalValue(value); |
| 51 | e.currentTarget.blur(); |
| 52 | } |
| 53 | }; |
| 54 | |
| 55 | return ( |
| 56 | <div className="group flex items-center gap-1"> |
| 57 | <div |
| 58 | className="h-4 w-2 shrink-0 rounded-sm" |
| 59 | style={{ backgroundColor: `hsl(var(--chart-${(colorIndex % 5) + 1}))` }} |
| 60 | /> |
| 61 | {showChartTypePicker && onChartTypeChange && ( |
| 62 | <ChartTypePicker value={chartType} onChange={onChartTypeChange} /> |
| 63 | )} |
| 64 | <input |
| 65 | aria-label="editable header control" |
| 66 | value={localValue} |
| 67 | onChange={(e) => setLocalValue(e.target.value)} |
| 68 | onBlur={commitHeaderRename} |
| 69 | onFocus={() => setIsEditing(true)} |
| 70 | onKeyDown={handleKeyDown} |
| 71 | className={`h-6 min-w-0 flex-1 border-0 bg-transparent px-1 text-xs font-semibold outline-none ${isEditing ? "bg-background ring ring-primary" : ""} truncate focus:bg-background focus:ring focus:ring-primary`} |
| 72 | placeholder="Series" |
| 73 | /> |
| 74 | {canRemove && ( |
| 75 | <button |
| 76 | type="button" |
| 77 | onClick={onRemove} |
| 78 | className="p-0.5 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100 hover:text-destructive" |
| 79 | > |
| 80 | <X className="size-3" /> |
| 81 | </button> |
| 82 | )} |
| 83 | </div> |
| 84 | ); |
| 85 | } |
| 86 |