阻止预览拖动触发浏览器文字选择

为预览节点层增加 select-none 样式

拖动起始阶段阻止浏览器默认选择行为

补充原生选字防回归测试并同步文档
This commit is contained in:
2026-09-03 14:36:49 +08:00
parent f10fc2f032
commit d225fdae01
5 changed files with 26 additions and 1 deletions
@@ -125,7 +125,7 @@ function RenderNode({
return (
<div
data-node-id={node.id}
className={`absolute min-h-0 min-w-0 ${isRoot ? 'cursor-default' : 'cursor-move'}`}
className={`absolute min-h-0 min-w-0 select-none ${isRoot ? 'cursor-default' : 'cursor-move'}`}
style={{
...geometry,
...(parentContainer
@@ -190,6 +190,7 @@ export function useNodeTransformInteraction({
const dragNode =
selectedNode && selectedNode.id !== tree?.root.id ? selectedNode : node;
if (!isFiniteTransform(dragNode.layout.transform)) return;
event.preventDefault();
event.stopPropagation();
event.currentTarget.setPointerCapture(event.pointerId);
suppressNextNodeClickRef.current = false;
@@ -88,6 +88,16 @@ function renderTree(
}
describe('UI tree preview visibility', () => {
it('disables native text selection on preview node layers', () => {
const rendered = renderTree('editor-overlay', new Set());
expect(
rendered.container
.querySelector('[data-node-id="child"]')
?.classList.contains('select-none'),
).toBe(true);
});
it('does not use click alone to select directly draggable nodes', () => {
const onSelectNode = vi.fn();
const rendered = renderTree('editor-overlay', new Set(), { onSelectNode });
@@ -119,6 +119,18 @@ function renderInteraction({
}
describe('useNodeTransformInteraction', () => {
it('prevents the browser default when starting a node drag', () => {
const { result } = renderInteraction();
const target = gestureTarget();
const event = pointerEvent(target, 1, 0, 0);
act(() => {
result.current.onNodePointerDown(event, child);
});
expect(event.preventDefault).toHaveBeenCalledTimes(1);
});
it('moves the current selection when dragging another node', () => {
const selected = node('selected');
const treeWithSelection: UITree = {
@@ -42,3 +42,5 @@ Container 专属数据是互斥 tagged unionHBox/VBox 存 `alignment + separa
布局只在 Inspector 编辑。Preview 不为 Container 管理的 child 提供拖拽或缩放手柄;Inspector 会提示其 Transform 被父 Container 忽略。普通 Control 仍保留原 Transform 编辑和自由预览操作。
Preview 的节点选择采用“松开确认”语义:节点或调整手柄按下时只建立指针手势,不立即改变选择;沿用现有 `2px` 屏幕位移阈值(达到阈值即视为拖动),未达到阈值且在预览区域内松开时才选择按下命中的节点,发生拖动、取消、失焦或在区域外松开均不选择。发生拖动时,Transform 更新作用于按下前已选中的节点;没有当前选择时才回退到按下节点。拖动外层节点时,嵌套 child 产生的后续 click 也不得重新选择;未发生拖动时,嵌套 child 仍按实际命中节点选择。
预览节点层禁止浏览器原生文字选择;拖动手势开始时阻止默认行为,避免 pointer drag 被浏览器解释为文本选取。