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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (editing && inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
inputRef.current.select();
|
||||
}
|
||||
}, [editing]);
|
||||
|
||||
const beginEdit = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
setEditing(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="card-header" {...dragProps}>
|
||||
<input
|
||||
className="card-title"
|
||||
value={title}
|
||||
placeholder={placeholder}
|
||||
spellCheck={false}
|
||||
onChange={(e) => onTitleChange(e.target.value)}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
/>
|
||||
<div className="card-header" {...dragProps} onContextMenu={beginEdit}>
|
||||
{editing ? (
|
||||
<input
|
||||
ref={inputRef}
|
||||
className="card-title card-title--editing"
|
||||
value={title}
|
||||
placeholder={placeholder}
|
||||
spellCheck={false}
|
||||
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()}
|
||||
/>
|
||||
) : (
|
||||
<span className={`card-title ${title ? "" : "card-title--empty"}`}>
|
||||
{title || placeholder}
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="card-close"
|
||||
|
||||
Reference in New Issue
Block a user