node.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. var __rest = (this && this.__rest) || function (s, e) {
  2. var t = {};
  3. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
  4. t[p] = s[p];
  5. if (s != null && typeof Object.getOwnPropertySymbols === "function")
  6. for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
  7. if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
  8. t[p[i]] = s[p[i]];
  9. }
  10. return t;
  11. };
  12. import { Point, Rectangle, Angle } from '@antv/x6-geometry';
  13. import { StringExt, ObjectExt, NumberExt, Interp, } from '@antv/x6-common';
  14. import { Registry } from '../registry/registry';
  15. import { Markup } from '../view/markup';
  16. import { Cell } from './cell';
  17. import { ShareRegistry } from './registry';
  18. import { PortManager } from './port';
  19. export class Node extends Cell {
  20. get [Symbol.toStringTag]() {
  21. return Node.toStringTag;
  22. }
  23. constructor(metadata = {}) {
  24. super(metadata);
  25. this.initPorts();
  26. }
  27. preprocess(metadata, ignoreIdCheck) {
  28. const { x, y, width, height } = metadata, others = __rest(metadata, ["x", "y", "width", "height"]);
  29. if (x != null || y != null) {
  30. const position = others.position;
  31. others.position = Object.assign(Object.assign({}, position), { x: x != null ? x : position ? position.x : 0, y: y != null ? y : position ? position.y : 0 });
  32. }
  33. if (width != null || height != null) {
  34. const size = others.size;
  35. others.size = Object.assign(Object.assign({}, size), { width: width != null ? width : size ? size.width : 0, height: height != null ? height : size ? size.height : 0 });
  36. }
  37. return super.preprocess(others, ignoreIdCheck);
  38. }
  39. isNode() {
  40. return true;
  41. }
  42. size(width, height, options) {
  43. if (width === undefined) {
  44. return this.getSize();
  45. }
  46. if (typeof width === 'number') {
  47. return this.setSize(width, height, options);
  48. }
  49. return this.setSize(width, height);
  50. }
  51. getSize() {
  52. const size = this.store.get('size');
  53. return size ? Object.assign({}, size) : { width: 1, height: 1 };
  54. }
  55. setSize(width, height, options) {
  56. if (typeof width === 'object') {
  57. this.resize(width.width, width.height, height);
  58. }
  59. else {
  60. this.resize(width, height, options);
  61. }
  62. return this;
  63. }
  64. resize(width, height, options = {}) {
  65. this.startBatch('resize', options);
  66. const direction = options.direction;
  67. if (direction) {
  68. const currentSize = this.getSize();
  69. switch (direction) {
  70. case 'left':
  71. case 'right':
  72. // Don't change height when resizing horizontally.
  73. height = currentSize.height; // eslint-disable-line
  74. break;
  75. case 'top':
  76. case 'bottom':
  77. // Don't change width when resizing vertically.
  78. width = currentSize.width; // eslint-disable-line
  79. break;
  80. default:
  81. break;
  82. }
  83. const map = {
  84. right: 0,
  85. 'top-right': 0,
  86. top: 1,
  87. 'top-left': 1,
  88. left: 2,
  89. 'bottom-left': 2,
  90. bottom: 3,
  91. 'bottom-right': 3,
  92. };
  93. let quadrant = map[direction];
  94. const angle = Angle.normalize(this.getAngle() || 0);
  95. if (options.absolute) {
  96. // We are taking the node's rotation into account
  97. quadrant += Math.floor((angle + 45) / 90);
  98. quadrant %= 4;
  99. }
  100. // This is a rectangle in size of the un-rotated node.
  101. const bbox = this.getBBox();
  102. // Pick the corner point on the node, which meant to stay on its
  103. // place before and after the rotation.
  104. let fixedPoint;
  105. if (quadrant === 0) {
  106. fixedPoint = bbox.getBottomLeft();
  107. }
  108. else if (quadrant === 1) {
  109. fixedPoint = bbox.getCorner();
  110. }
  111. else if (quadrant === 2) {
  112. fixedPoint = bbox.getTopRight();
  113. }
  114. else {
  115. fixedPoint = bbox.getOrigin();
  116. }
  117. // Find an image of the previous indent point. This is the position,
  118. // where is the point actually located on the screen.
  119. const imageFixedPoint = fixedPoint
  120. .clone()
  121. .rotate(-angle, bbox.getCenter());
  122. // Every point on the element rotates around a circle with the centre of
  123. // rotation in the middle of the element while the whole element is being
  124. // rotated. That means that the distance from a point in the corner of
  125. // the element (supposed its always rect) to the center of the element
  126. // doesn't change during the rotation and therefore it equals to a
  127. // distance on un-rotated element.
  128. // We can find the distance as DISTANCE = (ELEMENTWIDTH/2)^2 + (ELEMENTHEIGHT/2)^2)^0.5.
  129. const radius = Math.sqrt(width * width + height * height) / 2;
  130. // Now we are looking for an angle between x-axis and the line starting
  131. // at image of fixed point and ending at the center of the element.
  132. // We call this angle `alpha`.
  133. // The image of a fixed point is located in n-th quadrant. For each
  134. // quadrant passed going anti-clockwise we have to add 90 degrees.
  135. // Note that the first quadrant has index 0.
  136. //
  137. // 3 | 2
  138. // --c-- Quadrant positions around the element's center `c`
  139. // 0 | 1
  140. //
  141. let alpha = (quadrant * Math.PI) / 2;
  142. // Add an angle between the beginning of the current quadrant (line
  143. // parallel with x-axis or y-axis going through the center of the
  144. // element) and line crossing the indent of the fixed point and the
  145. // center of the element. This is the angle we need but on the
  146. // un-rotated element.
  147. alpha += Math.atan(quadrant % 2 === 0 ? height / width : width / height);
  148. // Lastly we have to deduct the original angle the element was rotated
  149. // by and that's it.
  150. alpha -= Angle.toRad(angle);
  151. // With this angle and distance we can easily calculate the centre of
  152. // the un-rotated element.
  153. // Note that fromPolar constructor accepts an angle in radians.
  154. const center = Point.fromPolar(radius, alpha, imageFixedPoint);
  155. // The top left corner on the un-rotated element has to be half a width
  156. // on the left and half a height to the top from the center. This will
  157. // be the origin of rectangle we were looking for.
  158. const origin = center.clone().translate(width / -2, height / -2);
  159. this.store.set('size', { width, height }, options);
  160. this.setPosition(origin.x, origin.y, options);
  161. }
  162. else {
  163. this.store.set('size', { width, height }, options);
  164. }
  165. this.stopBatch('resize', options);
  166. return this;
  167. }
  168. scale(sx, sy, origin, options = {}) {
  169. const scaledBBox = this.getBBox().scale(sx, sy, origin == null ? undefined : origin);
  170. this.startBatch('scale', options);
  171. this.setPosition(scaledBBox.x, scaledBBox.y, options);
  172. this.resize(scaledBBox.width, scaledBBox.height, options);
  173. this.stopBatch('scale');
  174. return this;
  175. }
  176. position(arg0, arg1, arg2) {
  177. if (typeof arg0 === 'number') {
  178. return this.setPosition(arg0, arg1, arg2);
  179. }
  180. return this.getPosition(arg0);
  181. }
  182. getPosition(options = {}) {
  183. if (options.relative) {
  184. const parent = this.getParent();
  185. if (parent != null && parent.isNode()) {
  186. const currentPosition = this.getPosition();
  187. const parentPosition = parent.getPosition();
  188. return {
  189. x: currentPosition.x - parentPosition.x,
  190. y: currentPosition.y - parentPosition.y,
  191. };
  192. }
  193. }
  194. const pos = this.store.get('position');
  195. return pos ? Object.assign({}, pos) : { x: 0, y: 0 };
  196. }
  197. setPosition(arg0, arg1, arg2 = {}) {
  198. let x;
  199. let y;
  200. let options;
  201. if (typeof arg0 === 'object') {
  202. x = arg0.x;
  203. y = arg0.y;
  204. options = arg1 || {};
  205. }
  206. else {
  207. x = arg0;
  208. y = arg1;
  209. options = arg2 || {};
  210. }
  211. if (options.relative) {
  212. const parent = this.getParent();
  213. if (parent != null && parent.isNode()) {
  214. const parentPosition = parent.getPosition();
  215. x += parentPosition.x;
  216. y += parentPosition.y;
  217. }
  218. }
  219. if (options.deep) {
  220. const currentPosition = this.getPosition();
  221. this.translate(x - currentPosition.x, y - currentPosition.y, options);
  222. }
  223. else {
  224. this.store.set('position', { x, y }, options);
  225. }
  226. return this;
  227. }
  228. translate(tx = 0, ty = 0, options = {}) {
  229. if (tx === 0 && ty === 0) {
  230. return this;
  231. }
  232. // Pass the initiator of the translation.
  233. options.translateBy = options.translateBy || this.id;
  234. const position = this.getPosition();
  235. if (options.restrict != null && options.translateBy === this.id) {
  236. // We are restricting the translation for the element itself only. We get
  237. // the bounding box of the element including all its embeds.
  238. // All embeds have to be translated the exact same way as the element.
  239. const bbox = this.getBBox({ deep: true });
  240. const ra = options.restrict;
  241. // - - - - - - - - - - - - -> ra.x + ra.width
  242. // - - - -> position.x |
  243. // -> bbox.x
  244. // ▓▓▓▓▓▓▓ |
  245. // ░░░░░░░▓▓▓▓▓▓▓
  246. // ░░░░░░░░░ |
  247. // ▓▓▓▓▓▓▓▓░░░░░░░
  248. // ▓▓▓▓▓▓▓▓ |
  249. // <-dx-> | restricted area right border
  250. // <-width-> | ░ translated element
  251. // <- - bbox.width - -> ▓ embedded element
  252. const dx = position.x - bbox.x;
  253. const dy = position.y - bbox.y;
  254. // Find the maximal/minimal coordinates that the element can be translated
  255. // while complies the restrictions.
  256. const x = Math.max(ra.x + dx, Math.min(ra.x + ra.width + dx - bbox.width, position.x + tx));
  257. const y = Math.max(ra.y + dy, Math.min(ra.y + ra.height + dy - bbox.height, position.y + ty));
  258. // recalculate the translation taking the restrictions into account.
  259. tx = x - position.x; // eslint-disable-line
  260. ty = y - position.y; // eslint-disable-line
  261. }
  262. const translatedPosition = {
  263. x: position.x + tx,
  264. y: position.y + ty,
  265. };
  266. // To find out by how much an element was translated in event
  267. // 'change:position' handlers.
  268. options.tx = tx;
  269. options.ty = ty;
  270. if (options.transition) {
  271. if (typeof options.transition !== 'object') {
  272. options.transition = {};
  273. }
  274. this.transition('position', translatedPosition, Object.assign(Object.assign({}, options.transition), { interp: Interp.object }));
  275. this.eachChild((child) => {
  276. var _a;
  277. const excluded = (_a = options.exclude) === null || _a === void 0 ? void 0 : _a.includes(child);
  278. if (!excluded) {
  279. child.translate(tx, ty, options);
  280. }
  281. });
  282. }
  283. else {
  284. this.startBatch('translate', options);
  285. this.store.set('position', translatedPosition, options);
  286. this.eachChild((child) => {
  287. var _a;
  288. const excluded = (_a = options.exclude) === null || _a === void 0 ? void 0 : _a.includes(child);
  289. if (!excluded) {
  290. child.translate(tx, ty, options);
  291. }
  292. });
  293. this.stopBatch('translate', options);
  294. }
  295. return this;
  296. }
  297. angle(val, options) {
  298. if (val == null) {
  299. return this.getAngle();
  300. }
  301. return this.rotate(val, options);
  302. }
  303. getAngle() {
  304. return this.store.get('angle', 0);
  305. }
  306. rotate(angle, options = {}) {
  307. const currentAngle = this.getAngle();
  308. if (options.center) {
  309. const size = this.getSize();
  310. const position = this.getPosition();
  311. const center = this.getBBox().getCenter();
  312. center.rotate(currentAngle - angle, options.center);
  313. const dx = center.x - size.width / 2 - position.x;
  314. const dy = center.y - size.height / 2 - position.y;
  315. this.startBatch('rotate', { angle, options });
  316. this.setPosition(position.x + dx, position.y + dy, options);
  317. this.rotate(angle, Object.assign(Object.assign({}, options), { center: null }));
  318. this.stopBatch('rotate');
  319. }
  320. else {
  321. this.store.set('angle', options.absolute ? angle : (currentAngle + angle) % 360, options);
  322. }
  323. return this;
  324. }
  325. // #endregion
  326. // #region common
  327. getBBox(options = {}) {
  328. if (options.deep) {
  329. const cells = this.getDescendants({ deep: true, breadthFirst: true });
  330. cells.push(this);
  331. return Cell.getCellsBBox(cells);
  332. }
  333. return Rectangle.fromPositionAndSize(this.getPosition(), this.getSize());
  334. }
  335. getConnectionPoint(edge, type) {
  336. const bbox = this.getBBox();
  337. const center = bbox.getCenter();
  338. const terminal = edge.getTerminal(type);
  339. if (terminal == null) {
  340. return center;
  341. }
  342. const portId = terminal.port;
  343. if (!portId || !this.hasPort(portId)) {
  344. return center;
  345. }
  346. const port = this.getPort(portId);
  347. if (!port || !port.group) {
  348. return center;
  349. }
  350. const layouts = this.getPortsPosition(port.group);
  351. const position = layouts[portId].position;
  352. const portCenter = Point.create(position).translate(bbox.getOrigin());
  353. const angle = this.getAngle();
  354. if (angle) {
  355. portCenter.rotate(-angle, center);
  356. }
  357. return portCenter;
  358. }
  359. /**
  360. * Sets cell's size and position based on the children bbox and given padding.
  361. */
  362. fit(options = {}) {
  363. const children = this.getChildren() || [];
  364. const embeds = children.filter((cell) => cell.isNode());
  365. if (embeds.length === 0) {
  366. return this;
  367. }
  368. this.startBatch('fit-embeds', options);
  369. if (options.deep) {
  370. embeds.forEach((cell) => cell.fit(options));
  371. }
  372. let { x, y, width, height } = Cell.getCellsBBox(embeds);
  373. const padding = NumberExt.normalizeSides(options.padding);
  374. x -= padding.left;
  375. y -= padding.top;
  376. width += padding.left + padding.right;
  377. height += padding.bottom + padding.top;
  378. this.store.set({
  379. position: { x, y },
  380. size: { width, height },
  381. }, options);
  382. this.stopBatch('fit-embeds');
  383. return this;
  384. }
  385. // #endregion
  386. // #region ports
  387. get portContainerMarkup() {
  388. return this.getPortContainerMarkup();
  389. }
  390. set portContainerMarkup(markup) {
  391. this.setPortContainerMarkup(markup);
  392. }
  393. getDefaultPortContainerMarkup() {
  394. return (this.store.get('defaultPortContainerMarkup') ||
  395. Markup.getPortContainerMarkup());
  396. }
  397. getPortContainerMarkup() {
  398. return (this.store.get('portContainerMarkup') ||
  399. this.getDefaultPortContainerMarkup());
  400. }
  401. setPortContainerMarkup(markup, options = {}) {
  402. this.store.set('portContainerMarkup', Markup.clone(markup), options);
  403. return this;
  404. }
  405. get portMarkup() {
  406. return this.getPortMarkup();
  407. }
  408. set portMarkup(markup) {
  409. this.setPortMarkup(markup);
  410. }
  411. getDefaultPortMarkup() {
  412. return this.store.get('defaultPortMarkup') || Markup.getPortMarkup();
  413. }
  414. getPortMarkup() {
  415. return this.store.get('portMarkup') || this.getDefaultPortMarkup();
  416. }
  417. setPortMarkup(markup, options = {}) {
  418. this.store.set('portMarkup', Markup.clone(markup), options);
  419. return this;
  420. }
  421. get portLabelMarkup() {
  422. return this.getPortLabelMarkup();
  423. }
  424. set portLabelMarkup(markup) {
  425. this.setPortLabelMarkup(markup);
  426. }
  427. getDefaultPortLabelMarkup() {
  428. return (this.store.get('defaultPortLabelMarkup') || Markup.getPortLabelMarkup());
  429. }
  430. getPortLabelMarkup() {
  431. return this.store.get('portLabelMarkup') || this.getDefaultPortLabelMarkup();
  432. }
  433. setPortLabelMarkup(markup, options = {}) {
  434. this.store.set('portLabelMarkup', Markup.clone(markup), options);
  435. return this;
  436. }
  437. get ports() {
  438. const res = this.store.get('ports', { items: [] });
  439. if (res.items == null) {
  440. res.items = [];
  441. }
  442. return res;
  443. }
  444. getPorts() {
  445. return ObjectExt.cloneDeep(this.ports.items);
  446. }
  447. getPortsByGroup(groupName) {
  448. return this.getPorts().filter((port) => port.group === groupName);
  449. }
  450. getPort(portId) {
  451. return ObjectExt.cloneDeep(this.ports.items.find((port) => port.id && port.id === portId));
  452. }
  453. getPortAt(index) {
  454. return this.ports.items[index] || null;
  455. }
  456. hasPorts() {
  457. return this.ports.items.length > 0;
  458. }
  459. hasPort(portId) {
  460. return this.getPortIndex(portId) !== -1;
  461. }
  462. getPortIndex(port) {
  463. const portId = typeof port === 'string' ? port : port.id;
  464. return portId != null
  465. ? this.ports.items.findIndex((item) => item.id === portId)
  466. : -1;
  467. }
  468. getPortsPosition(groupName) {
  469. const size = this.getSize();
  470. const layouts = this.port.getPortsLayoutByGroup(groupName, new Rectangle(0, 0, size.width, size.height));
  471. return layouts.reduce((memo, item) => {
  472. const layout = item.portLayout;
  473. memo[item.portId] = {
  474. position: Object.assign({}, layout.position),
  475. angle: layout.angle || 0,
  476. };
  477. return memo;
  478. }, {});
  479. }
  480. getPortProp(portId, path) {
  481. return this.getPropByPath(this.prefixPortPath(portId, path));
  482. }
  483. setPortProp(portId, arg1, arg2, arg3) {
  484. if (typeof arg1 === 'string' || Array.isArray(arg1)) {
  485. const path = this.prefixPortPath(portId, arg1);
  486. const value = arg2;
  487. return this.setPropByPath(path, value, arg3);
  488. }
  489. const path = this.prefixPortPath(portId);
  490. const value = arg1;
  491. return this.setPropByPath(path, value, arg2);
  492. }
  493. removePortProp(portId, path, options) {
  494. if (typeof path === 'string' || Array.isArray(path)) {
  495. return this.removePropByPath(this.prefixPortPath(portId, path), options);
  496. }
  497. return this.removePropByPath(this.prefixPortPath(portId), path);
  498. }
  499. portProp(portId, path, value, options) {
  500. if (path == null) {
  501. return this.getPortProp(portId);
  502. }
  503. if (typeof path === 'string' || Array.isArray(path)) {
  504. if (arguments.length === 2) {
  505. return this.getPortProp(portId, path);
  506. }
  507. if (value == null) {
  508. return this.removePortProp(portId, path, options);
  509. }
  510. return this.setPortProp(portId, path, value, options);
  511. }
  512. return this.setPortProp(portId, path, value);
  513. }
  514. prefixPortPath(portId, path) {
  515. const index = this.getPortIndex(portId);
  516. if (index === -1) {
  517. throw new Error(`Unable to find port with id: "${portId}"`);
  518. }
  519. if (path == null || path === '') {
  520. return ['ports', 'items', `${index}`];
  521. }
  522. if (Array.isArray(path)) {
  523. return ['ports', 'items', `${index}`, ...path];
  524. }
  525. return `ports/items/${index}/${path}`;
  526. }
  527. addPort(port, options) {
  528. const ports = [...this.ports.items];
  529. ports.push(port);
  530. this.setPropByPath('ports/items', ports, options);
  531. return this;
  532. }
  533. addPorts(ports, options) {
  534. this.setPropByPath('ports/items', [...this.ports.items, ...ports], options);
  535. return this;
  536. }
  537. insertPort(index, port, options) {
  538. const ports = [...this.ports.items];
  539. ports.splice(index, 0, port);
  540. this.setPropByPath('ports/items', ports, options);
  541. return this;
  542. }
  543. removePort(port, options = {}) {
  544. return this.removePortAt(this.getPortIndex(port), options);
  545. }
  546. removePortAt(index, options = {}) {
  547. if (index >= 0) {
  548. const ports = [...this.ports.items];
  549. ports.splice(index, 1);
  550. options.rewrite = true;
  551. this.setPropByPath('ports/items', ports, options);
  552. }
  553. return this;
  554. }
  555. removePorts(portsForRemoval, opt) {
  556. let options;
  557. if (Array.isArray(portsForRemoval)) {
  558. options = opt || {};
  559. if (portsForRemoval.length) {
  560. options.rewrite = true;
  561. const currentPorts = [...this.ports.items];
  562. const remainingPorts = currentPorts.filter((cp) => !portsForRemoval.some((p) => {
  563. const id = typeof p === 'string' ? p : p.id;
  564. return cp.id === id;
  565. }));
  566. this.setPropByPath('ports/items', remainingPorts, options);
  567. }
  568. }
  569. else {
  570. options = portsForRemoval || {};
  571. options.rewrite = true;
  572. this.setPropByPath('ports/items', [], options);
  573. }
  574. return this;
  575. }
  576. getParsedPorts() {
  577. return this.port.getPorts();
  578. }
  579. getParsedGroups() {
  580. return this.port.groups;
  581. }
  582. getPortsLayoutByGroup(groupName, bbox) {
  583. return this.port.getPortsLayoutByGroup(groupName, bbox);
  584. }
  585. initPorts() {
  586. this.updatePortData();
  587. this.on('change:ports', () => {
  588. this.processRemovedPort();
  589. this.updatePortData();
  590. });
  591. }
  592. processRemovedPort() {
  593. const current = this.ports;
  594. const currentItemsMap = {};
  595. current.items.forEach((item) => {
  596. if (item.id) {
  597. currentItemsMap[item.id] = true;
  598. }
  599. });
  600. const removed = {};
  601. const previous = this.store.getPrevious('ports') || {
  602. items: [],
  603. };
  604. previous.items.forEach((item) => {
  605. if (item.id && !currentItemsMap[item.id]) {
  606. removed[item.id] = true;
  607. }
  608. });
  609. const model = this.model;
  610. if (model && !ObjectExt.isEmpty(removed)) {
  611. const incomings = model.getConnectedEdges(this, { incoming: true });
  612. incomings.forEach((edge) => {
  613. const portId = edge.getTargetPortId();
  614. if (portId && removed[portId]) {
  615. edge.remove();
  616. }
  617. });
  618. const outgoings = model.getConnectedEdges(this, { outgoing: true });
  619. outgoings.forEach((edge) => {
  620. const portId = edge.getSourcePortId();
  621. if (portId && removed[portId]) {
  622. edge.remove();
  623. }
  624. });
  625. }
  626. }
  627. validatePorts() {
  628. const ids = {};
  629. const errors = [];
  630. this.ports.items.forEach((p) => {
  631. if (typeof p !== 'object') {
  632. errors.push(`Invalid port ${p}.`);
  633. }
  634. if (p.id == null) {
  635. p.id = this.generatePortId();
  636. }
  637. if (ids[p.id]) {
  638. errors.push('Duplicitied port id.');
  639. }
  640. ids[p.id] = true;
  641. });
  642. return errors;
  643. }
  644. generatePortId() {
  645. return StringExt.uuid();
  646. }
  647. updatePortData() {
  648. const err = this.validatePorts();
  649. if (err.length > 0) {
  650. this.store.set('ports', this.store.getPrevious('ports'));
  651. throw new Error(err.join(' '));
  652. }
  653. const prev = this.port ? this.port.getPorts() : null;
  654. this.port = new PortManager(this.ports);
  655. const curr = this.port.getPorts();
  656. const added = prev
  657. ? curr.filter((item) => {
  658. if (!prev.find((prevPort) => prevPort.id === item.id)) {
  659. return item;
  660. }
  661. return null;
  662. })
  663. : [...curr];
  664. const removed = prev
  665. ? prev.filter((item) => {
  666. if (!curr.find((curPort) => curPort.id === item.id)) {
  667. return item;
  668. }
  669. return null;
  670. })
  671. : [];
  672. if (added.length > 0) {
  673. this.notify('ports:added', { added, cell: this, node: this });
  674. }
  675. if (removed.length > 0) {
  676. this.notify('ports:removed', { removed, cell: this, node: this });
  677. }
  678. }
  679. }
  680. Node.defaults = {
  681. angle: 0,
  682. position: { x: 0, y: 0 },
  683. size: { width: 1, height: 1 },
  684. };
  685. (function (Node) {
  686. Node.toStringTag = `X6.${Node.name}`;
  687. function isNode(instance) {
  688. if (instance == null) {
  689. return false;
  690. }
  691. if (instance instanceof Node) {
  692. return true;
  693. }
  694. const tag = instance[Symbol.toStringTag];
  695. const node = instance;
  696. if ((tag == null || tag === Node.toStringTag) &&
  697. typeof node.isNode === 'function' &&
  698. typeof node.isEdge === 'function' &&
  699. typeof node.prop === 'function' &&
  700. typeof node.attr === 'function' &&
  701. typeof node.size === 'function' &&
  702. typeof node.position === 'function') {
  703. return true;
  704. }
  705. return false;
  706. }
  707. Node.isNode = isNode;
  708. })(Node || (Node = {}));
  709. (function (Node) {
  710. Node.config({
  711. propHooks(_a) {
  712. var { ports } = _a, metadata = __rest(_a, ["ports"]);
  713. if (ports) {
  714. metadata.ports = Array.isArray(ports) ? { items: ports } : ports;
  715. }
  716. return metadata;
  717. },
  718. });
  719. })(Node || (Node = {}));
  720. (function (Node) {
  721. Node.registry = Registry.create({
  722. type: 'node',
  723. process(shape, options) {
  724. if (ShareRegistry.exist(shape, true)) {
  725. throw new Error(`Node with name '${shape}' was registered by anthor Edge`);
  726. }
  727. if (typeof options === 'function') {
  728. options.config({ shape });
  729. return options;
  730. }
  731. let parent = Node;
  732. const { inherit } = options, config = __rest(options, ["inherit"]);
  733. if (inherit) {
  734. if (typeof inherit === 'string') {
  735. const base = this.get(inherit);
  736. if (base == null) {
  737. this.onNotFound(inherit, 'inherited');
  738. }
  739. else {
  740. parent = base;
  741. }
  742. }
  743. else {
  744. parent = inherit;
  745. }
  746. }
  747. if (config.constructorName == null) {
  748. config.constructorName = shape;
  749. }
  750. const ctor = parent.define.call(parent, config);
  751. ctor.config({ shape });
  752. return ctor;
  753. },
  754. });
  755. ShareRegistry.setNodeRegistry(Node.registry);
  756. })(Node || (Node = {}));
  757. (function (Node) {
  758. let counter = 0;
  759. function getClassName(name) {
  760. if (name) {
  761. return StringExt.pascalCase(name);
  762. }
  763. counter += 1;
  764. return `CustomNode${counter}`;
  765. }
  766. function define(config) {
  767. const { constructorName, overwrite } = config, others = __rest(config, ["constructorName", "overwrite"]);
  768. const ctor = ObjectExt.createClass(getClassName(constructorName || others.shape), this);
  769. ctor.config(others);
  770. if (others.shape) {
  771. Node.registry.register(others.shape, ctor, overwrite);
  772. }
  773. return ctor;
  774. }
  775. Node.define = define;
  776. function create(options) {
  777. const shape = options.shape || 'rect';
  778. const Ctor = Node.registry.get(shape);
  779. if (Ctor) {
  780. return new Ctor(options);
  781. }
  782. return Node.registry.onNotFound(shape);
  783. }
  784. Node.create = create;
  785. })(Node || (Node = {}));
  786. //# sourceMappingURL=node.js.map