@yanqirenshi/d3.deployment 0.8.4 → 0.9.0

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/dist/js/Rectum.js CHANGED
@@ -64,9 +64,13 @@ var Rectum = exports["default"] = /*#__PURE__*/function (_Colon) {
64
64
  value: function initCallbacks(params) {
65
65
  var callbacks = params && params.callbacks || {};
66
66
  var node = callbacks.node || {};
67
+ var edge = callbacks.edge || {};
67
68
  return {
68
69
  node: {
69
70
  click: node.click || null
71
+ },
72
+ edge: {
73
+ click: edge.click || null
70
74
  }
71
75
  };
72
76
  }
@@ -411,6 +415,7 @@ var Rectum = exports["default"] = /*#__PURE__*/function (_Colon) {
411
415
  }
412
416
  this.attachNodeDrag(place, data);
413
417
  this.attachNodeClick(place);
418
+ this.attachEdgeClick(place);
414
419
  }
415
420
  /**
416
421
  * ノードのクリック通知(callbacks.node.click)。
@@ -435,6 +440,75 @@ var Rectum = exports["default"] = /*#__PURE__*/function (_Colon) {
435
440
  callback(d._core, d);
436
441
  });
437
442
  }
443
+ /**
444
+ * エッジのクリック通知(callbacks.edge.click)。
445
+ * エッジ path のほか、両端のポート円からも同じエッジを通知する
446
+ * (細い線より円のほうが掴みやすい)。(core, edge) を渡す契約は
447
+ * node.click と同じ(core = 入力のエッジオブジェクトそのまま)。
448
+ */
449
+ }, {
450
+ key: "attachEdgeClick",
451
+ value: function attachEdgeClick(place) {
452
+ var _this5 = this;
453
+ var callback = this._callbacks.edge.click;
454
+ if (!callback) return;
455
+ place.selectAll('path.edge').style('cursor', 'pointer').on('click', function (event) {
456
+ // path の datum は線分データのため data-edge-id からエッジを引く
457
+ var el = event.currentTarget;
458
+ var id = el && el.getAttribute && el.getAttribute('data-edge-id');
459
+ var edge = _this5.findEdge(id);
460
+ if (!edge) return;
461
+ event.stopPropagation();
462
+ callback(edge._core, edge);
463
+ });
464
+ place.selectAll('circle.port').style('cursor', 'pointer').on('click', function (event, d) {
465
+ event.stopPropagation();
466
+ callback(d.edge._core, d.edge);
467
+ });
468
+ }
469
+ /** id(入力 id。DOM の data-edge-id は文字列)からエッジを引く。 */
470
+ }, {
471
+ key: "findEdge",
472
+ value: function findEdge(id) {
473
+ var data = this.data();
474
+ if (!data || id === null || id === undefined) return null;
475
+ return data.edges.ht[id] || data.edges.ht[Number(id)] || null;
476
+ }
477
+ /**
478
+ * エッジのポート位置(角度)を変更して再フィットし、DOM があれば
479
+ * その場で再描画する(remount 不要)。角度は入力と同じ規約
480
+ * (ノード中心から見て 0=真下・時計回り。90=左, 180=真上, 270=右)。
481
+ *
482
+ * @param {*} edge_id 対象エッジの id(入力の id)
483
+ * @param {object} positions { from?: 角度, to?: 角度 } 省略側は変更しない
484
+ */
485
+ }, {
486
+ key: "setEdgePorts",
487
+ value: function setEdgePorts(edge_id) {
488
+ var positions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
489
+ var edge = this.findEdge(edge_id);
490
+ if (!edge) return this;
491
+
492
+ // 入力(_core)の角度を更新する。fittingPort は _core を読むので
493
+ // そのまま再フィットでき、以後の再描画・remount でも維持される。
494
+ if (positions.from !== undefined && positions.from !== null) edge._core.from.position = positions.from;
495
+ if (positions.to !== undefined && positions.to !== null) edge._core.to.position = positions.to;
496
+ this.fittingPort(edge.from.port);
497
+ this.fittingPort(edge.to.port);
498
+ this.fittingEdge(edge);
499
+ var place = this.d3Element();
500
+ if (place) {
501
+ place.selectAll('circle.port').filter(function (d) {
502
+ return d === edge.from.port || d === edge.to.port;
503
+ }).attr('cx', function (d) {
504
+ return d.position.x;
505
+ }).attr('cy', function (d) {
506
+ return d.position.y;
507
+ });
508
+ this._painter.EDGE.redraw(place, edge);
509
+ }
510
+ return this;
511
+ }
438
512
  ///// ////////////////////////////////////////////////////////////////
439
513
  ///// Drag (grab + drag でノードを移動)
440
514
  ///// ////////////////////////////////////////////////////////////////
@@ -63,8 +63,11 @@ var Edge = exports["default"] = /*#__PURE__*/function () {
63
63
  // (これを落とすと _id=null → data-edge-id="null" になり、
64
64
  // ドラッグ時の redraw が対象 path を特定できず接続線が追従しない)
65
65
  if (data.id || data.id === 0) new_data._id = data.id;else if (data._id || data._id === 0) new_data._id = data._id;
66
- if (data.from.id) new_data.from.id = data.from.id;
67
- if (data.to.id) new_data.to.id = data.to.id;
66
+
67
+ // id: 0 も有効な参照として扱う(truthy チェックだと 0 のノードへの
68
+ // 接続が無視され、ポート解決時にクラッシュする — sitemap 側 issue #11 と同族)
69
+ if (data.from.id !== undefined && data.from.id !== null) new_data.from.id = data.from.id;
70
+ if (data.to.id !== undefined && data.to.id !== null) new_data.to.id = data.to.id;
68
71
  if (data.stroke) {
69
72
  if (data.stroke.color) new_data.stroke.color = data.stroke.color;
70
73
  if (data.stroke.width) new_data.stroke.width = data.stroke.width;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yanqirenshi/d3.deployment",
3
- "version": "0.8.4",
3
+ "version": "0.9.0",
4
4
  "description": "See [Documents](https://yanqirenshi.github.io/D3.Deployment/)",
5
5
  "main": "dist/index.js",
6
6
  "directories": {
@@ -147,3 +147,78 @@ test('moving a child leaf shifts only that child, and edges from it follow', ()
147
147
  expect(child._position).toEqual({ ...c0, x: c0.x - 20, y: c0.y + 40 });
148
148
  expect(parent._position).toEqual(p0); // 親は動かない
149
149
  });
150
+
151
+ // sitemap 側 issue #11 と同族: id 0 のノードをエッジの from/to に指定すると
152
+ // Edge.normalize の truthy チェックで参照が落ち、ポート解決でクラッシュしていた。
153
+ test('id 0 is a valid node reference in edges (same class as sitemap issue #11)', () => {
154
+ const rectum = new Rectum({});
155
+ const sample = {
156
+ nodes: [
157
+ {
158
+ type: 'NODE', id: 0,
159
+ label: { text: 'Zero', position: { x: 20, y: 20 } },
160
+ size: { w: 200, h: 100 }, position: { x: 0, y: 0 },
161
+ children: [],
162
+ },
163
+ {
164
+ type: 'NODE', id: 1,
165
+ label: { text: 'One', position: { x: 20, y: 20 } },
166
+ size: { w: 200, h: 100 }, position: { x: 400, y: 0 },
167
+ children: [],
168
+ },
169
+ ],
170
+ edges: [
171
+ { id: 100, from: { id: 0, position: 0 }, to: { id: 1, position: 180 } },
172
+ ],
173
+ };
174
+
175
+ expect(() => rectum.data(sample)).not.toThrow();
176
+
177
+ const data = rectum.data();
178
+ const edge = data.edges.list.find((e) => e._id === 100);
179
+
180
+ expect(edge.from.id).toBe(0);
181
+ expect(edge.from.node).toBe(data.nodes.ht[0]);
182
+ expect(typeof edge.from.position.x).toBe('number');
183
+ expect(typeof edge.from.position.y).toBe('number');
184
+ });
185
+
186
+ // issue #13: エッジのポート位置(角度)を描画後に変更する公開 API。
187
+ // setEdgePorts は _core の角度を更新してポートを再フィットする(DOM 不要でテスト可能)。
188
+ test('setEdgePorts refits ports to the new angles (issue #13)', () => {
189
+ const rectum = new Rectum({});
190
+ rectum.data(NESTED_SAMPLE());
191
+ const data = rectum.data();
192
+
193
+ const edge = data.edges.ht[100];
194
+ const node1 = data.nodes.ht[1];
195
+ const node2 = data.nodes.ht[2];
196
+
197
+ // 初期: from は角度 0(真下) → node 1 の下辺中央(マージン 8)
198
+ expect(edge.from.port.position.x).toBeCloseTo(node1._position.x + node1._size.w / 2, 0);
199
+ expect(edge.from.port.position.y).toBeCloseTo(node1._position.y + node1._size.h + 8, 0);
200
+
201
+ rectum.setEdgePorts(100, { from: 180, to: 90 });
202
+
203
+ // _core が更新され、from ポートは上辺中央へ、to ポートは左辺中央へ再フィット
204
+ expect(edge._core.from.position).toBe(180);
205
+ expect(edge._core.to.position).toBe(90);
206
+
207
+ expect(edge.from.port.position.x).toBeCloseTo(node1._position.x + node1._size.w / 2, 0);
208
+ expect(edge.from.port.position.y).toBeCloseTo(node1._position.y - 8, 0);
209
+
210
+ expect(edge.to.port.position.x).toBeCloseTo(node2._position.x - 8, 0);
211
+ expect(edge.to.port.position.y).toBeCloseTo(node2._position.y + node2._size.h / 2, 0);
212
+
213
+ // エッジ端点もポートに追従
214
+ expect(edge.from.position).toEqual(edge.from.port.position);
215
+ expect(edge.to.position).toEqual(edge.to.port.position);
216
+
217
+ // 片側のみの変更(from は 180 のまま)
218
+ rectum.setEdgePorts(100, { to: 270 });
219
+ expect(edge._core.from.position).toBe(180);
220
+ expect(edge.to.port.position.x).toBeCloseTo(node2._position.x + node2._size.w + 8, 0);
221
+
222
+ // 未知のエッジ id は無視される(例外なし)
223
+ expect(() => rectum.setEdgePorts(999, { from: 0 })).not.toThrow();
224
+ });