树根拖拽的指针取消不再提交偏移
PreviewWorkspace.tsx 拆出 handleTreePointerCancel:pointercancel 只清拖拽状态与指针捕获,不再按抬手坐标算最终偏移,避免浏览器取消手势时把意外偏移写进文档 tests/previewRightPanDrag.test.tsx 补「抬起提交、取消不提交」用例,并给画布夹具补上 setTreeOffset 桩
This commit is contained in:
+16
-1
@@ -588,6 +588,21 @@ export function PreviewWorkspace({
|
||||
event.currentTarget.releasePointerCapture(event.pointerId);
|
||||
};
|
||||
|
||||
/** 浏览器发起的指针取消不是正常抬起:只清拖拽状态与捕获,不写回树偏移。 */
|
||||
const handleTreePointerCancel = (
|
||||
event: ReactPointerEvent<HTMLDivElement>,
|
||||
) => {
|
||||
const drag = treeDragRef.current;
|
||||
if (!drag || drag.pointerId !== event.pointerId) return;
|
||||
treeDragRef.current = null;
|
||||
setPreviewTreeOffsets((current) => {
|
||||
const next = new Map(current);
|
||||
next.delete(drag.treeId);
|
||||
return next;
|
||||
});
|
||||
releasePointerCapture(event.currentTarget, event.pointerId);
|
||||
};
|
||||
|
||||
const handleWheel = (event: React.WheelEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
const rect = event.currentTarget.getBoundingClientRect();
|
||||
@@ -702,7 +717,7 @@ export function PreviewWorkspace({
|
||||
}
|
||||
onPointerMove={handleTreePointerMove}
|
||||
onPointerUp={handleTreePointerUp}
|
||||
onPointerCancel={handleTreePointerUp}
|
||||
onPointerCancel={handleTreePointerCancel}
|
||||
>
|
||||
{showOriginImage && previewUrls[item.src_ui_design] ? (
|
||||
<img
|
||||
|
||||
@@ -117,6 +117,7 @@ function createCanvas(
|
||||
insertNode: vi.fn(),
|
||||
insertNodeAfter: vi.fn(),
|
||||
deleteNode: vi.fn(),
|
||||
setTreeOffset: vi.fn(),
|
||||
openClearDialog: vi.fn(),
|
||||
...overrides,
|
||||
};
|
||||
@@ -206,6 +207,64 @@ afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('PreviewWorkspace tree drag', () => {
|
||||
function renderTreeDrag() {
|
||||
const rendered = renderPreview();
|
||||
const treeLayer = rendered.container.querySelector(
|
||||
'[data-tree-id]',
|
||||
) as HTMLDivElement;
|
||||
const rootNode = treeLayer.querySelector(
|
||||
'[data-node-id="root"]',
|
||||
) as HTMLDivElement;
|
||||
return { ...rendered, rootNode, treeLayer };
|
||||
}
|
||||
|
||||
it('commits the tree offset on pointerup but not on pointercancel', () => {
|
||||
const committed = renderTreeDrag();
|
||||
dispatchPointer(committed.rootNode, 'pointerdown', {
|
||||
button: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
pointerId: 11,
|
||||
});
|
||||
// 指针捕获在树容器上,后续指针事件由浏览器派发给捕获元素。
|
||||
dispatchPointer(committed.treeLayer, 'pointermove', {
|
||||
clientX: 40,
|
||||
clientY: 50,
|
||||
pointerId: 11,
|
||||
});
|
||||
dispatchPointer(committed.treeLayer, 'pointerup', {
|
||||
button: 0,
|
||||
clientX: 40,
|
||||
clientY: 50,
|
||||
pointerId: 11,
|
||||
});
|
||||
expect(committed.canvas.setTreeOffset).toHaveBeenCalledTimes(1);
|
||||
expect(committed.canvas.setTreeOffset.mock.calls[0]?.[0]).toBe('page-1');
|
||||
cleanup();
|
||||
|
||||
const cancelled = renderTreeDrag();
|
||||
dispatchPointer(cancelled.rootNode, 'pointerdown', {
|
||||
button: 0,
|
||||
clientX: 10,
|
||||
clientY: 10,
|
||||
pointerId: 12,
|
||||
});
|
||||
dispatchPointer(cancelled.treeLayer, 'pointermove', {
|
||||
clientX: 40,
|
||||
clientY: 50,
|
||||
pointerId: 12,
|
||||
});
|
||||
dispatchPointer(cancelled.treeLayer, 'pointercancel', {
|
||||
button: 0,
|
||||
clientX: 40,
|
||||
clientY: 50,
|
||||
pointerId: 12,
|
||||
});
|
||||
expect(cancelled.canvas.setTreeOffset).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('PreviewWorkspace right-button pan', () => {
|
||||
it('pans the viewport on a right drag and never opens the node menu', () => {
|
||||
const { canvas, child, container, preview } = renderPreview();
|
||||
|
||||
Reference in New Issue
Block a user