Files
invyone/frontend/components/control/ide/ContextBar.tsx
T
DDD1542 2f398ae0b3 chore: 제어모드 IDE 작업 + v2/legacy 레지스트리 컴포넌트 폐기
- 제어모드 IDE: ControlCardPanel, control/ide/* (Canvas/LeftRail/RightRail/PanZoomStage/V3RuleNode 등), schemas, lib/api/control
- 레지스트리 정리: aggregation-widget, status-count, section-card/paper, table-list(legacy/v2), tabs-widget 폐기 → table/_shared/ 로 통합
- InvLegacyButtonConfigPanel cp 마이그레이션
- canonical data view cleanup 후속 노트
2026-05-19 21:31:03 +09:00

138 lines
4.3 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import {
Eye, Pencil, Play, History, Zap, LayoutDashboard,
Save, Undo2, FolderOpen, Search, X,
} from 'lucide-react';
import { useControlMode } from '../hooks/useControlMode';
import { listPresence, type PresenceUser } from '@/lib/api/control';
interface ContextBarProps {
selectedCard: Record<string, any>;
onExit: () => void; // 카드 닫기 (제어 유지)
onCtrlExit: () => void; // 제어 종료
}
const MODE_TABS = [
{ k: 'view' as const, Ic: Eye, label: 'READ' },
{ k: 'edit' as const, Ic: Pencil, label: 'EDIT' },
{ k: 'run' as const, Ic: Play, label: 'RUN' },
{ k: 'history' as const, Ic: History, label: 'HISTORY' },
];
export function ContextBar({ selectedCard, onExit, onCtrlExit }: ContextBarProps) {
const mode = useControlMode((s) => s.mode);
const setMode = useControlMode((s) => s.setMode);
const [presence, setPresence] = useState<PresenceUser[]>([]);
useEffect(() => {
let alive = true;
listPresence('').then((p) => { if (alive) setPresence(p); });
return () => { alive = false; };
}, []);
const tableName = selectedCard.primary_table ?? selectedCard.PRIMARY_TABLE ?? '';
const cardTitle = selectedCard.title ?? selectedCard.TITLE ?? '카드';
const dirtyCount = 0; // TODO 단계 6에서 store 도입
return (
<div className="ctrl-ide-ctxbar">
{/* 좌측 — 배지 + brumb */}
<div className="ctrl-ide-badge">
<Zap size={10} strokeWidth={2.5} />
CONTROL IDE
</div>
<span className="ctrl-ide-sep">/</span>
<button className="ctrl-ide-tool" disabled>
<LayoutDashboard size={11} />
</button>
<span className="ctrl-ide-sep">/</span>
<div className="ctrl-ide-crumb-card">
{cardTitle}
{tableName && <span className="ctrl-ide-crumb-tbl">{tableName}</span>}
</div>
<div style={{ flex: 1 }} />
{/* presence stack — 빈 배열이면 미렌더 */}
{presence.length > 0 && (
<>
<div className="ctrl-presence">
{presence.slice(0, 4).map((p, i) => (
<span
key={i}
className={`ctrl-presence-avatar${p.mode === 'edit' ? ' is-edit' : ''}`}
style={{ background: `rgb(${p.color})` }}
title={`${p.name} · ${p.mode === 'edit' ? '편집중' : '보는중'}`}
>
{p.short}
</span>
))}
{presence.length > 4 && (
<span className="ctrl-presence-more">+{presence.length - 4}</span>
)}
</div>
<span className="ctrl-ide-vsep" aria-hidden="true" />
</>
)}
{/* cmd-k */}
<button className="ctrl-ide-tool ctrl-ide-cmdk" title="명령 팔레트 (⌘K)">
<Search size={10} />
K
</button>
<span className="ctrl-ide-vsep" aria-hidden="true" />
{/* mode 4-segmented tabs */}
<div className="ctrl-ide-mode-tabs">
{MODE_TABS.map(({ k, Ic, label }) => (
<button
key={k}
className={`ctrl-ide-mode-tab${mode === k ? ' on' : ''}`}
onClick={() => setMode(k)}
>
<Ic size={10} />
{label}
</button>
))}
</div>
{/* toolbar */}
<button className="ctrl-ide-tool" title="불러오기">
<FolderOpen size={11} />
<span></span>
</button>
<button className={`ctrl-ide-tool${dirtyCount > 0 ? ' primary' : ''}`} title="저장">
<Save size={11} />
<span>{dirtyCount > 0 ? `저장 · ${dirtyCount}` : '저장'}</span>
</button>
<button className="ctrl-ide-tool" title="되돌리기">
<Undo2 size={11} />
</button>
<span className="ctrl-ide-vsep" aria-hidden="true" />
{/* 카드 닫기 (제어 유지) */}
<button
onClick={onExit}
title="닫고 대시보드로 (제어 유지)"
className="ctrl-ide-tool ctrl-ide-close"
>
<X size={11} />
<span></span>
</button>
{/* 제어 종료 */}
<button
onClick={onCtrlExit}
title="제어 모드 종료"
className="ctrl-ide-tool ctrl-ide-exit"
>
</button>
</div>
);
}