view.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  2. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  3. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  4. else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  5. return c > 3 && r && Object.defineProperty(target, key, r), r;
  6. };
  7. import { Dom, FunctionExt } from '@antv/x6-common';
  8. import { Cell } from '../model';
  9. import { Config } from '../config';
  10. import { View, Markup } from '../view';
  11. export class GraphView extends View {
  12. /** Graph's `this.container` is from outer, should not dispose */
  13. get disposeContainer() {
  14. return false;
  15. }
  16. get options() {
  17. return this.graph.options;
  18. }
  19. constructor(graph) {
  20. super();
  21. this.graph = graph;
  22. const { selectors, fragment } = Markup.parseJSONMarkup(GraphView.markup);
  23. this.background = selectors.background;
  24. this.grid = selectors.grid;
  25. this.svg = selectors.svg;
  26. this.defs = selectors.defs;
  27. this.viewport = selectors.viewport;
  28. this.primer = selectors.primer;
  29. this.stage = selectors.stage;
  30. this.decorator = selectors.decorator;
  31. this.overlay = selectors.overlay;
  32. this.container = this.options.container;
  33. this.restore = GraphView.snapshoot(this.container);
  34. Dom.addClass(this.container, this.prefixClassName('graph'));
  35. Dom.append(this.container, fragment);
  36. this.delegateEvents();
  37. }
  38. delegateEvents() {
  39. const ctor = this.constructor;
  40. super.delegateEvents(ctor.events);
  41. return this;
  42. }
  43. /**
  44. * Guard the specified event. If the event is not interesting, it
  45. * returns `true`, otherwise returns `false`.
  46. */
  47. guard(e, view) {
  48. // handled as `contextmenu` type
  49. if (e.type === 'mousedown' && e.button === 2) {
  50. return true;
  51. }
  52. if (this.options.guard && this.options.guard(e, view)) {
  53. return true;
  54. }
  55. if (e.data && e.data.guarded !== undefined) {
  56. return e.data.guarded;
  57. }
  58. if (view && view.cell && Cell.isCell(view.cell)) {
  59. return false;
  60. }
  61. if (this.svg === e.target ||
  62. this.container === e.target ||
  63. this.svg.contains(e.target)) {
  64. return false;
  65. }
  66. return true;
  67. }
  68. findView(elem) {
  69. return this.graph.findViewByElem(elem);
  70. }
  71. onDblClick(evt) {
  72. if (this.options.preventDefaultDblClick) {
  73. evt.preventDefault();
  74. }
  75. const e = this.normalizeEvent(evt);
  76. const view = this.findView(e.target);
  77. if (this.guard(e, view)) {
  78. return;
  79. }
  80. const localPoint = this.graph.snapToGrid(e.clientX, e.clientY);
  81. if (view) {
  82. view.onDblClick(e, localPoint.x, localPoint.y);
  83. }
  84. else {
  85. this.graph.trigger('blank:dblclick', {
  86. e,
  87. x: localPoint.x,
  88. y: localPoint.y,
  89. });
  90. }
  91. }
  92. onClick(evt) {
  93. if (this.getMouseMovedCount(evt) <= this.options.clickThreshold) {
  94. const e = this.normalizeEvent(evt);
  95. const view = this.findView(e.target);
  96. if (this.guard(e, view)) {
  97. return;
  98. }
  99. const localPoint = this.graph.snapToGrid(e.clientX, e.clientY);
  100. if (view) {
  101. view.onClick(e, localPoint.x, localPoint.y);
  102. }
  103. else {
  104. this.graph.trigger('blank:click', {
  105. e,
  106. x: localPoint.x,
  107. y: localPoint.y,
  108. });
  109. }
  110. }
  111. }
  112. isPreventDefaultContextMenu(view) {
  113. let preventDefaultContextMenu = this.options.preventDefaultContextMenu;
  114. if (typeof preventDefaultContextMenu === 'function') {
  115. preventDefaultContextMenu = FunctionExt.call(preventDefaultContextMenu, this.graph, { view });
  116. }
  117. return preventDefaultContextMenu;
  118. }
  119. onContextMenu(evt) {
  120. const e = this.normalizeEvent(evt);
  121. const view = this.findView(e.target);
  122. if (this.isPreventDefaultContextMenu(view)) {
  123. evt.preventDefault();
  124. }
  125. if (this.guard(e, view)) {
  126. return;
  127. }
  128. const localPoint = this.graph.snapToGrid(e.clientX, e.clientY);
  129. if (view) {
  130. view.onContextMenu(e, localPoint.x, localPoint.y);
  131. }
  132. else {
  133. this.graph.trigger('blank:contextmenu', {
  134. e,
  135. x: localPoint.x,
  136. y: localPoint.y,
  137. });
  138. }
  139. }
  140. delegateDragEvents(e, view) {
  141. if (e.data == null) {
  142. e.data = {};
  143. }
  144. this.setEventData(e, {
  145. currentView: view || null,
  146. mouseMovedCount: 0,
  147. startPosition: {
  148. x: e.clientX,
  149. y: e.clientY,
  150. },
  151. });
  152. const ctor = this.constructor;
  153. this.delegateDocumentEvents(ctor.documentEvents, e.data);
  154. this.undelegateEvents();
  155. }
  156. getMouseMovedCount(e) {
  157. const data = this.getEventData(e);
  158. return data.mouseMovedCount || 0;
  159. }
  160. onMouseDown(evt) {
  161. const e = this.normalizeEvent(evt);
  162. const view = this.findView(e.target);
  163. if (this.guard(e, view)) {
  164. return;
  165. }
  166. if (this.options.preventDefaultMouseDown) {
  167. evt.preventDefault();
  168. }
  169. const localPoint = this.graph.snapToGrid(e.clientX, e.clientY);
  170. if (view) {
  171. view.onMouseDown(e, localPoint.x, localPoint.y);
  172. }
  173. else {
  174. if (this.options.preventDefaultBlankAction &&
  175. ['touchstart'].includes(e.type)) {
  176. evt.preventDefault();
  177. }
  178. this.graph.trigger('blank:mousedown', {
  179. e,
  180. x: localPoint.x,
  181. y: localPoint.y,
  182. });
  183. }
  184. this.delegateDragEvents(e, view);
  185. }
  186. onMouseMove(evt) {
  187. const data = this.getEventData(evt);
  188. const startPosition = data.startPosition;
  189. if (startPosition &&
  190. startPosition.x === evt.clientX &&
  191. startPosition.y === evt.clientY) {
  192. return;
  193. }
  194. if (data.mouseMovedCount == null) {
  195. data.mouseMovedCount = 0;
  196. }
  197. data.mouseMovedCount += 1;
  198. const mouseMovedCount = data.mouseMovedCount;
  199. if (mouseMovedCount <= this.options.moveThreshold) {
  200. return;
  201. }
  202. const e = this.normalizeEvent(evt);
  203. const localPoint = this.graph.snapToGrid(e.clientX, e.clientY);
  204. const view = data.currentView;
  205. if (view) {
  206. view.onMouseMove(e, localPoint.x, localPoint.y);
  207. }
  208. else {
  209. this.graph.trigger('blank:mousemove', {
  210. e,
  211. x: localPoint.x,
  212. y: localPoint.y,
  213. });
  214. }
  215. this.setEventData(e, data);
  216. }
  217. onMouseUp(e) {
  218. this.undelegateDocumentEvents();
  219. const normalized = this.normalizeEvent(e);
  220. const localPoint = this.graph.snapToGrid(normalized.clientX, normalized.clientY);
  221. const data = this.getEventData(e);
  222. const view = data.currentView;
  223. if (view) {
  224. view.onMouseUp(normalized, localPoint.x, localPoint.y);
  225. }
  226. else {
  227. this.graph.trigger('blank:mouseup', {
  228. e: normalized,
  229. x: localPoint.x,
  230. y: localPoint.y,
  231. });
  232. }
  233. if (!e.isPropagationStopped()) {
  234. const ev = new Dom.EventObject(e, {
  235. type: 'click',
  236. data: e.data,
  237. });
  238. this.onClick(ev);
  239. }
  240. e.stopImmediatePropagation();
  241. this.delegateEvents();
  242. }
  243. onMouseOver(evt) {
  244. const e = this.normalizeEvent(evt);
  245. const view = this.findView(e.target);
  246. if (this.guard(e, view)) {
  247. return;
  248. }
  249. if (view) {
  250. view.onMouseOver(e);
  251. }
  252. else {
  253. // prevent border of paper from triggering this
  254. if (this.container === e.target) {
  255. return;
  256. }
  257. this.graph.trigger('blank:mouseover', { e });
  258. }
  259. }
  260. onMouseOut(evt) {
  261. const e = this.normalizeEvent(evt);
  262. const view = this.findView(e.target);
  263. if (this.guard(e, view)) {
  264. return;
  265. }
  266. if (view) {
  267. view.onMouseOut(e);
  268. }
  269. else {
  270. if (this.container === e.target) {
  271. return;
  272. }
  273. this.graph.trigger('blank:mouseout', { e });
  274. }
  275. }
  276. onMouseEnter(evt) {
  277. const e = this.normalizeEvent(evt);
  278. const view = this.findView(e.target);
  279. if (this.guard(e, view)) {
  280. return;
  281. }
  282. const relatedView = this.graph.findViewByElem(e.relatedTarget);
  283. if (view) {
  284. if (relatedView === view) {
  285. // mouse moved from tool to view
  286. return;
  287. }
  288. view.onMouseEnter(e);
  289. }
  290. else {
  291. if (relatedView) {
  292. return;
  293. }
  294. this.graph.trigger('graph:mouseenter', { e });
  295. }
  296. }
  297. onMouseLeave(evt) {
  298. const e = this.normalizeEvent(evt);
  299. const view = this.findView(e.target);
  300. if (this.guard(e, view)) {
  301. return;
  302. }
  303. const relatedView = this.graph.findViewByElem(e.relatedTarget);
  304. if (view) {
  305. if (relatedView === view) {
  306. // mouse moved from view to tool
  307. return;
  308. }
  309. view.onMouseLeave(e);
  310. }
  311. else {
  312. if (relatedView) {
  313. return;
  314. }
  315. this.graph.trigger('graph:mouseleave', { e });
  316. }
  317. }
  318. onMouseWheel(evt) {
  319. const e = this.normalizeEvent(evt);
  320. const view = this.findView(e.target);
  321. if (this.guard(e, view)) {
  322. return;
  323. }
  324. const originalEvent = e.originalEvent;
  325. const localPoint = this.graph.snapToGrid(originalEvent.clientX, originalEvent.clientY);
  326. const delta = Math.max(-1, Math.min(1, originalEvent.wheelDelta || -originalEvent.detail));
  327. if (view) {
  328. view.onMouseWheel(e, localPoint.x, localPoint.y, delta);
  329. }
  330. else {
  331. this.graph.trigger('blank:mousewheel', {
  332. e,
  333. delta,
  334. x: localPoint.x,
  335. y: localPoint.y,
  336. });
  337. }
  338. }
  339. onCustomEvent(evt) {
  340. const elem = evt.currentTarget;
  341. const event = elem.getAttribute('event') || elem.getAttribute('data-event');
  342. if (event) {
  343. const view = this.findView(elem);
  344. if (view) {
  345. const e = this.normalizeEvent(evt);
  346. if (this.guard(e, view)) {
  347. return;
  348. }
  349. const localPoint = this.graph.snapToGrid(e.clientX, e.clientY);
  350. view.onCustomEvent(e, event, localPoint.x, localPoint.y);
  351. }
  352. }
  353. }
  354. handleMagnetEvent(evt, handler) {
  355. const magnetElem = evt.currentTarget;
  356. const magnetValue = magnetElem.getAttribute('magnet');
  357. if (magnetValue && magnetValue.toLowerCase() !== 'false') {
  358. const view = this.findView(magnetElem);
  359. if (view) {
  360. const e = this.normalizeEvent(evt);
  361. if (this.guard(e, view)) {
  362. return;
  363. }
  364. const localPoint = this.graph.snapToGrid(e.clientX, e.clientY);
  365. FunctionExt.call(handler, this.graph, view, e, magnetElem, localPoint.x, localPoint.y);
  366. }
  367. }
  368. }
  369. onMagnetMouseDown(e) {
  370. this.handleMagnetEvent(e, (view, e, magnet, x, y) => {
  371. view.onMagnetMouseDown(e, magnet, x, y);
  372. });
  373. }
  374. onMagnetDblClick(e) {
  375. this.handleMagnetEvent(e, (view, e, magnet, x, y) => {
  376. view.onMagnetDblClick(e, magnet, x, y);
  377. });
  378. }
  379. onMagnetContextMenu(e) {
  380. const view = this.findView(e.target);
  381. if (this.isPreventDefaultContextMenu(view)) {
  382. e.preventDefault();
  383. }
  384. this.handleMagnetEvent(e, (view, e, magnet, x, y) => {
  385. view.onMagnetContextMenu(e, magnet, x, y);
  386. });
  387. }
  388. onLabelMouseDown(evt) {
  389. const labelNode = evt.currentTarget;
  390. const view = this.findView(labelNode);
  391. if (view) {
  392. const e = this.normalizeEvent(evt);
  393. if (this.guard(e, view)) {
  394. return;
  395. }
  396. const localPoint = this.graph.snapToGrid(e.clientX, e.clientY);
  397. view.onLabelMouseDown(e, localPoint.x, localPoint.y);
  398. }
  399. }
  400. onImageDragStart() {
  401. // This is the only way to prevent image dragging in Firefox that works.
  402. // Setting -moz-user-select: none, draggable="false" attribute or
  403. // user-drag: none didn't help.
  404. return false;
  405. }
  406. dispose() {
  407. this.undelegateEvents();
  408. this.undelegateDocumentEvents();
  409. this.restore();
  410. this.restore = () => { };
  411. }
  412. }
  413. __decorate([
  414. View.dispose()
  415. ], GraphView.prototype, "dispose", null);
  416. (function (GraphView) {
  417. const prefixCls = `${Config.prefixCls}-graph`;
  418. GraphView.markup = [
  419. {
  420. ns: Dom.ns.xhtml,
  421. tagName: 'div',
  422. selector: 'background',
  423. className: `${prefixCls}-background`,
  424. },
  425. {
  426. ns: Dom.ns.xhtml,
  427. tagName: 'div',
  428. selector: 'grid',
  429. className: `${prefixCls}-grid`,
  430. },
  431. {
  432. ns: Dom.ns.svg,
  433. tagName: 'svg',
  434. selector: 'svg',
  435. className: `${prefixCls}-svg`,
  436. attrs: {
  437. width: '100%',
  438. height: '100%',
  439. 'xmlns:xlink': Dom.ns.xlink,
  440. },
  441. children: [
  442. {
  443. tagName: 'defs',
  444. selector: 'defs',
  445. },
  446. {
  447. tagName: 'g',
  448. selector: 'viewport',
  449. className: `${prefixCls}-svg-viewport`,
  450. children: [
  451. {
  452. tagName: 'g',
  453. selector: 'primer',
  454. className: `${prefixCls}-svg-primer`,
  455. },
  456. {
  457. tagName: 'g',
  458. selector: 'stage',
  459. className: `${prefixCls}-svg-stage`,
  460. },
  461. {
  462. tagName: 'g',
  463. selector: 'decorator',
  464. className: `${prefixCls}-svg-decorator`,
  465. },
  466. {
  467. tagName: 'g',
  468. selector: 'overlay',
  469. className: `${prefixCls}-svg-overlay`,
  470. },
  471. ],
  472. },
  473. ],
  474. },
  475. ];
  476. function snapshoot(elem) {
  477. const cloned = elem.cloneNode();
  478. elem.childNodes.forEach((child) => cloned.appendChild(child));
  479. return () => {
  480. // remove all children
  481. Dom.empty(elem);
  482. // remove all attributes
  483. while (elem.attributes.length > 0) {
  484. elem.removeAttribute(elem.attributes[0].name);
  485. }
  486. // restore attributes
  487. for (let i = 0, l = cloned.attributes.length; i < l; i += 1) {
  488. const attr = cloned.attributes[i];
  489. elem.setAttribute(attr.name, attr.value);
  490. }
  491. // restore children
  492. cloned.childNodes.forEach((child) => elem.appendChild(child));
  493. };
  494. }
  495. GraphView.snapshoot = snapshoot;
  496. })(GraphView || (GraphView = {}));
  497. (function (GraphView) {
  498. const prefixCls = Config.prefixCls;
  499. GraphView.events = {
  500. dblclick: 'onDblClick',
  501. contextmenu: 'onContextMenu',
  502. touchstart: 'onMouseDown',
  503. mousedown: 'onMouseDown',
  504. mouseover: 'onMouseOver',
  505. mouseout: 'onMouseOut',
  506. mouseenter: 'onMouseEnter',
  507. mouseleave: 'onMouseLeave',
  508. mousewheel: 'onMouseWheel',
  509. DOMMouseScroll: 'onMouseWheel',
  510. [`mouseenter .${prefixCls}-cell`]: 'onMouseEnter',
  511. [`mouseleave .${prefixCls}-cell`]: 'onMouseLeave',
  512. [`mouseenter .${prefixCls}-cell-tools`]: 'onMouseEnter',
  513. [`mouseleave .${prefixCls}-cell-tools`]: 'onMouseLeave',
  514. [`mousedown .${prefixCls}-cell [event]`]: 'onCustomEvent',
  515. [`touchstart .${prefixCls}-cell [event]`]: 'onCustomEvent',
  516. [`mousedown .${prefixCls}-cell [data-event]`]: 'onCustomEvent',
  517. [`touchstart .${prefixCls}-cell [data-event]`]: 'onCustomEvent',
  518. [`dblclick .${prefixCls}-cell [magnet]`]: 'onMagnetDblClick',
  519. [`contextmenu .${prefixCls}-cell [magnet]`]: 'onMagnetContextMenu',
  520. [`mousedown .${prefixCls}-cell [magnet]`]: 'onMagnetMouseDown',
  521. [`touchstart .${prefixCls}-cell [magnet]`]: 'onMagnetMouseDown',
  522. [`dblclick .${prefixCls}-cell [data-magnet]`]: 'onMagnetDblClick',
  523. [`contextmenu .${prefixCls}-cell [data-magnet]`]: 'onMagnetContextMenu',
  524. [`mousedown .${prefixCls}-cell [data-magnet]`]: 'onMagnetMouseDown',
  525. [`touchstart .${prefixCls}-cell [data-magnet]`]: 'onMagnetMouseDown',
  526. [`dragstart .${prefixCls}-cell image`]: 'onImageDragStart',
  527. [`mousedown .${prefixCls}-edge .${prefixCls}-edge-label`]: 'onLabelMouseDown',
  528. [`touchstart .${prefixCls}-edge .${prefixCls}-edge-label`]: 'onLabelMouseDown',
  529. };
  530. GraphView.documentEvents = {
  531. mousemove: 'onMouseMove',
  532. touchmove: 'onMouseMove',
  533. mouseup: 'onMouseUp',
  534. touchend: 'onMouseUp',
  535. touchcancel: 'onMouseUp',
  536. };
  537. })(GraphView || (GraphView = {}));
  538. //# sourceMappingURL=view.js.map