/* eslint-disable */
/* timing-grid.jsx — cue-sheet сетка мероприятия: треки × тайм-слоты × карточки. */
(function () {
const R = window.React;
const { useState, useRef } = R;
const D = window.TimingData;
const TIME_W = 70;
const COL_MIN = 158;
/* ---------- Карточка в ячейке ---------- */
function GridCard({ card, color, onPatch, onDel, dragInfo, onDragState }) {
const [hover, setHover] = useState(false);
return (
{ e.stopPropagation(); dragInfo.current = { type: 'card', id: card.id }; onDragState(card.id); }}
onDragEnd={() => { dragInfo.current = null; onDragState(null); }}
onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
style={{
position: 'relative', background: 'var(--color-surface)',
border: '1px solid var(--color-line-subtle)', borderLeft: '3px solid ' + color,
borderRadius: 6, padding: '6px 8px 7px', cursor: 'grab', boxShadow: 'var(--shadow-card)',
}}>
);
}
/* ---------- Ячейка (drop-зона) ---------- */
function Cell({ track, slot, cards, ops, dragInfo, over, setOver, dragId, onDragState, isLast }) {
const isOver = over && over.t === track.id && over.s === slot.id;
return (
{ e.preventDefault(); setOver({ t: track.id, s: slot.id }); }}
onDrop={e => {
e.preventDefault();
const di = dragInfo.current;
if (di && di.type === 'card') ops.moveCard(di.id, track.id, slot.id);
else if (di && di.type === 'lib') ops.addCardFromLib(track.id, slot.id, di.block);
dragInfo.current = null; setOver(null); onDragState(null);
}}
style={{
borderRight: '1px solid var(--color-line-subtle)',
borderBottom: isLast ? 'none' : '1px solid var(--color-line-subtle)',
background: isOver ? 'rgba(44,58,87,0.05)' : 'transparent',
padding: 6, minHeight: 56, display: 'flex', flexDirection: 'column', gap: 5,
transition: 'background .1s',
}}>
{cards.map(c => (
ops.patchCard(c.id, p)} onDel={() => ops.delCard(c.id)}
dragInfo={dragInfo} onDragState={onDragState} />
))}
);
}
/* ---------- Заголовок трека ---------- */
function TrackHead({ track, ops, canDelete }) {
const [hover, setHover] = useState(false);
const [palette, setPalette] = useState(false);
return (
setHover(true)} onMouseLeave={() => { setHover(false); setPalette(false); }}
style={{
position: 'sticky', top: 0, zIndex: 6, background: 'var(--color-muted-50)',
borderRight: '1px solid var(--color-line-subtle)', borderBottom: '1px solid var(--color-line-strong)',
padding: '8px 8px 8px 10px', display: 'flex', alignItems: 'center', gap: 7,
}}>
ops.patchTrack(track.id, { name: e.target.value })}
style={{ flex: 1, minWidth: 0, border: '1px solid transparent', background: 'transparent', borderRadius: 4, padding: '2px 4px', margin: '-2px -4px', fontSize: 12, fontWeight: 600, color: 'var(--color-ink-900)', outline: 'none' }}
onFocus={e => { e.target.style.background = 'var(--color-surface)'; e.target.style.borderColor = 'var(--color-line-strong)'; }}
onBlur={e => { e.target.style.background = 'transparent'; e.target.style.borderColor = 'transparent'; }}
/>
{hover && canDelete && (
ops.delTrack(track.id)} title="Удалить колонку" style={{ flex: '0 0 auto', width: 18, height: 18, border: 0, borderRadius: 4, background: 'transparent', color: 'var(--color-ink-400)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
)}
);
}
/* ---------- Сетка ---------- */
function EventGrid({ grid, ops, dragInfo }) {
const [over, setOver] = useState(null);
const [dragId, setDragId] = useState(null);
const slots = grid.slots.slice().sort((a, b) => a.time - b.time);
const tracks = grid.tracks;
const cols = TIME_W + 'px repeat(' + tracks.length + ', minmax(' + COL_MIN + 'px, 1fr)) 40px';
return (
{ if (e.target === e.currentTarget) setOver(null); }}>
{/* corner */}
Время
{/* track headers */}
{tracks.map(t =>
1} />)}
{/* add-track header */}
{/* rows */}
{slots.map((slot, i) => {
const next = slots[i + 1];
let delta = next ? next.time - slot.time : 0;
if (!delta) { grid.cards.filter(c => c.slotId === slot.id && c.dur).forEach(c => { delta = Math.max(delta, c.dur); }); }
const isLast = i === slots.length - 1;
return (
{/* time cell */}
1} />
{tracks.map(t => (
c.trackId === t.id && c.slotId === slot.id)}
ops={ops} dragInfo={dragInfo} over={over} setOver={setOver} dragId={dragId} onDragState={setDragId} isLast={isLast} />
))}
{/* spacer under add-track col */}
|
);
})}
{/* add slot row */}
Тайм-слот
);
}
function TimeCell({ slot, delta, ops, isLast, canDelete }) {
const [hover, setHover] = useState(false);
return (
setHover(true)} onMouseLeave={() => setHover(false)}
style={{
position: 'sticky', left: 0, zIndex: 4, background: 'var(--color-canvas)',
borderRight: '1px solid var(--color-line-strong)', borderBottom: isLast ? 'none' : '1px solid var(--color-line-subtle)',
padding: '8px 4px 8px 8px', display: 'flex', flexDirection: 'column', gap: 1,
}}>
ops.patchSlot(slot.id, { time: D.parse(e.target.value) })}
style={{ width: 52, border: '1px solid transparent', background: 'transparent', borderRadius: 4, padding: '1px 3px', fontFamily: 'var(--font-mono)', fontSize: 13, fontWeight: 600, color: 'var(--color-ink-900)', fontVariantNumeric: 'tabular-nums', outline: 'none' }}
onFocus={e => { e.target.style.background = 'var(--color-surface)'; e.target.style.borderColor = 'var(--color-line-strong)'; }}
onBlur={e => { e.target.style.background = 'transparent'; e.target.style.borderColor = 'transparent'; }}
/>
{delta > 0 && +{D.dur(delta)}}
{hover && canDelete && (
ops.delSlot(slot.id)} title="Удалить слот" style={{ marginTop: 2, width: 18, height: 18, border: 0, borderRadius: 4, background: 'transparent', color: 'var(--color-ink-400)', display: 'flex', alignItems: 'center' }}>
)}
);
}
window.TimingGrid = { EventGrid };
})();