Skip to content

Commit 4da578f

Browse files
committed
Allow dragging the roadmap canvas background to pan
1 parent a1e7869 commit 4da578f

3 files changed

Lines changed: 29 additions & 1 deletion

File tree

‎doc/architecture/SPEC/rfc_service.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
正文展示在 Markdown 解析前移除独立 HTML 注释(包括 roadmap 内部元数据),并保留分段,
1616
避免紧邻表格的注释变成可见数据行。草稿原文不变;围栏和缩进代码中的注释仍展示,原始 HTML 仍禁用。
17+
路线图空白区域通过 Pointer Events 拖动滚动视口,节点仍保留点击和键盘操作;取消或释放指针结束拖动,重置同时恢复缩放和滚动位置。
1718

1819
## 功能与模块边界
1920

‎src/infermatrix_copilot/rfc_service/web/app.js‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,29 @@ function updateGraphNodes(graphs) {
491491
});
492492
}
493493
}
494+
function enableGraphPanning(canvas) {
495+
let drag = null;
496+
canvas.addEventListener("pointerdown", event => {
497+
if (!event.isPrimary || event.button !== 0 || event.target.closest(".roadmap-actionable,a,button")) return;
498+
drag = {id: event.pointerId, x: event.clientX, y: event.clientY, left: canvas.scrollLeft, top: canvas.scrollTop};
499+
canvas.setPointerCapture(event.pointerId);
500+
canvas.classList.add("is-panning");
501+
event.preventDefault();
502+
});
503+
canvas.addEventListener("pointermove", event => {
504+
if (!drag || drag.id !== event.pointerId) return;
505+
canvas.scrollLeft = drag.left + drag.x - event.clientX;
506+
canvas.scrollTop = drag.top + drag.y - event.clientY;
507+
event.preventDefault();
508+
});
509+
const stop = event => {
510+
if (!drag || drag.id !== event.pointerId) return;
511+
drag = null;
512+
canvas.classList.remove("is-panning");
513+
if (canvas.hasPointerCapture(event.pointerId)) canvas.releasePointerCapture(event.pointerId);
514+
};
515+
for (const type of ["pointerup", "pointercancel", "lostpointercapture"]) canvas.addEventListener(type, stop);
516+
}
494517
function dependencyGraph(rfc) {
495518
const models = graphModels(rfc);
496519
const sources = models.map(model => graphSource(model, translated, true));
@@ -507,6 +530,9 @@ function dependencyGraph(rfc) {
507530
activeGraphs = graphs;
508531
for (const [modelIndex, model] of models.entries()) {
509532
const canvas = element("div", {class: "graph roadmap-canvas"}, element("p", {class: "muted"}, "正在绘制路线图…"));
533+
canvas.setAttribute("tabindex", "0");
534+
canvas.setAttribute("aria-label", `${model.title}:拖动空白区域平移,点击节点查看详情`);
535+
enableGraphPanning(canvas);
510536
const controls = element("div", {class: "actions"});
511537
const legend = element("div", {class: "graph-legend"}, ...["planned", "in_progress", "partial", "implemented", "accepted"].map(state => element("span", {class: `graph-key ${state}`}, state === "accepted" ? "已验收" : translated(state))));
512538
const section = element("section", {class: "roadmap-track"}, element("div", {class: "panel-title"}, element("h3", {}, model.title), controls), legend, canvas);
@@ -558,7 +584,7 @@ function dependencyGraph(rfc) {
558584
const zoom = delta => { scale = Math.max(.4, Math.min(2.5, scale + delta)); drawing.setAttribute("width", String(width * scale)); };
559585
const minus = button("−", () => zoom(-.2)); minus.setAttribute("aria-label", `${model.title} 缩小`);
560586
const plus = button("+", () => zoom(.2)); plus.setAttribute("aria-label", `${model.title} 放大`);
561-
controls.append(minus, plus, button("重置", () => { scale = 1; drawing.setAttribute("width", String(width)); }), button("下载 SVG", () => {
587+
controls.append(minus, plus, button("重置", () => { scale = 1; drawing.setAttribute("width", String(width)); canvas.scrollLeft = canvas.scrollTop = 0; }), button("下载 SVG", () => {
562588
const exported = drawing.cloneNode(true);
563589
const style = document.createElementNS("http://www.w3.org/2000/svg", "style");
564590
style.textContent = roadmapSVGStyles;

‎src/infermatrix_copilot/rfc_service/web/style.css‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)