Header now behaves like this:

- 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.
This commit is contained in:
Haapy
2026-05-15 11:44:42 +00:00
parent 092d27beab
commit a831b22213
2 changed files with 50 additions and 11 deletions

View File

@@ -76,10 +76,20 @@
font-family: inherit; font-family: inherit;
font-size: inherit; font-size: inherit;
padding: 0.15em 0; padding: 0.15em 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card-title--empty {
opacity: 0.35;
}
.card-title--editing {
cursor: text; cursor: text;
} }
.card-title::placeholder { .card-title--editing::placeholder {
color: var(--text); color: var(--text);
opacity: 0.35; opacity: 0.35;
} }

View File

@@ -1,4 +1,4 @@
import React from "react"; import React, { useEffect, useRef, useState } from "react";
interface Props { interface Props {
title: string; title: string;
@@ -9,16 +9,45 @@ interface Props {
} }
export function CardHeader({ title, placeholder, onTitleChange, onClose, dragProps }: Props) { export function CardHeader({ title, placeholder, onTitleChange, onClose, dragProps }: Props) {
const [editing, setEditing] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (editing && inputRef.current) {
inputRef.current.focus();
inputRef.current.select();
}
}, [editing]);
const beginEdit = (e: React.MouseEvent) => {
e.preventDefault();
setEditing(true);
};
return ( return (
<div className="card-header" {...dragProps}> <div className="card-header" {...dragProps} onContextMenu={beginEdit}>
{editing ? (
<input <input
className="card-title" ref={inputRef}
className="card-title card-title--editing"
value={title} value={title}
placeholder={placeholder} placeholder={placeholder}
spellCheck={false} spellCheck={false}
onChange={(e) => onTitleChange(e.target.value)} onChange={(e) => onTitleChange(e.target.value)}
onBlur={() => setEditing(false)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === "Escape") {
e.preventDefault();
setEditing(false);
}
}}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
/> />
) : (
<span className={`card-title ${title ? "" : "card-title--empty"}`}>
{title || placeholder}
</span>
)}
<button <button
type="button" type="button"
className="card-close" className="card-close"