UI④:元素盘点击落子真能落 —— 原型里这条路是坏的
事件流实测:click → drag.init → create.start → create.move → **create.rejected**。 create.move 要有 hover 目标才算 canExecute,而 click 到达时鼠标早已抬起, 拖拽在同一个调用栈里就被拒 —— 工具提示写着「点击后点画布落子」,一颗子也落不下。 点击这条路改为直接 modeling.createShape 到空位(撞车往下让、一列满换列); 拖拽那条路不动。实测:落子 32→33、追加自动连线 33→34、撤销回退干净。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -441,36 +441,92 @@ export const PALETTE_SPECS: Array<{ key: string; label: string; className: strin
|
|||||||
];
|
];
|
||||||
|
|
||||||
class FlowPalette {
|
class FlowPalette {
|
||||||
static $inject = ["palette", "create", "elementFactory", "elementRegistry", "flowEditorConfig"];
|
static $inject = ["palette", "create", "elementFactory", "elementRegistry", "flowEditorConfig", "modeling", "canvas"];
|
||||||
|
|
||||||
private _create: Any;
|
private _create: Any;
|
||||||
private _elementFactory: Any;
|
private _elementFactory: Any;
|
||||||
private _elementRegistry: Any;
|
private _elementRegistry: Any;
|
||||||
private _config: Any;
|
private _config: Any;
|
||||||
|
private _modeling: Any;
|
||||||
|
private _canvas: Any;
|
||||||
|
|
||||||
constructor(palette: Any, create: Any, elementFactory: Any, elementRegistry: Any, flowEditorConfig: Any) {
|
constructor(
|
||||||
|
palette: Any,
|
||||||
|
create: Any,
|
||||||
|
elementFactory: Any,
|
||||||
|
elementRegistry: Any,
|
||||||
|
flowEditorConfig: Any,
|
||||||
|
modeling: Any,
|
||||||
|
canvas: Any,
|
||||||
|
) {
|
||||||
this._create = create;
|
this._create = create;
|
||||||
this._elementFactory = elementFactory;
|
this._elementFactory = elementFactory;
|
||||||
this._elementRegistry = elementRegistry;
|
this._elementRegistry = elementRegistry;
|
||||||
this._config = flowEditorConfig;
|
this._config = flowEditorConfig;
|
||||||
|
this._modeling = modeling;
|
||||||
|
this._canvas = canvas;
|
||||||
palette.registerProvider(this);
|
palette.registerProvider(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 造一枚待落的新节点(id 现取,避开已用与保留号) */
|
||||||
|
private _mint(spec: NewNodeSpec): Any {
|
||||||
|
const bo = specNode(spec);
|
||||||
|
bo.id = mintNodeId(spec.kind, usedIds(this._elementRegistry), this._config.reserved);
|
||||||
|
const size = MANUAL_SIZES[spec.kind];
|
||||||
|
return this._elementFactory.createShape({ width: size.w, height: size.h, businessObject: bo });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击落子的落点:视口中心起,撞车就往下让一格,直到空位。
|
||||||
|
*
|
||||||
|
* 为什么不复用拖拽那条路:`create.start` 靠鼠标**移动**取 hover 目标才算 `canExecute`,
|
||||||
|
* 而 click 事件到达时鼠标早已抬起 —— 拖拽在同一个调用栈里就以 `create.rejected` 收场,
|
||||||
|
* 一颗子也落不下(实测)。所以点击这条路直接建,不走拖拽。
|
||||||
|
*/
|
||||||
|
private _freeSpot(shape: Any): { x: number; y: number } {
|
||||||
|
const viewbox = this._canvas.viewbox();
|
||||||
|
let x = snap(viewbox.x + viewbox.width / 2);
|
||||||
|
let y = snap(viewbox.y + Math.min(viewbox.height / 2, 200));
|
||||||
|
const hits = (px: number, py: number) =>
|
||||||
|
this._elementRegistry
|
||||||
|
.getAll()
|
||||||
|
.some(
|
||||||
|
(el: Any) =>
|
||||||
|
!el.waypoints &&
|
||||||
|
el.parent &&
|
||||||
|
!el.labelTarget &&
|
||||||
|
Math.abs(el.x + el.width / 2 - px) < (el.width + shape.width) / 2 + 20 &&
|
||||||
|
Math.abs(el.y + el.height / 2 - py) < (el.height + shape.height) / 2 + 20,
|
||||||
|
);
|
||||||
|
let guard = 0;
|
||||||
|
while (hits(x, y) && guard < 40) {
|
||||||
|
y = snap(y + 70);
|
||||||
|
guard += 1;
|
||||||
|
// 一列排满就换到右边一列重来,别无限往下掉出视野
|
||||||
|
if (guard % 8 === 0) {
|
||||||
|
x = snap(x + 160);
|
||||||
|
y = snap(viewbox.y + Math.min(viewbox.height / 2, 200));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { x, y };
|
||||||
|
}
|
||||||
|
|
||||||
getPaletteEntries(): Record<string, Any> {
|
getPaletteEntries(): Record<string, Any> {
|
||||||
const entries: Record<string, Any> = {};
|
const entries: Record<string, Any> = {};
|
||||||
for (const item of PALETTE_SPECS) {
|
for (const item of PALETTE_SPECS) {
|
||||||
const action = (event: Any) => {
|
|
||||||
const bo = specNode(item.spec);
|
|
||||||
bo.id = mintNodeId(item.spec.kind, usedIds(this._elementRegistry), this._config.reserved);
|
|
||||||
const size = MANUAL_SIZES[item.spec.kind];
|
|
||||||
const shape = this._elementFactory.createShape({ width: size.w, height: size.h, businessObject: bo });
|
|
||||||
this._create.start(event, shape);
|
|
||||||
};
|
|
||||||
entries[item.key] = {
|
entries[item.key] = {
|
||||||
group: "flow",
|
group: "flow",
|
||||||
className: item.className,
|
className: item.className,
|
||||||
title: `${item.label} — 拖到画布或点击后点画布落子`,
|
title: `${item.label} — 拖到画布,或直接点击(落在空位上,再拖到想要的地方)`,
|
||||||
action: { dragstart: action, click: action },
|
action: {
|
||||||
|
// 拖:鼠标一路带着走,落点由手定
|
||||||
|
dragstart: (event: Any) => this._create.start(event, this._mint(item.spec)),
|
||||||
|
// 点:当场落在空位上 —— 见 `_freeSpot` 注释,点击走不通拖拽那条路
|
||||||
|
click: () => {
|
||||||
|
const shape = this._mint(item.spec);
|
||||||
|
this._modeling.createShape(shape, this._freeSpot(shape), this._canvas.getRootElement());
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return entries;
|
return entries;
|
||||||
|
|||||||
Reference in New Issue
Block a user