fix(editor): drag interactions, undo history, overflow clipping
Four critical fixes: - Remove overflow:hidden on tlRef so Timeline.init's scroll survives re-renders - Don't call _renderClips() inside mousedown (was destroying event target mid-drag) - Use refs for undo history to eliminate stale closure in onClipsChanged callback - Change .tl-clip-area overflow:hidden to overflow:visible so pointer events reach clip edges
This commit is contained in:
parent
4673efac6a
commit
3dad82d992
2 changed files with 23 additions and 10 deletions
|
|
@ -94,7 +94,7 @@
|
||||||
var area = document.createElement('div');
|
var area = document.createElement('div');
|
||||||
area.className = 'tl-clip-area';
|
area.className = 'tl-clip-area';
|
||||||
area.dataset.trackId = t.id;
|
area.dataset.trackId = t.id;
|
||||||
area.style.cssText = 'flex:1;position:relative;overflow:hidden;';
|
area.style.cssText = 'flex:1;position:relative;overflow:visible;';
|
||||||
area.addEventListener('click', _onAreaClick);
|
area.addEventListener('click', _onAreaClick);
|
||||||
row.appendChild(area);
|
row.appendChild(area);
|
||||||
|
|
||||||
|
|
@ -269,8 +269,12 @@
|
||||||
// Drag to move (body only)
|
// Drag to move (body only)
|
||||||
el.addEventListener('mousedown', function (e) {
|
el.addEventListener('mousedown', function (e) {
|
||||||
if (e.target === lh || e.target === rh) return;
|
if (e.target === lh || e.target === rh) return;
|
||||||
|
e.preventDefault();
|
||||||
s.selectedId = clip._id;
|
s.selectedId = clip._id;
|
||||||
_renderClips();
|
// Update selection highlight without destroying the element we're dragging
|
||||||
|
s.tracksEl.querySelectorAll('.tl-clip').forEach(function (c) {
|
||||||
|
c.style.borderColor = c.dataset.clipId === clip._id ? 'var(--accent)' : 'var(--border-strong)';
|
||||||
|
});
|
||||||
_onMoveStart(e, clip);
|
_onMoveStart(e, clip);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@ function Editor() {
|
||||||
const streamCacheRef = React.useRef({});
|
const streamCacheRef = React.useRef({});
|
||||||
|
|
||||||
const tlInitRef = React.useRef(false);
|
const tlInitRef = React.useRef(false);
|
||||||
|
// Refs so Timeline callbacks always read current values without stale closure issues
|
||||||
|
const historyRef = React.useRef([[]]);
|
||||||
|
const historyIdxRef = React.useRef(0);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const data = window.ZAMPP_DATA;
|
const data = window.ZAMPP_DATA;
|
||||||
|
|
@ -78,6 +81,8 @@ function Editor() {
|
||||||
setCurrentSeq(seq);
|
setCurrentSeq(seq);
|
||||||
setPlayheadFrames(0);
|
setPlayheadFrames(0);
|
||||||
setSelectedClipId(null);
|
setSelectedClipId(null);
|
||||||
|
historyRef.current = [clips];
|
||||||
|
historyIdxRef.current = 0;
|
||||||
setHistory([clips]);
|
setHistory([clips]);
|
||||||
setHistoryIdx(0);
|
setHistoryIdx(0);
|
||||||
setIsDirty(false);
|
setIsDirty(false);
|
||||||
|
|
@ -130,9 +135,11 @@ function Editor() {
|
||||||
|
|
||||||
function handleClipsChanged(clips) {
|
function handleClipsChanged(clips) {
|
||||||
setCurrentSeq(prev => prev ? { ...prev, clips } : prev);
|
setCurrentSeq(prev => prev ? { ...prev, clips } : prev);
|
||||||
const newHistory = history.slice(0, historyIdx + 1);
|
const newHistory = historyRef.current.slice(0, historyIdxRef.current + 1);
|
||||||
newHistory.push(clips.map(c => ({ ...c })));
|
newHistory.push(clips.map(c => ({ ...c })));
|
||||||
if (newHistory.length > 50) newHistory.shift();
|
if (newHistory.length > 50) newHistory.shift();
|
||||||
|
historyRef.current = newHistory;
|
||||||
|
historyIdxRef.current = newHistory.length - 1;
|
||||||
setHistory(newHistory);
|
setHistory(newHistory);
|
||||||
setHistoryIdx(newHistory.length - 1);
|
setHistoryIdx(newHistory.length - 1);
|
||||||
markDirty();
|
markDirty();
|
||||||
|
|
@ -173,20 +180,22 @@ function Editor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function undo() {
|
function undo() {
|
||||||
if (historyIdx <= 0) return;
|
if (historyIdxRef.current <= 0) return;
|
||||||
const idx = historyIdx - 1;
|
const idx = historyIdxRef.current - 1;
|
||||||
|
historyIdxRef.current = idx;
|
||||||
setHistoryIdx(idx);
|
setHistoryIdx(idx);
|
||||||
const clips = (history[idx] || []).map(c => ({ ...c, _id: _uid() }));
|
const clips = (historyRef.current[idx] || []).map(c => ({ ...c, _id: _uid() }));
|
||||||
setCurrentSeq(prev => prev ? { ...prev, clips } : prev);
|
setCurrentSeq(prev => prev ? { ...prev, clips } : prev);
|
||||||
renderTimelineClips(clips);
|
renderTimelineClips(clips);
|
||||||
markDirty();
|
markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
function redo() {
|
function redo() {
|
||||||
if (historyIdx >= history.length - 1) return;
|
if (historyIdxRef.current >= historyRef.current.length - 1) return;
|
||||||
const idx = historyIdx + 1;
|
const idx = historyIdxRef.current + 1;
|
||||||
|
historyIdxRef.current = idx;
|
||||||
setHistoryIdx(idx);
|
setHistoryIdx(idx);
|
||||||
const clips = (history[idx] || []).map(c => ({ ...c, _id: _uid() }));
|
const clips = (historyRef.current[idx] || []).map(c => ({ ...c, _id: _uid() }));
|
||||||
setCurrentSeq(prev => prev ? { ...prev, clips } : prev);
|
setCurrentSeq(prev => prev ? { ...prev, clips } : prev);
|
||||||
renderTimelineClips(clips);
|
renderTimelineClips(clips);
|
||||||
markDirty();
|
markDirty();
|
||||||
|
|
@ -356,7 +365,7 @@ function Editor() {
|
||||||
}} />
|
}} />
|
||||||
<span style={{ fontSize: 9, color: 'var(--text-tertiary)', fontFamily: 'monospace', width: 28 }}>{scale}px</span>
|
<span style={{ fontSize: 9, color: 'var(--text-tertiary)', fontFamily: 'monospace', width: 28 }}>{scale}px</span>
|
||||||
</div>
|
</div>
|
||||||
<div ref={tlRef} className="timeline-container" style={{ flex: 1, overflow: 'hidden' }} />
|
<div ref={tlRef} className="timeline-container" style={{ flex: 1, minHeight: 0 }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue