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-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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}>
|
||||||
<input
|
{editing ? (
|
||||||
className="card-title"
|
<input
|
||||||
value={title}
|
ref={inputRef}
|
||||||
placeholder={placeholder}
|
className="card-title card-title--editing"
|
||||||
spellCheck={false}
|
value={title}
|
||||||
onChange={(e) => onTitleChange(e.target.value)}
|
placeholder={placeholder}
|
||||||
onPointerDown={(e) => e.stopPropagation()}
|
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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="card-close"
|
className="card-close"
|
||||||
|
|||||||
Reference in New Issue
Block a user