From 3116c3dfe418a8bc276703307484a3e04208e68c Mon Sep 17 00:00:00 2001 From: "Vincent D. Warmerdam" Date: Mon, 10 Aug 2026 10:38:17 +0200 Subject: [PATCH] Fix EdgeDraw to react to Python-side name changes (0.5.24) EdgeDraw's frontend read `names` only once at init and registered change handlers for `links`/`directed` but not `names`, so adding or removing nodes from Python never updated the drawing. Add a `change:names` handler that rebuilds nodes (reusing positions for survivors), prunes links pointing at removed nodes (syncing them back), and restarts the force simulation. Also demos the runtime add/remove-node pattern and bumps the release to 0.5.24. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 6 ++++ demos/edgedraw.py | 35 ++++++++++++++++++++-- js/edgedraw.js | 55 ++++++++++++++++++++++++++++++---- pyproject.toml | 2 +- uv.lock | 2 +- wigglystuff/static/edgedraw.js | 2 +- 6 files changed, 91 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a7aa156..16ac9e71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## [0.5.24] - 2026-08-10 + +### Fixed + +- `EdgeDraw` now redraws when `names` changes from Python, so nodes can be added or removed at runtime (e.g. `widget.names = widget.names + ["e"]`). Previously the frontend read `names` only once at init and never reacted, so Python-side node changes never appeared. Surviving nodes keep their position, links that pointed at a removed node are pruned (and synced back), and the force simulation restarts. Note that traitlets only fires on reassignment — an in-place `widget.names.append(...)` still won't sync; build a new list instead. + ## [0.5.23] - 2026-07-28 ### Added diff --git a/demos/edgedraw.py b/demos/edgedraw.py index 1c091854..26ea6297 100644 --- a/demos/edgedraw.py +++ b/demos/edgedraw.py @@ -2,18 +2,20 @@ # requires-python = ">=3.10" # dependencies = [ # "marimo", -# "wigglystuff==0.3.1", +# "wigglystuff==0.5.24", # ] # /// + import marimo -__generated_with = "0.18.4" +__generated_with = "0.23.16" app = marimo.App() @app.cell def _(): import marimo as mo + return (mo,) @@ -84,8 +86,35 @@ def _(widget): return +@app.cell(hide_code=True) +def _(mo): + mo.md(r""" + ## Editing nodes from Python + + You can add or remove nodes at runtime by updating `widget.names`. + Traitlets only notices a change when you **reassign** the list, so build a + new list (`widget.names = widget.names + ["e"]`) rather than mutating it in + place with `.append()` — an in-place mutation never syncs to the drawing. + """) + return + + @app.cell -def _(): +def _(mo): + name_input = mo.ui.text(placeholder="node name") + add_button = mo.ui.run_button(label="Add node", kind="success") + remove_button = mo.ui.run_button(label="Remove node", kind="danger") + mo.hstack([name_input, add_button, remove_button], justify="start") + return add_button, name_input, remove_button + + +@app.cell +def _(add_button, name_input, remove_button, widget): + name = name_input.value.strip() + if add_button.value and name and name not in widget.names: + widget.names = widget.names + [name] + if remove_button.value and name in widget.names: + widget.names = [n for n in widget.names if n != name] return diff --git a/js/edgedraw.js b/js/edgedraw.js index 71afde18..a07d13a5 100644 --- a/js/edgedraw.js +++ b/js/edgedraw.js @@ -11,9 +11,9 @@ function render({model, el}){ // Sample nodes const names = model.get("names"); - const nodes = names.map((name) => ({ id: name, x: 100, y: 100 })); - const nodeOrder = new Map(names.map((name, index) => [name, index])); - const nodeById = new Map(nodes.map((node) => [node.id, node])); + let nodes = names.map((name) => ({ id: name, x: 100, y: 100 })); + let nodeOrder = new Map(names.map((name, index) => [name, index])); + let nodeById = new Map(nodes.map((node) => [node.id, node])); let links = []; let selectedNode = null; @@ -66,7 +66,7 @@ function render({model, el}){ const nodeGroup = svg.append("g"); // Draw nodes - const node = nodeGroup.selectAll(".node") + let node = nodeGroup.selectAll(".node") .data(nodes) .join("circle") .attr("class", "node") @@ -74,7 +74,7 @@ function render({model, el}){ .on("click", handleNodeClick); // Add labels - const labels = nodeGroup.selectAll(".label") + let labels = nodeGroup.selectAll(".label") .data(nodes) .join("text") .attr("class", "label") @@ -196,6 +196,51 @@ function render({model, el}){ simulation.force("link").links(links); updateLinks(); }); + + model.on("change:names", () => { + const updatedNames = model.get("names"); + + // Reuse existing node objects (positions/velocities) for surviving + // names; new names spawn in the middle and let the simulation settle. + const previousById = nodeById; + nodes = updatedNames.map((name) => + previousById.get(name) || { id: name, x: width / 2, y: height / 2 } + ); + nodeOrder = new Map(updatedNames.map((name, index) => [name, index])); + nodeById = new Map(nodes.map((node) => [node.id, node])); + + if (selectedNode && !nodeById.has(selectedNode.id)) { + selectedNode = null; + } + + // Drop any links that referenced removed nodes. + links = hydrateLinks(model.get("links")); + + node = nodeGroup.selectAll(".node") + .data(nodes, (d) => d.id) + .join("circle") + .attr("class", "node") + .attr("r", 10) + .on("click", handleNodeClick); + node.call(nodeDrag); + + labels = nodeGroup.selectAll(".label") + .data(nodes, (d) => d.id) + .join("text") + .attr("class", "label") + .attr("dx", 15) + .attr("dy", 4) + .text(d => d.id); + + simulation.nodes(nodes); + simulation.force("link").links(links); + simulation.alpha(1).restart(); + updateLinks(); + + // Keep Python's `links` consistent when removing a node prunes edges. + model.set("links", links.map(l => ({source: l.source.id, target: l.target.id}))); + model.save_changes(); + }); }; export default { render }; diff --git a/pyproject.toml b/pyproject.toml index 0075b2dc..5fdb3055 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "wigglystuff" -version = "0.5.23" +version = "0.5.24" description = "Collection of Anywidget Widgets" readme = "README.md" requires-python = ">=3.11" diff --git a/uv.lock b/uv.lock index ba4a34f6..a2937b2e 100644 --- a/uv.lock +++ b/uv.lock @@ -2979,7 +2979,7 @@ wheels = [ [[package]] name = "wigglystuff" -version = "0.5.23" +version = "0.5.24" source = { editable = "." } dependencies = [ { name = "anywidget" }, diff --git a/wigglystuff/static/edgedraw.js b/wigglystuff/static/edgedraw.js index c3f62b78..2ec2e6b6 100644 --- a/wigglystuff/static/edgedraw.js +++ b/wigglystuff/static/edgedraw.js @@ -1 +1 @@ -var W="http://www.w3.org/1999/xhtml",ht={svg:"http://www.w3.org/2000/svg",xhtml:W,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function J(t){var e=t+="",r=e.indexOf(":");return r>=0&&(e=t.slice(0,r))!=="xmlns"&&(t=t.slice(r+1)),ht.hasOwnProperty(e)?{space:ht[e],local:t}:t}function We(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===W&&e.documentElement.namespaceURI===W?e.createElement(t):e.createElementNS(r,t)}}function Je(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Z(t){var e=J(t);return(e.local?Je:We)(e)}function Ze(){}function j(t){return t==null?Ze:function(){return this.querySelector(t)}}function kt(t){typeof t!="function"&&(t=j(t));for(var e=this._groups,r=e.length,i=new Array(r),n=0;n=N&&(N=x+1);!(A=c[N])&&++N<_;);s._next=A||null}}return u=new C(u,i),u._enter=a,u._exit=l,u}function lr(t){return typeof t=="object"&&"length"in t?t:Array.from(t)}function Bt(){return new C(this._exit||this._groups.map(et),this._parents)}function Vt(t,e,r){var i=this.enter(),n=this,o=this.exit();return typeof t=="function"?(i=t(i),i&&(i=i.selection())):i=i.append(t+""),e!=null&&(n=e(n),n&&(n=n.selection())),r==null?o.remove():r(o),i&&n?i.merge(n).order():n}function Ot(t){for(var e=t.selection?t.selection():t,r=this._groups,i=e._groups,n=r.length,o=i.length,u=Math.min(n,o),a=new Array(n),l=0;l=0;)(u=i[n])&&(o&&u.compareDocumentPosition(o)^4&&o.parentNode.insertBefore(u,o),o=u);return this}function Yt(t){t||(t=cr);function e(v,d){return v&&d?t(v.__data__,d.__data__):!v-!d}for(var r=this._groups,i=r.length,n=new Array(i),o=0;oe?1:t>=e?0:NaN}function Ut(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function qt(){return Array.from(this)}function Ht(){for(var t=this._groups,e=0,r=t.length;e1?this.each((e==null?yr:typeof e=="function"?vr:_r)(t,e,r??"")):wr(this.node(),t)}function wr(t,e){return t.style.getPropertyValue(e)||rt(t).getComputedStyle(t,null).getPropertyValue(e)}function Ar(t){return function(){delete this[t]}}function Nr(t,e){return function(){this[t]=e}}function br(t,e){return function(){var r=e.apply(this,arguments);r==null?delete this[t]:this[t]=r}}function Jt(t,e){return arguments.length>1?this.each((e==null?Ar:typeof e=="function"?br:Nr)(t,e)):this.node()[t]}function Zt(t){return t.trim().split(/^|\s+/)}function mt(t){return t.classList||new jt(t)}function jt(t){this._node=t,this._names=Zt(t.getAttribute("class")||"")}jt.prototype={add:function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function te(t,e){for(var r=mt(t),i=-1,n=e.length;++i=0&&(r=e.slice(i+1),e=e.slice(0,i)),{type:e,name:r}})}function Yr(t){return function(){var e=this.__on;if(e){for(var r=0,i=-1,n=e.length,o;r{}};function ye(){for(var t=0,e=arguments.length,r={},i;t=0&&(i=r.slice(n+1),r=r.slice(0,n)),r&&!e.hasOwnProperty(r))throw new Error("unknown type: "+r);return{type:r,name:i}})}it.prototype=ye.prototype={constructor:it,on:function(t,e){var r=this._,i=Kr(t+"",r),n,o=-1,u=i.length;if(arguments.length<2){for(;++o0)for(var r=new Array(n),i=0,n,o;i()=>t;function q(t,{sourceEvent:e,subject:r,target:i,identifier:n,active:o,x:u,y:a,dx:l,dy:f,dispatch:p}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},subject:{value:r,enumerable:!0,configurable:!0},target:{value:i,enumerable:!0,configurable:!0},identifier:{value:n,enumerable:!0,configurable:!0},active:{value:o,enumerable:!0,configurable:!0},x:{value:u,enumerable:!0,configurable:!0},y:{value:a,enumerable:!0,configurable:!0},dx:{value:l,enumerable:!0,configurable:!0},dy:{value:f,enumerable:!0,configurable:!0},_:{value:p}})}q.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};function Jr(t){return!t.ctrlKey&&!t.button}function Zr(){return this.parentNode}function jr(t,e){return e??{x:t.x,y:t.y}}function tn(){return navigator.maxTouchPoints||"ontouchstart"in this}function gt(){var t=Jr,e=Zr,r=jr,i=tn,n={},o=Y("start","drag","end"),u=0,a,l,f,p,v=0;function d(s){s.on("mousedown.drag",h).filter(i).on("touchstart.drag",c).on("touchmove.drag",g,_e).on("touchend.drag touchcancel.drag",x).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function h(s,A){if(!(p||!t.call(this,s,A))){var M=N(this,e.call(this,s,A),s,A,"mouse");M&&(P(s.view).on("mousemove.drag",_,R).on("mouseup.drag",m,R),ve(s.view),ot(s),f=!1,a=s.clientX,l=s.clientY,M("start",s))}}function _(s){if(z(s),!f){var A=s.clientX-a,M=s.clientY-l;f=A*A+M*M>v}n.mouse("drag",s)}function m(s){P(s.view).on("mousemove.drag mouseup.drag",null),we(s.view,f),z(s),n.mouse("end",s)}function c(s,A){if(t.call(this,s,A)){var M=s.changedTouches,S=e.call(this,s,A),y=M.length,w,b;for(w=0;w=(v=(a+f)/2))?a=v:f=v,(c=r>=(d=(l+p)/2))?l=d:p=d,n=o,!(o=o[g=c<<1|m]))return n[g]=u,t;if(h=+t._x.call(null,o.data),_=+t._y.call(null,o.data),e===h&&r===_)return u.next=o,n?n[g]=u:t._root=u,t;do n=n?n[g]=new Array(4):t._root=new Array(4),(m=e>=(v=(a+f)/2))?a=v:f=v,(c=r>=(d=(l+p)/2))?l=d:p=d;while((g=c<<1|m)===(x=(_>=d)<<1|h>=v));return n[x]=o,n[g]=u,t}function be(t){var e,r,i=t.length,n,o,u=new Array(i),a=new Array(i),l=1/0,f=1/0,p=-1/0,v=-1/0;for(r=0;rp&&(p=n),ov&&(v=o));if(l>p||f>v)return this;for(this.cover(l,f).cover(p,v),r=0;rt||t>=n||i>e||e>=o;)switch(f=(ep||(a=_.y0)>v||(l=_.x1)=g)<<1|t>=c)&&(_=d[d.length-1],d[d.length-1]=d[d.length-1-m],d[d.length-1-m]=_)}else{var x=t-+this._x.call(null,h.data),N=e-+this._y.call(null,h.data),s=x*x+N*N;if(s=(d=(u+l)/2))?u=d:l=d,(m=v>=(h=(a+f)/2))?a=h:f=h,e=r,!(r=r[c=m<<1|_]))return this;if(!r.length)break;(e[c+1&3]||e[c+2&3]||e[c+3&3])&&(i=e,g=c)}for(;r.data!==t;)if(n=r,!(r=r.next))return this;return(o=r.next)&&delete r.next,n?(o?n.next=o:delete n.next,this):e?(o?e[c]=o:delete e[c],(r=e[0]||e[1]||e[2]||e[3])&&r===(e[3]||e[2]||e[1]||e[0])&&!r.length&&(i?i[g]=r:this._root=r),this):(this._root=o,this)}function Te(t){for(var e=0,r=t.length;ed.index){var E=h-y.x-y.vx,D=_-y.y-y.vy,k=E*E+D*D;kh+b||M_+b||S<_-b}}function a(f){if(f.data)return f.r=r[f.data.index];for(var p=f.r=0;p<4;++p)f[p]&&f[p].r>f.r&&(f.r=f[p].r)}function l(){if(e){var f,p=e.length,v;for(r=new Array(p),f=0;f[e(A,M,u),A])),s;for(c=0,a=new Array(g);c=0&&t._call.call(void 0,e),t=t._next;--O}function Xe(){V=(ut=G.now())+ft,O=Q=0;try{qe()}finally{O=0,un(),V=0}}function an(){var t=G.now(),e=t-ut;e>Ye&&(ft-=e,ut=t)}function un(){for(var t,e=at,r,i=1/0;e;)e._call?(i>e._time&&(i=e._time),t=e,e=e._next):(r=e._next,e._next=null,e=t?t._next=r:at=r);$=t,At(i)}function At(t){if(!O){Q&&(Q=clearTimeout(Q));var e=t-V;e>24?(t<1/0&&(Q=setTimeout(Xe,t-G.now()-ft)),H&&(H=clearInterval(H))):(H||(ut=G.now(),H=setInterval(an,Ye)),O=1,Ue(Xe))}}function He(){let t=1;return()=>(t=(1664525*t+1013904223)%4294967296)/4294967296}function Qe(t){return t.x}function $e(t){return t.y}var fn=10,sn=Math.PI*(3-Math.sqrt(5));function bt(t){var e,r=1,i=.001,n=1-Math.pow(i,1/300),o=0,u=.6,a=new Map,l=st(v),f=Y("tick","end"),p=He();t==null&&(t=[]);function v(){d(),f.call("tick",e),r1?(c==null?a.delete(m):a.set(m,_(c)),e):a.get(m)},find:function(m,c,g){var x=0,N=t.length,s,A,M,S,y;for(g==null?g=1/0:g*=g,x=0;x1?(f.on(m,c),e):f.on(m)}}}function Mt(){var t,e,r,i,n=F(-30),o,u=1,a=1/0,l=.81;function f(h){var _,m=t.length,c=B(t,Qe,$e).visitAfter(v);for(i=h,_=0;_=a)return;(h.data!==e||h.next)&&(g===0&&(g=I(r),s+=g*g),x===0&&(x=I(r),s+=x*x),s({id:y,x:100,y:100})),o=new Map(i.map((y,w)=>[y,w])),u=new Map(n.map(y=>[y.id,y])),a=[],l=null,f=t.get("directed");function p(y){return(y||[]).map(w=>!w||typeof w!="object"?null:{source:u.get(w.source),target:u.get(w.target)}).filter(w=>w&&w.source&&w.target)}let v=600,d=400,h=P(r).append("svg").attr("width",v).attr("height",d);h.append("defs").append("marker").attr("id","arrowhead").attr("viewBox","-0 -5 10 10").attr("refX",13).attr("refY",0).attr("orient","auto").attr("markerWidth",6).attr("markerHeight",6).append("path").attr("d","M0,-5L10,0L0,5").attr("class","arrow"),a=p(t.get("links"));let _=bt(n).force("link",vt(a).id(y=>y.id).distance(100)).force("charge",Mt().strength(-50)).force("center",xt(v/2,d/2)).force("collide",_t().radius(30)).on("tick",M),m=h.append("g"),c=h.append("g"),g=c.selectAll(".node").data(n).join("circle").attr("class","node").attr("r",10).on("click",s),x=c.selectAll(".label").data(n).join("text").attr("class","label").attr("dx",15).attr("dy",4).text(y=>y.id);function N(y,w){return o.get(y.id)<=o.get(w.id)?{source:y,target:w}:{source:w,target:y}}function s(y,w){if(!l)l=w,g.classed("selected",b=>b===l);else if(l===w)l=null,g.classed("selected",!1);else{let b=a.find(k=>k.source===l&&k.target===w||k.source.id===l.id&&k.target.id===w.id),E=a.find(k=>k.source===w&&k.target===l||k.source.id===w.id&&k.target.id===l.id),D=b||E;f?(E&&(a=a.filter(k=>k!==E)),b||a.push({source:l,target:w})):D?a=a.filter(k=>k!==D):a.push(N(l,w)),_.force("link").links(a),l=null,g.classed("selected",!1),A()}t.set("links",a.map(b=>({source:b.source.id,target:b.target.id}))),console.log(a.map(b=>({source:b.source.id,target:b.target.id}))),t.save_changes()}function A(){let y=m.selectAll(".link").data(a).join("path").attr("class","link").attr("marker-end",f?"url(#arrowhead)":null).attr("d",w=>{let b=w.target.x-w.source.x,E=w.target.y-w.source.y;return`M${w.source.x},${w.source.y}L${w.target.x},${w.target.y}`}).on("click",function(w,b){w.stopPropagation(),a=a.filter(E=>E!==b),_.force("link").links(a),A(),t.set("links",a.map(E=>({source:E.source.id,target:E.target.id}))),console.log(a.map(E=>({source:E.source.id,target:E.target.id}))),t.save_changes()})}function M(){g.attr("cx",y=>y.x=Math.max(15,Math.min(v-15,y.x))).attr("cy",y=>y.y=Math.max(15,Math.min(d-15,y.y))),x.attr("x",y=>y.x).attr("y",y=>y.y),A()}let S=gt().on("drag",function(y,w){w.x=y.x,w.y=y.y,_.alpha(.1).restart()});g.call(S),t.on("change:directed",()=>{f=t.get("directed"),A()}),t.on("change:links",()=>{a=p(t.get("links")),_.force("link").links(a),A()})}var Ha={render:ln};export{Ha as default}; +var W="http://www.w3.org/1999/xhtml",ht={svg:"http://www.w3.org/2000/svg",xhtml:W,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function J(t){var e=t+="",r=e.indexOf(":");return r>=0&&(e=t.slice(0,r))!=="xmlns"&&(t=t.slice(r+1)),ht.hasOwnProperty(e)?{space:ht[e],local:t}:t}function We(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===W&&e.documentElement.namespaceURI===W?e.createElement(t):e.createElementNS(r,t)}}function Je(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Z(t){var e=J(t);return(e.local?Je:We)(e)}function Ze(){}function j(t){return t==null?Ze:function(){return this.querySelector(t)}}function Et(t){typeof t!="function"&&(t=j(t));for(var e=this._groups,r=e.length,i=new Array(r),n=0;n=b&&(b=y+1);!(N=c[b])&&++b<_;);s._next=N||null}}return u=new C(u,i),u._enter=a,u._exit=l,u}function lr(t){return typeof t=="object"&&"length"in t?t:Array.from(t)}function Rt(){return new C(this._exit||this._groups.map(et),this._parents)}function Vt(t,e,r){var i=this.enter(),n=this,o=this.exit();return typeof t=="function"?(i=t(i),i&&(i=i.selection())):i=i.append(t+""),e!=null&&(n=e(n),n&&(n=n.selection())),r==null?o.remove():r(o),i&&n?i.merge(n).order():n}function Ot(t){for(var e=t.selection?t.selection():t,r=this._groups,i=e._groups,n=r.length,o=i.length,u=Math.min(n,o),a=new Array(n),l=0;l=0;)(u=i[n])&&(o&&u.compareDocumentPosition(o)^4&&o.parentNode.insertBefore(u,o),o=u);return this}function Yt(t){t||(t=cr);function e(v,g){return v&&g?t(v.__data__,g.__data__):!v-!g}for(var r=this._groups,i=r.length,n=new Array(i),o=0;oe?1:t>=e?0:NaN}function Ut(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function qt(){return Array.from(this)}function Ht(){for(var t=this._groups,e=0,r=t.length;e1?this.each((e==null?yr:typeof e=="function"?vr:_r)(t,e,r??"")):wr(this.node(),t)}function wr(t,e){return t.style.getPropertyValue(e)||rt(t).getComputedStyle(t,null).getPropertyValue(e)}function Ar(t){return function(){delete this[t]}}function Nr(t,e){return function(){this[t]=e}}function br(t,e){return function(){var r=e.apply(this,arguments);r==null?delete this[t]:this[t]=r}}function Jt(t,e){return arguments.length>1?this.each((e==null?Ar:typeof e=="function"?br:Nr)(t,e)):this.node()[t]}function Zt(t){return t.trim().split(/^|\s+/)}function mt(t){return t.classList||new jt(t)}function jt(t){this._node=t,this._names=Zt(t.getAttribute("class")||"")}jt.prototype={add:function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function te(t,e){for(var r=mt(t),i=-1,n=e.length;++i=0&&(r=e.slice(i+1),e=e.slice(0,i)),{type:e,name:r}})}function Yr(t){return function(){var e=this.__on;if(e){for(var r=0,i=-1,n=e.length,o;r{}};function ye(){for(var t=0,e=arguments.length,r={},i;t=0&&(i=r.slice(n+1),r=r.slice(0,n)),r&&!e.hasOwnProperty(r))throw new Error("unknown type: "+r);return{type:r,name:i}})}it.prototype=ye.prototype={constructor:it,on:function(t,e){var r=this._,i=Kr(t+"",r),n,o=-1,u=i.length;if(arguments.length<2){for(;++o0)for(var r=new Array(n),i=0,n,o;i()=>t;function q(t,{sourceEvent:e,subject:r,target:i,identifier:n,active:o,x:u,y:a,dx:l,dy:f,dispatch:p}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},subject:{value:r,enumerable:!0,configurable:!0},target:{value:i,enumerable:!0,configurable:!0},identifier:{value:n,enumerable:!0,configurable:!0},active:{value:o,enumerable:!0,configurable:!0},x:{value:u,enumerable:!0,configurable:!0},y:{value:a,enumerable:!0,configurable:!0},dx:{value:l,enumerable:!0,configurable:!0},dy:{value:f,enumerable:!0,configurable:!0},_:{value:p}})}q.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};function Jr(t){return!t.ctrlKey&&!t.button}function Zr(){return this.parentNode}function jr(t,e){return e??{x:t.x,y:t.y}}function tn(){return navigator.maxTouchPoints||"ontouchstart"in this}function gt(){var t=Jr,e=Zr,r=jr,i=tn,n={},o=Y("start","drag","end"),u=0,a,l,f,p,v=0;function g(s){s.on("mousedown.drag",h).filter(i).on("touchstart.drag",c).on("touchmove.drag",d,_e).on("touchend.drag touchcancel.drag",y).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function h(s,N){if(!(p||!t.call(this,s,N))){var M=b(this,e.call(this,s,N),s,N,"mouse");M&&(P(s.view).on("mousemove.drag",_,B).on("mouseup.drag",m,B),ve(s.view),ot(s),f=!1,a=s.clientX,l=s.clientY,M("start",s))}}function _(s){if(z(s),!f){var N=s.clientX-a,M=s.clientY-l;f=N*N+M*M>v}n.mouse("drag",s)}function m(s){P(s.view).on("mousemove.drag mouseup.drag",null),we(s.view,f),z(s),n.mouse("end",s)}function c(s,N){if(t.call(this,s,N)){var M=s.changedTouches,k=e.call(this,s,N),x=M.length,w,A;for(w=0;w=(v=(a+f)/2))?a=v:f=v,(c=r>=(g=(l+p)/2))?l=g:p=g,n=o,!(o=o[d=c<<1|m]))return n[d]=u,t;if(h=+t._x.call(null,o.data),_=+t._y.call(null,o.data),e===h&&r===_)return u.next=o,n?n[d]=u:t._root=u,t;do n=n?n[d]=new Array(4):t._root=new Array(4),(m=e>=(v=(a+f)/2))?a=v:f=v,(c=r>=(g=(l+p)/2))?l=g:p=g;while((d=c<<1|m)===(y=(_>=g)<<1|h>=v));return n[y]=o,n[d]=u,t}function be(t){var e,r,i=t.length,n,o,u=new Array(i),a=new Array(i),l=1/0,f=1/0,p=-1/0,v=-1/0;for(r=0;rp&&(p=n),ov&&(v=o));if(l>p||f>v)return this;for(this.cover(l,f).cover(p,v),r=0;rt||t>=n||i>e||e>=o;)switch(f=(ep||(a=_.y0)>v||(l=_.x1)=d)<<1|t>=c)&&(_=g[g.length-1],g[g.length-1]=g[g.length-1-m],g[g.length-1-m]=_)}else{var y=t-+this._x.call(null,h.data),b=e-+this._y.call(null,h.data),s=y*y+b*b;if(s=(g=(u+l)/2))?u=g:l=g,(m=v>=(h=(a+f)/2))?a=h:f=h,e=r,!(r=r[c=m<<1|_]))return this;if(!r.length)break;(e[c+1&3]||e[c+2&3]||e[c+3&3])&&(i=e,d=c)}for(;r.data!==t;)if(n=r,!(r=r.next))return this;return(o=r.next)&&delete r.next,n?(o?n.next=o:delete n.next,this):e?(o?e[c]=o:delete e[c],(r=e[0]||e[1]||e[2]||e[3])&&r===(e[3]||e[2]||e[1]||e[0])&&!r.length&&(i?i[d]=r:this._root=r),this):(this._root=o,this)}function Te(t){for(var e=0,r=t.length;eg.index){var S=h-x.x-x.vx,D=_-x.y-x.vy,E=S*S+D*D;Eh+A||M_+A||k<_-A}}function a(f){if(f.data)return f.r=r[f.data.index];for(var p=f.r=0;p<4;++p)f[p]&&f[p].r>f.r&&(f.r=f[p].r)}function l(){if(e){var f,p=e.length,v;for(r=new Array(p),f=0;f[e(N,M,u),N])),s;for(c=0,a=new Array(d);c=0&&t._call.call(void 0,e),t=t._next;--O}function Xe(){V=(ut=G.now())+ft,O=Q=0;try{qe()}finally{O=0,un(),V=0}}function an(){var t=G.now(),e=t-ut;e>Ye&&(ft-=e,ut=t)}function un(){for(var t,e=at,r,i=1/0;e;)e._call?(i>e._time&&(i=e._time),t=e,e=e._next):(r=e._next,e._next=null,e=t?t._next=r:at=r);$=t,At(i)}function At(t){if(!O){Q&&(Q=clearTimeout(Q));var e=t-V;e>24?(t<1/0&&(Q=setTimeout(Xe,t-G.now()-ft)),H&&(H=clearInterval(H))):(H||(ut=G.now(),H=setInterval(an,Ye)),O=1,Ue(Xe))}}function He(){let t=1;return()=>(t=(1664525*t+1013904223)%4294967296)/4294967296}function Qe(t){return t.x}function $e(t){return t.y}var fn=10,sn=Math.PI*(3-Math.sqrt(5));function bt(t){var e,r=1,i=.001,n=1-Math.pow(i,1/300),o=0,u=.6,a=new Map,l=st(v),f=Y("tick","end"),p=He();t==null&&(t=[]);function v(){g(),f.call("tick",e),r1?(c==null?a.delete(m):a.set(m,_(c)),e):a.get(m)},find:function(m,c,d){var y=0,b=t.length,s,N,M,k,x;for(d==null?d=1/0:d*=d,y=0;y1?(f.on(m,c),e):f.on(m)}}}function Mt(){var t,e,r,i,n=F(-30),o,u=1,a=1/0,l=.81;function f(h){var _,m=t.length,c=R(t,Qe,$e).visitAfter(v);for(i=h,_=0;_=a)return;(h.data!==e||h.next)&&(d===0&&(d=I(r),s+=d*d),y===0&&(y=I(r),s+=y*y),s({id:x,x:100,y:100})),o=new Map(i.map((x,w)=>[x,w])),u=new Map(n.map(x=>[x.id,x])),a=[],l=null,f=t.get("directed");function p(x){return(x||[]).map(w=>!w||typeof w!="object"?null:{source:u.get(w.source),target:u.get(w.target)}).filter(w=>w&&w.source&&w.target)}let v=600,g=400,h=P(r).append("svg").attr("width",v).attr("height",g);h.append("defs").append("marker").attr("id","arrowhead").attr("viewBox","-0 -5 10 10").attr("refX",13).attr("refY",0).attr("orient","auto").attr("markerWidth",6).attr("markerHeight",6).append("path").attr("d","M0,-5L10,0L0,5").attr("class","arrow"),a=p(t.get("links"));let _=bt(n).force("link",vt(a).id(x=>x.id).distance(100)).force("charge",Mt().strength(-50)).force("center",xt(v/2,g/2)).force("collide",_t().radius(30)).on("tick",M),m=h.append("g"),c=h.append("g"),d=c.selectAll(".node").data(n).join("circle").attr("class","node").attr("r",10).on("click",s),y=c.selectAll(".label").data(n).join("text").attr("class","label").attr("dx",15).attr("dy",4).text(x=>x.id);function b(x,w){return o.get(x.id)<=o.get(w.id)?{source:x,target:w}:{source:w,target:x}}function s(x,w){if(!l)l=w,d.classed("selected",A=>A===l);else if(l===w)l=null,d.classed("selected",!1);else{let A=a.find(E=>E.source===l&&E.target===w||E.source.id===l.id&&E.target.id===w.id),S=a.find(E=>E.source===w&&E.target===l||E.source.id===w.id&&E.target.id===l.id),D=A||S;f?(S&&(a=a.filter(E=>E!==S)),A||a.push({source:l,target:w})):D?a=a.filter(E=>E!==D):a.push(b(l,w)),_.force("link").links(a),l=null,d.classed("selected",!1),N()}t.set("links",a.map(A=>({source:A.source.id,target:A.target.id}))),console.log(a.map(A=>({source:A.source.id,target:A.target.id}))),t.save_changes()}function N(){let x=m.selectAll(".link").data(a).join("path").attr("class","link").attr("marker-end",f?"url(#arrowhead)":null).attr("d",w=>{let A=w.target.x-w.source.x,S=w.target.y-w.source.y;return`M${w.source.x},${w.source.y}L${w.target.x},${w.target.y}`}).on("click",function(w,A){w.stopPropagation(),a=a.filter(S=>S!==A),_.force("link").links(a),N(),t.set("links",a.map(S=>({source:S.source.id,target:S.target.id}))),console.log(a.map(S=>({source:S.source.id,target:S.target.id}))),t.save_changes()})}function M(){d.attr("cx",x=>x.x=Math.max(15,Math.min(v-15,x.x))).attr("cy",x=>x.y=Math.max(15,Math.min(g-15,x.y))),y.attr("x",x=>x.x).attr("y",x=>x.y),N()}let k=gt().on("drag",function(x,w){w.x=x.x,w.y=x.y,_.alpha(.1).restart()});d.call(k),t.on("change:directed",()=>{f=t.get("directed"),N()}),t.on("change:links",()=>{a=p(t.get("links")),_.force("link").links(a),N()}),t.on("change:names",()=>{let x=t.get("names"),w=u;n=x.map(A=>w.get(A)||{id:A,x:v/2,y:g/2}),o=new Map(x.map((A,S)=>[A,S])),u=new Map(n.map(A=>[A.id,A])),l&&!u.has(l.id)&&(l=null),a=p(t.get("links")),d=c.selectAll(".node").data(n,A=>A.id).join("circle").attr("class","node").attr("r",10).on("click",s),d.call(k),y=c.selectAll(".label").data(n,A=>A.id).join("text").attr("class","label").attr("dx",15).attr("dy",4).text(A=>A.id),_.nodes(n),_.force("link").links(a),_.alpha(1).restart(),N(),t.set("links",a.map(A=>({source:A.source.id,target:A.target.id}))),t.save_changes()})}var Ha={render:ln};export{Ha as default};