-
Notifications
You must be signed in to change notification settings - Fork 96
Expand file tree
/
Copy pathAddSectionButton.tsx
More file actions
110 lines (100 loc) · 3.41 KB
/
AddSectionButton.tsx
File metadata and controls
110 lines (100 loc) · 3.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import React, { useState, useRef, useEffect } from "react";
import { Plus } from "lucide-react";
// import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip";
interface AddSectionButtonProps {
onCreateSection: (name: string) => Promise<boolean>;
}
const alignWithSectionCaretStyle: React.CSSProperties = {
borderLeftWidth: 3,
borderLeftColor: "transparent",
};
export const AddSectionButton: React.FC<AddSectionButtonProps> = ({ onCreateSection }) => {
const [isCreating, setIsCreating] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [name, setName] = useState("");
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (isCreating && inputRef.current) {
inputRef.current.focus();
}
}, [isCreating]);
const handleSubmit = async () => {
if (isSubmitting) {
return;
}
const trimmed = name.trim();
if (!trimmed) {
setName("");
setIsCreating(false);
return;
}
setIsSubmitting(true);
try {
// Keep the input open until creation succeeds so backend/IPC failures do not
// look like they created a section successfully.
const didCreateSection = await onCreateSection(trimmed);
if (!didCreateSection) {
return;
}
setName("");
setIsCreating(false);
} catch {
// The caller owns error presentation; keep the current draft visible so the
// user can retry instead of losing their typed section name.
} finally {
setIsSubmitting(false);
}
};
const submitWithoutThrowing = () => {
handleSubmit().catch(() => undefined);
};
if (isCreating) {
return (
<div
// Match the section header's reserved 3px color rail so the add affordance's
// plus icon stays horizontally aligned with the section caret.
className="flex items-center gap-1 px-2 py-0.5"
style={alignWithSectionCaretStyle}
>
<div className="flex h-5 w-5 shrink-0 items-center justify-center">
<Plus size={12} className="text-muted/60" />
</div>
<input
ref={inputRef}
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
disabled={isSubmitting}
onBlur={submitWithoutThrowing}
onKeyDown={(e) => {
if (e.key === "Enter") {
submitWithoutThrowing();
}
if (e.key === "Escape") {
setName("");
setIsCreating(false);
}
}}
placeholder="Section name..."
data-testid="add-section-input"
className="bg-background/50 text-foreground min-w-0 flex-1 rounded border border-white/20 px-1.5 py-0.5 text-[11px] outline-none select-text"
/>
</div>
);
}
return (
<button
onClick={() => setIsCreating(true)}
data-testid="add-section-button"
// Keep the affordance in the same icon/text columns as section rows so the
// add-sub-folder action reads as part of the project hierarchy.
className="text-muted/60 hover:text-muted flex w-full cursor-pointer items-center gap-1 border-none bg-transparent px-2 py-0.5 text-left text-[11px] transition-colors"
style={alignWithSectionCaretStyle}
>
<div className="flex h-5 w-5 shrink-0 items-center justify-center">
<Plus size={12} />
</div>
<span>Add section</span>
</button>
);
};