@aicupa/plugin-todo-dependency 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/service.js +3 -2
- package/view/index.html +64 -2
package/package.json
CHANGED
package/service.js
CHANGED
|
@@ -121,9 +121,10 @@ module.exports = (api) => {
|
|
|
121
121
|
if (node.todo && node.todo.id === todoId) {
|
|
122
122
|
node.todo.done = done
|
|
123
123
|
if (done) {
|
|
124
|
-
node.todo.
|
|
124
|
+
node.todo.end = Date.now()
|
|
125
|
+
node.todo.focus = false
|
|
125
126
|
} else {
|
|
126
|
-
delete node.todo.
|
|
127
|
+
delete node.todo.end
|
|
127
128
|
}
|
|
128
129
|
return true
|
|
129
130
|
}
|
package/view/index.html
CHANGED
|
@@ -125,6 +125,8 @@
|
|
|
125
125
|
.level-danger { border-color: #ff4d4f; }
|
|
126
126
|
|
|
127
127
|
#edgeSvg { position: absolute; top: 0; left: 0; pointer-events: none; }
|
|
128
|
+
#edgeSvg path { transition: stroke-width 0.2s, opacity 0.2s; }
|
|
129
|
+
@keyframes edge-flow { from { stroke-dashoffset: 18; } to { stroke-dashoffset: 0; } }
|
|
128
130
|
|
|
129
131
|
#graphInner { transition: opacity 0.35s ease; }
|
|
130
132
|
#graphInner.fade-out { opacity: 0; }
|
|
@@ -444,7 +446,11 @@
|
|
|
444
446
|
svg.appendChild(defs)
|
|
445
447
|
|
|
446
448
|
const ARROW_H = 6
|
|
449
|
+
const edgeEls = []
|
|
450
|
+
const downMap = {}, upMap = {}
|
|
447
451
|
for (const [from, to] of edges) {
|
|
452
|
+
;(downMap[from] || (downMap[from] = [])).push(to)
|
|
453
|
+
;(upMap[to] || (upMap[to] = [])).push(from)
|
|
448
454
|
const p1 = positions[from], p2 = positions[to]
|
|
449
455
|
if (!p1 || !p2) continue
|
|
450
456
|
const x1 = p1.x + NODE_W / 2, y1 = p1.y + NODE_H
|
|
@@ -454,17 +460,55 @@
|
|
|
454
460
|
const fromDone = todoData[from]?.done
|
|
455
461
|
const toBlocked = isBlocked(to)
|
|
456
462
|
const isBlockingEdge = toBlocked && !fromDone
|
|
457
|
-
|
|
463
|
+
const pathEl = svgEl('path', {
|
|
458
464
|
d: `M${x1},${y1} C${x1},${y1 + cp} ${x2},${y2 - cp} ${x2},${y2}`,
|
|
459
465
|
fill: 'none',
|
|
460
466
|
stroke: isBlockingEdge ? blockedEdgeColor : edgeColor,
|
|
461
467
|
'stroke-width': 1.5,
|
|
462
468
|
'stroke-dasharray': isBlockingEdge ? '6,3' : 'none',
|
|
463
469
|
'marker-end': isBlockingEdge ? 'url(#arrow-blocked)' : 'url(#arrow)',
|
|
464
|
-
})
|
|
470
|
+
})
|
|
471
|
+
svg.appendChild(pathEl)
|
|
472
|
+
edgeEls.push({ el: pathEl, from, to })
|
|
465
473
|
}
|
|
466
474
|
inner.appendChild(svg)
|
|
467
475
|
|
|
476
|
+
// Apply flow animation from each focus node to its downstream end
|
|
477
|
+
const flowNodes = new Set()
|
|
478
|
+
for (const nid of nodeIds) {
|
|
479
|
+
if (todoData[nid]?.focus && !todoData[nid]?.done && !isBlocked(nid)) {
|
|
480
|
+
flowNodes.add(nid)
|
|
481
|
+
const q = [nid]
|
|
482
|
+
while (q.length) {
|
|
483
|
+
const cur = q.shift()
|
|
484
|
+
for (const next of (downMap[cur] || [])) {
|
|
485
|
+
if (!flowNodes.has(next)) { flowNodes.add(next); q.push(next) }
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
edgeEls.forEach(e => {
|
|
491
|
+
if (flowNodes.has(e.from) && flowNodes.has(e.to)) {
|
|
492
|
+
e.el.style.strokeDasharray = '12 6'
|
|
493
|
+
e.el.style.animation = 'edge-flow 0.6s linear infinite'
|
|
494
|
+
}
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
function collectChain(id) {
|
|
498
|
+
const visited = new Set([id])
|
|
499
|
+
const queue = [id]
|
|
500
|
+
while (queue.length) {
|
|
501
|
+
const cur = queue.shift()
|
|
502
|
+
for (const next of (downMap[cur] || [])) { if (!visited.has(next)) { visited.add(next); queue.push(next) } }
|
|
503
|
+
}
|
|
504
|
+
const queue2 = [id]
|
|
505
|
+
while (queue2.length) {
|
|
506
|
+
const cur = queue2.shift()
|
|
507
|
+
for (const prev of (upMap[cur] || [])) { if (!visited.has(prev)) { visited.add(prev); queue2.push(prev) } }
|
|
508
|
+
}
|
|
509
|
+
return visited
|
|
510
|
+
}
|
|
511
|
+
|
|
468
512
|
for (const id of nodeIds) {
|
|
469
513
|
const pos = positions[id]; if (!pos) continue
|
|
470
514
|
const todo = todoData[id]
|
|
@@ -476,6 +520,24 @@
|
|
|
476
520
|
else if (blocked) el.classList.add('blocked')
|
|
477
521
|
} else el.classList.add('not-found')
|
|
478
522
|
el.style.cssText = `left:${pos.x}px;top:${pos.y}px;width:${NODE_W}px;`
|
|
523
|
+
el.dataset.id = id
|
|
524
|
+
el.addEventListener('mouseenter', () => {
|
|
525
|
+
const chain = collectChain(id)
|
|
526
|
+
edgeEls.forEach(e => {
|
|
527
|
+
if (chain.has(e.from) && chain.has(e.to)) {
|
|
528
|
+
e.el.style.strokeWidth = '2.5'
|
|
529
|
+
e.el.style.opacity = '1'
|
|
530
|
+
} else {
|
|
531
|
+
e.el.style.opacity = '0.15'
|
|
532
|
+
}
|
|
533
|
+
})
|
|
534
|
+
})
|
|
535
|
+
el.addEventListener('mouseleave', () => {
|
|
536
|
+
edgeEls.forEach(e => {
|
|
537
|
+
e.el.style.strokeWidth = ''
|
|
538
|
+
e.el.style.opacity = ''
|
|
539
|
+
})
|
|
540
|
+
})
|
|
479
541
|
|
|
480
542
|
if (todo) {
|
|
481
543
|
const check = document.createElement('div')
|