mirror of
https://github.com/PR0M3TH3AN/Artstr-Studio.git
synced 2026-10-27 08:40:39 +00:00
Text layers: actually enter edit mode on second click (defer focus + relookup)
The previous attempt added a focus() call in the pointerup stop() branch, but it never stuck. CDP-driven tests against the real app showed two reasons the focus kept getting clobbered: 1. The click event still fires AFTER pointerup, propagates up to the .freeLayer's click handler, which re-runs selectLayerWithEvent + renderLayerList + syncLayerInputs. Something in that chain blurs the contenteditable (focus log: focusin → blur → focusout, all on the .textLayer, in that order). 2. The selection change between the two clicks triggers a render() somewhere — which rebuilds the layers and DETACHES the original .textLayer node that startLayerDrag's closure captured. So even when focus did briefly land on textTarget, that textTarget was already a stale, detached element by the next tick. Fix: - Defer the focus call via setTimeout(0), so it runs AFTER the click event chain completes and any blur-on-click side effects have fired. - Inside the deferred callback, re-query the live .textLayer for the layer's id via `.freeLayer[data-layer-id="..."] .textLayer`, so we never focus a detached element. - Only apply the click-position caret range if it's inside the LIVE editable (caretRangeFromPoint can resolve into the detached node if the layout hasn't reflowed yet). Verified end-to-end via CDP + Input.dispatchMouseEvent against the running app (with the welcome modal dismissed and a fresh ?n=… cache-bust). Two single clicks in sequence: - After click 1: state.selectedLayerId='tx1', activeElement=BODY. - After click 2: activeElement is a DIV with className='textLayer' and isContentEditable=true — caret is now in the text and the user can drag-select / type / apply the toolbar buttons. Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
1 parent
df45b63f45
commit
dc31b023d5
1 file changed
+31
-19
+31
-19
@@ -14077,27 +14077,39 @@
|
||||
setTimeout(() => { delete el.dataset.dragMoved; }, 0);
|
||||
} else if (textTarget && wasSoleSelected) {
|
||||
// Click without drag on a text layer that was already the lone
|
||||
// selection → drop into edit mode. Focus the contenteditable and
|
||||
// place the caret at the click position so the user can start
|
||||
// selecting / typing immediately. This is the standard "select
|
||||
// once, click again to edit" pattern from PowerPoint / Keynote.
|
||||
textTarget.focus();
|
||||
// selection → drop into edit mode. Two subtleties:
|
||||
// 1. The click event still has to fire on .freeLayer after this
|
||||
// pointerup, and that handler ends up clobbering the focus.
|
||||
// So defer with setTimeout(0) to run after the click chain.
|
||||
// 2. The selection change may have triggered a re-render that
|
||||
// detaches the original textTarget. Look the layer's .textLayer
|
||||
// up by id when the deferred handler runs, so we always focus
|
||||
// the live DOM node and not a stale reference.
|
||||
const layerId = layer.id;
|
||||
const cx = (upEvent && typeof upEvent.clientX === 'number') ? upEvent.clientX : startX;
|
||||
const cy = (upEvent && typeof upEvent.clientY === 'number') ? upEvent.clientY : startY;
|
||||
try {
|
||||
let range = null;
|
||||
if (typeof document.caretRangeFromPoint === 'function') {
|
||||
range = document.caretRangeFromPoint(cx, cy);
|
||||
} else if (typeof document.caretPositionFromPoint === 'function') {
|
||||
const pos = document.caretPositionFromPoint(cx, cy);
|
||||
if (pos) { range = document.createRange(); range.setStart(pos.offsetNode, pos.offset); range.collapse(true); }
|
||||
}
|
||||
if (range) {
|
||||
const sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
} catch (e) {}
|
||||
setTimeout(() => {
|
||||
const live = document.querySelector(`.freeLayer[data-layer-id="${layerId}"] .textLayer`);
|
||||
if (!live) return;
|
||||
live.focus();
|
||||
try {
|
||||
let range = null;
|
||||
if (typeof document.caretRangeFromPoint === 'function') {
|
||||
range = document.caretRangeFromPoint(cx, cy);
|
||||
} else if (typeof document.caretPositionFromPoint === 'function') {
|
||||
const pos = document.caretPositionFromPoint(cx, cy);
|
||||
if (pos) { range = document.createRange(); range.setStart(pos.offsetNode, pos.offset); range.collapse(true); }
|
||||
}
|
||||
// Only use the caret position if it's inside the live editable
|
||||
// — caretRangeFromPoint can return a position in the previous
|
||||
// (now detached) .textLayer.
|
||||
if (range && live.contains(range.startContainer)) {
|
||||
const sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
} catch (e) {}
|
||||
}, 0);
|
||||
}
|
||||
localStorage.setItem('case-cover-template-maker-autosave', JSON.stringify(state));
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user