From a831b2221337049f1b1feea7ab3266f26a608c1e Mon Sep 17 00:00:00 2001 From: Haapy Date: Fri, 15 May 2026 11:44:42 +0000 Subject: [PATCH] Header now behaves like this: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Left-click + drag on the header → moves the card (the title is now a span, so the drag handler always wins). - Right-click on the header → swaps to an input, auto-focused with text selected. - Enter or Escape or click outside → exits edit mode. - Empty title shows the dimmed placeholder (Note / Terminal) and overflowing titles get an ellipsis. Browser's native context menu is suppressed on the header via e.preventDefault(), but only there — right-clicking elsewhere is unaffected. --- src/canvas/canvas.css | 12 +++++++- src/canvas/cards/CardHeader.tsx | 49 ++++++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/canvas/canvas.css b/src/canvas/canvas.css index 8f321d2..e6eea98 100644 --- a/src/canvas/canvas.css +++ b/src/canvas/canvas.css @@ -76,10 +76,20 @@ font-family: inherit; font-size: inherit; padding: 0.15em 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.card-title--empty { + opacity: 0.35; +} + +.card-title--editing { cursor: text; } -.card-title::placeholder { +.card-title--editing::placeholder { color: var(--text); opacity: 0.35; } diff --git a/src/canvas/cards/CardHeader.tsx b/src/canvas/cards/CardHeader.tsx index 82647a4..52ce6aa 100644 --- a/src/canvas/cards/CardHeader.tsx +++ b/src/canvas/cards/CardHeader.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useRef, useState } from "react"; interface Props { title: string; @@ -9,16 +9,45 @@ interface Props { } export function CardHeader({ title, placeholder, onTitleChange, onClose, dragProps }: Props) { + const [editing, setEditing] = useState(false); + const inputRef = useRef(null); + + useEffect(() => { + if (editing && inputRef.current) { + inputRef.current.focus(); + inputRef.current.select(); + } + }, [editing]); + + const beginEdit = (e: React.MouseEvent) => { + e.preventDefault(); + setEditing(true); + }; + return ( -
- onTitleChange(e.target.value)} - onPointerDown={(e) => e.stopPropagation()} - /> +
+ {editing ? ( + onTitleChange(e.target.value)} + onBlur={() => setEditing(false)} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === "Escape") { + e.preventDefault(); + setEditing(false); + } + }} + onPointerDown={(e) => e.stopPropagation()} + /> + ) : ( + + {title || placeholder} + + )}