mirror of
https://github.com/PR0M3TH3AN/Artstr-Studio.git
synced 2026-11-01 19:20:40 +00:00
Text layers: click already-selected text to enter edit mode
The previous click flow only entered edit mode on a true double-click (event.detail > 1 on the second pointerdown), which is timing- sensitive — slow second clicks fell through to layer-drag and edit mode was hard to reach. Result: users clicked the text and the whole layer kept "selecting" as an object. Three changes to startLayerDrag to fix it: 1. Capture `wasSoleSelected = isLayerSelected(layer.id) && selectedLayerIds().length === 1` at the top of the handler, BEFORE the selection logic runs. This tells us whether the user was clicking on a text layer that was already the lone selection — the standard signal in PowerPoint / Keynote / Google Slides that the user wants to edit the text now. 2. A small dead-zone in the move handler (Math.hypot(dx, dy) < 4 ignores tiny jitter). Without it, a click with a 1-pixel mouse wobble flipped `moved` to true and skipped the edit-mode path. 3. New branch in stop() — if the pointer was released without crossing the dead-zone (moved === false), the click target was inside .textLayer, and wasSoleSelected was true, focus the contenteditable and place the caret at the click position via document.caretRangeFromPoint (with a caretPositionFromPoint fallback for Firefox-style browsers). Net behaviour: - First click on an unselected text layer → selects (drag handles appear, no edit mode). - Second click on the same now-selected text layer → enters edit mode, caret lands where the click was. - Click + drag on a selected text layer → drags normally (moved > 4px, edit-mode branch skipped). - Existing double-click path (event.detail > 1) still works as a belt-and-suspenders fallback for very fast double-clicks. CDP synthesized-event tests couldn't fully drive the pointer- capture path so end-to-end verification needs a real browser; the logic was code-reviewed. Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
1 parent
aaf044a946
commit
df45b63f45
1 file changed
+39
-3
+39
-3
@@ -13993,6 +13993,11 @@
|
||||
if (layer.locked) { setLayerSelection([layer.id], layer.id); render(); return; }
|
||||
const textTarget = event.target.closest('.textLayer');
|
||||
if (!textTarget) blurEditableFocus();
|
||||
// Capture BEFORE we touch selection state: was this text layer
|
||||
// already the lone-selected layer when the user clicked it? If so,
|
||||
// a click-without-drag should drop the user into edit mode (the
|
||||
// standard slide-editor UX). Stashed for the pointerup branch below.
|
||||
const wasSoleSelected = isLayerSelected(layer.id) && selectedLayerIds().length === 1;
|
||||
const additiveSelect = !!(event.shiftKey || event.ctrlKey || event.metaKey);
|
||||
if (additiveSelect) {
|
||||
event.preventDefault();
|
||||
@@ -14029,9 +14034,17 @@
|
||||
el.setPointerCapture(event.pointerId);
|
||||
|
||||
const move = (moveEvent) => {
|
||||
// Small dead-zone so a click with a tiny pixel jitter isn't
|
||||
// interpreted as a drag. Anything inside this radius is ignored;
|
||||
// the user can still enter text-edit mode via the pointerup branch.
|
||||
if (!moved) {
|
||||
const ddx = moveEvent.clientX - startX;
|
||||
const ddy = moveEvent.clientY - startY;
|
||||
if (Math.hypot(ddx, ddy) < 4) return;
|
||||
}
|
||||
if (!snapshotted) {
|
||||
// Capture the pre-drag state on the first pointermove so a click
|
||||
// without movement doesn't pollute the undo stack.
|
||||
// Capture the pre-drag state on the first real pointermove so a
|
||||
// click without movement doesn't pollute the undo stack.
|
||||
pushHistory(dragLayers.length > 1 ? 'move selected layers' : 'move layer');
|
||||
snapshotted = true;
|
||||
}
|
||||
@@ -14054,7 +14067,7 @@
|
||||
syncLayerInputs();
|
||||
};
|
||||
|
||||
const stop = () => {
|
||||
const stop = (upEvent) => {
|
||||
document.body.classList.remove('dragging-layer');
|
||||
el.removeEventListener('pointermove', move);
|
||||
el.removeEventListener('pointerup', stop);
|
||||
@@ -14062,6 +14075,29 @@
|
||||
if (moved) {
|
||||
el.dataset.dragMoved = '1';
|
||||
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();
|
||||
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) {}
|
||||
}
|
||||
localStorage.setItem('case-cover-template-maker-autosave', JSON.stringify(state));
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user