summaryrefslogtreecommitdiff
path: root/src/components/PackageSwitcher.tsx
blob: 38c19673dcd4bd25febd0bf4fcd83d049324d9bf (plain) (blame)
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>
  );
}