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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
import { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
import type { PackageSlot } from '../hooks/usePackages';
interface Props {
slots: PackageSlot[];
activeId: string;
onSwitch: (id: string) => void;
onCreate: () => void;
onRemove: (id: string) => void;
onRename: (id: string, name: string) => void;
onDuplicate: (id: string) => void;
}
interface MenuState {
id: string;
top: number;
left: number;
}
export function PackageSwitcher({ slots, activeId, onSwitch, onCreate, onRemove, onRename, onDuplicate }: Props) {
const [editingId, setEditingId] = useState<string | null>(null);
const [draft, setDraft] = useState('');
const [menu, setMenu] = useState<MenuState | null>(null);
// Close dropdown on any outside click
useEffect(() => {
if (!menu) return;
const handler = () => setMenu(null);
document.addEventListener('click', handler);
return () => document.removeEventListener('click', handler);
}, [menu]);
const openMenu = (id: string, e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
if (menu?.id === id) { setMenu(null); return; }
const rect = e.currentTarget.getBoundingClientRect();
setMenu({ id, top: rect.bottom + 4, left: rect.left });
};
const commitRename = (id: string) => {
const name = draft.trim();
if (name) onRename(id, name);
setEditingId(null);
};
return (
<div className="pkg-switcher">
{slots.map(slot => (
<div
key={slot.id}
className={`pkg-tab${slot.id === activeId ? ' active' : ''}`}
onClick={() => slot.id !== activeId && onSwitch(slot.id)}
>
{editingId === slot.id ? (
<input
autoFocus
className="pkg-tab-input"
value={draft}
onChange={e => setDraft(e.target.value)}
onBlur={() => commitRename(slot.id)}
onKeyDown={e => {
if (e.key === 'Enter') commitRename(slot.id);
if (e.key === 'Escape') setEditingId(null);
}}
onClick={e => e.stopPropagation()}
/>
) : (
<span
className="pkg-tab-name"
onDoubleClick={e => {
e.stopPropagation();
setDraft(slot.name);
setEditingId(slot.id);
}}
>
{slot.name}
</span>
)}
<div className="pkg-tab-actions">
<button
className="pkg-tab-menu-btn"
title="Options"
onClick={e => openMenu(slot.id, e)}
>
···
</button>
{slots.length > 1 && (
<button
className="pkg-tab-close"
title="Remove"
onClick={e => { e.stopPropagation(); onRemove(slot.id); }}
>
×
</button>
)}
</div>
</div>
))}
<button className="pkg-new-btn" onClick={onCreate} title="New package">+</button>
{/* Dropdown rendered in a portal to escape overflow clipping */}
{menu && createPortal(
<div
className="pkg-tab-dropdown"
style={{ position: 'fixed', top: menu.top, left: menu.left }}
onClick={e => e.stopPropagation()}
>
<button onClick={() => {
const slot = slots.find(s => s.id === menu.id);
if (slot) { setDraft(slot.name); setEditingId(slot.id); }
setMenu(null);
}}>Rename</button>
<button onClick={() => { onDuplicate(menu.id); setMenu(null); }}>Duplicate</button>
{slots.length > 1 && (
<button className="danger" onClick={() => { onRemove(menu.id); setMenu(null); }}>Delete</button>
)}
</div>,
document.body
)}
</div>
);
}
|