@joint/core 4.2.0-alpha.0 → 4.2.0-alpha.1

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.
@@ -34,6 +34,8 @@ export const LinkView = CellView.extend({
34
34
  _labelCache: null,
35
35
  _labelSelectors: null,
36
36
  _V: null,
37
+ _sourceMagnet: null,
38
+ _targetMagnet: null,
37
39
  _dragData: null, // deprecated
38
40
 
39
41
  metrics: null,
@@ -75,23 +77,31 @@ export const LinkView = CellView.extend({
75
77
  UPDATE_PRIORITY: 1,
76
78
  EPSILON: 1e-6,
77
79
 
78
- confirmUpdate: function(flags, opt) {
80
+ confirmUpdate: function(flags, opt = {}) {
79
81
 
80
- opt || (opt = {});
82
+ const { paper, model } = this;
83
+ const { attributes } = model;
84
+ const { source: { id: sourceId }, target: { id: targetId }} = attributes;
81
85
 
82
86
  if (this.hasFlag(flags, Flags.SOURCE)) {
83
- if (!this.updateEndProperties('source')) return flags;
87
+ this._sourceMagnet = null; // reset cached source magnet
88
+ this.checkEndModel('source', sourceId);
84
89
  flags = this.removeFlag(flags, Flags.SOURCE);
85
90
  }
86
91
 
87
92
  if (this.hasFlag(flags, Flags.TARGET)) {
88
- if (!this.updateEndProperties('target')) return flags;
93
+ this._targetMagnet = null; // reset cached target magnet
94
+ this.checkEndModel('target', targetId);
89
95
  flags = this.removeFlag(flags, Flags.TARGET);
90
96
  }
91
97
 
92
- const { paper, sourceView, targetView } = this;
93
- if (paper && ((sourceView && !paper.isViewMounted(sourceView)) || (targetView && !paper.isViewMounted(targetView)))) {
94
- // Wait for the sourceView and targetView to be rendered
98
+ if (
99
+ paper && (
100
+ (sourceId && !paper.isCellVisible(sourceId)) ||
101
+ (targetId && !paper.isCellVisible(targetId))
102
+ )
103
+ ) {
104
+ // Wait for the source and target views to be rendered
95
105
  return flags;
96
106
  }
97
107
 
@@ -101,8 +111,8 @@ export const LinkView = CellView.extend({
101
111
  this.updateTools(opt);
102
112
  flags = this.removeFlag(flags, [Flags.RENDER, Flags.UPDATE, Flags.LABELS, Flags.TOOLS, Flags.CONNECTOR]);
103
113
 
104
- if (env.test('isSafari')) {
105
- this.__fixSafariBug268376();
114
+ if (env.test('isAppleWebKit')) {
115
+ this.__fixWebKitBug268376();
106
116
  }
107
117
 
108
118
  return flags;
@@ -110,8 +120,6 @@ export const LinkView = CellView.extend({
110
120
 
111
121
  let updateHighlighters = false;
112
122
 
113
- const { model } = this;
114
- const { attributes } = model;
115
123
  let updateLabels = this.hasFlag(flags, Flags.LABELS);
116
124
 
117
125
  if (updateLabels) {
@@ -157,8 +165,8 @@ export const LinkView = CellView.extend({
157
165
  return flags;
158
166
  },
159
167
 
160
- __fixSafariBug268376: function() {
161
- // Safari has a bug where any change after the first render is not reflected in the DOM.
168
+ __fixWebKitBug268376: function() {
169
+ // WebKit has a bug where any change after the first render is not reflected in the DOM.
162
170
  // https://bugs.webkit.org/show_bug.cgi?id=268376
163
171
  const { el } = this;
164
172
  const childNodes = Array.from(el.childNodes);
@@ -931,44 +939,11 @@ export const LinkView = CellView.extend({
931
939
  }
932
940
  },
933
941
 
934
- updateEndProperties: function(endType) {
935
-
936
- const { model, paper } = this;
937
- const endViewProperty = `${endType}View`;
938
- const endDef = model.get(endType);
939
- const endId = endDef && endDef.id;
940
-
941
- if (!endId) {
942
- // the link end is a point ~ rect 0x0
943
- this[endViewProperty] = null;
944
- this.updateEndMagnet(endType);
945
- return true;
946
- }
947
-
948
- const endModel = paper.getModelById(endId);
949
- if (!endModel) throw new Error('LinkView: invalid ' + endType + ' cell.');
950
-
951
- const endView = endModel.findView(paper);
952
- if (!endView) {
953
- // A view for a model should always exist
954
- return false;
955
- }
956
-
957
- this[endViewProperty] = endView;
958
- this.updateEndMagnet(endType);
959
- return true;
960
- },
961
-
962
- updateEndMagnet: function(endType) {
963
-
964
- const endMagnetProperty = `${endType}Magnet`;
965
- const endView = this.getEndView(endType);
966
- if (endView) {
967
- let connectedMagnet = endView.getMagnetFromLinkEnd(this.model.get(endType));
968
- if (connectedMagnet === endView.el) connectedMagnet = null;
969
- this[endMagnetProperty] = connectedMagnet;
970
- } else {
971
- this[endMagnetProperty] = null;
942
+ checkEndModel: function(endType, endId) {
943
+ if (!endId) return;
944
+ const endModel = this.paper.getModelById(endId);
945
+ if (!endModel) {
946
+ throw new Error(`LinkView: invalid ${endType} cell.`);
972
947
  }
973
948
  },
974
949
 
@@ -1858,56 +1833,30 @@ export const LinkView = CellView.extend({
1858
1833
  let isSnapped = false;
1859
1834
  // checking view in close area of the pointer
1860
1835
 
1861
- var r = snapLinks.radius || 50;
1862
- var viewsInArea = paper.findElementViewsInArea(
1863
- { x: x - r, y: y - r, width: 2 * r, height: 2 * r },
1864
- snapLinks.findInAreaOptions
1865
- );
1836
+ const radius = snapLinks.radius || 50;
1837
+ const findInAreaOptions = snapLinks.findInAreaOptions;
1866
1838
 
1867
- var prevClosestView = data.closestView || null;
1868
- var prevClosestMagnet = data.closestMagnet || null;
1869
- var prevMagnetProxy = data.magnetProxy || null;
1839
+ const prevClosestView = data.closestView || null;
1840
+ const prevClosestMagnet = data.closestMagnet || null;
1841
+ const prevMagnetProxy = data.magnetProxy || null;
1870
1842
 
1871
1843
  data.closestView = data.closestMagnet = data.magnetProxy = null;
1872
1844
 
1873
- var minDistance = Number.MAX_VALUE;
1874
- var pointer = new Point(x, y);
1875
-
1876
- viewsInArea.forEach(function(view) {
1877
- const candidates = [];
1878
- // skip connecting to the element in case '.': { magnet: false } attribute present
1879
- if (view.el.getAttribute('magnet') !== 'false') {
1880
- candidates.push({
1881
- bbox: view.model.getBBox(),
1882
- magnet: view.el
1883
- });
1845
+ const isValidCandidate = (view, magnet) => {
1846
+ // Do not snap to the current view
1847
+ if (view === this) {
1848
+ return false;
1884
1849
  }
1885
1850
 
1886
- view.$('[magnet]').toArray().forEach(magnet => {
1887
- candidates.push({
1888
- bbox: view.getNodeBBox(magnet),
1889
- magnet
1890
- });
1891
- });
1892
-
1893
- candidates.forEach(candidate => {
1894
- const { magnet, bbox } = candidate;
1895
- // find distance from the center of the model to pointer coordinates
1896
- const distance = bbox.center().squaredDistance(pointer);
1897
- // the connection is looked up in a circle area by `distance < r`
1898
- if (distance < minDistance) {
1899
- const isAlreadyValidated = prevClosestMagnet === magnet;
1900
- if (isAlreadyValidated || paper.options.validateConnection.apply(
1901
- paper, data.validateConnectionArgs(view, (view.el === magnet) ? null : magnet)
1902
- )) {
1903
- minDistance = distance;
1904
- data.closestView = view;
1905
- data.closestMagnet = magnet;
1906
- }
1907
- }
1908
- });
1851
+ const isAlreadyValidated = prevClosestMagnet === magnet;
1852
+ return isAlreadyValidated || paper.options.validateConnection.apply(
1853
+ paper, data.validateConnectionArgs(view, (view.el === magnet) ? null : magnet)
1854
+ );
1855
+ };
1909
1856
 
1910
- }, this);
1857
+ const closest = paper.findClosestMagnetToPoint({ x, y }, { radius, findInAreaOptions, filter: isValidCandidate });
1858
+ data.closestView = closest ? closest.view : null;
1859
+ data.closestMagnet = closest ? closest.magnet : null;
1911
1860
 
1912
1861
  var end;
1913
1862
  var magnetProxy = null;
@@ -2225,8 +2174,82 @@ export const LinkView = CellView.extend({
2225
2174
  Flags: Flags,
2226
2175
  });
2227
2176
 
2228
- Object.defineProperty(LinkView.prototype, 'sourceBBox', {
2177
+ Object.defineProperty(LinkView.prototype, 'sourceView', {
2178
+ enumerable: true,
2179
+
2180
+ get: function() {
2181
+ const source = this.model.attributes.source;
2182
+ if (source.id && this.paper) {
2183
+ return this.paper.findViewByModel(source.id);
2184
+ }
2185
+ return null;
2186
+ }
2187
+
2188
+ });
2189
+
2190
+ Object.defineProperty(LinkView.prototype, 'targetView', {
2191
+ enumerable: true,
2192
+
2193
+ get: function() {
2194
+ const target = this.model.attributes.target;
2195
+ if (target.id && this.paper) {
2196
+ return this.paper.findViewByModel(target.id);
2197
+ }
2198
+ return null;
2199
+ }
2200
+ });
2201
+
2202
+ Object.defineProperty(LinkView.prototype, 'sourceMagnet', {
2203
+ enumerable: true,
2204
+
2205
+ get: function() {
2206
+ const sourceView = this.sourceView;
2207
+ if (!sourceView) return null;
2208
+ let sourceMagnet = null;
2209
+ // Check if the magnet is already found and cached.
2210
+ // We need to check if the cached magnet is still part of the source view.
2211
+ // The source view might have been disposed and recreated, or the magnet might have been changed.
2212
+ const cachedSourceMagnet = this._sourceMagnet;
2213
+ if (cachedSourceMagnet && sourceView.el.contains(cachedSourceMagnet)) {
2214
+ sourceMagnet = cachedSourceMagnet;
2215
+ } else {
2216
+ // If the cached magnet is not valid, we need to find the magnet.
2217
+ sourceMagnet = sourceView.getMagnetFromLinkEnd(this.model.attributes.source);
2218
+ }
2219
+ this._sourceMagnet = sourceMagnet;
2220
+ if (sourceMagnet === sourceView.el) {
2221
+ // If the source magnet is the element itself, we treat it as no magnet.
2222
+ return null;
2223
+ }
2224
+ return sourceMagnet;
2225
+ }
2226
+ });
2229
2227
 
2228
+ Object.defineProperty(LinkView.prototype, 'targetMagnet', {
2229
+ enumerable: true,
2230
+
2231
+ get: function() {
2232
+ const targetView = this.targetView;
2233
+ if (!targetView) return null;
2234
+ let targetMagnet = null;
2235
+ // Check if the magnet is already found and cached (See `sourceMagnet` for explanation).
2236
+ const cachedTargetMagnet = this._targetMagnet;
2237
+ if (cachedTargetMagnet && targetView.el.contains(cachedTargetMagnet)) {
2238
+ targetMagnet = cachedTargetMagnet;
2239
+ } else {
2240
+ // If the cached magnet is not valid, we need to find the magnet.
2241
+ targetMagnet = targetView.getMagnetFromLinkEnd(this.model.attributes.target);
2242
+ }
2243
+ this._targetMagnet = targetMagnet;
2244
+ if (targetMagnet === targetView.el) {
2245
+ // If the target magnet is the element itself, we treat it as no magnet.
2246
+ return null;
2247
+ }
2248
+ return targetMagnet;
2249
+ }
2250
+ });
2251
+
2252
+ Object.defineProperty(LinkView.prototype, 'sourceBBox', {
2230
2253
  enumerable: true,
2231
2254
 
2232
2255
  get: function() {
@@ -2241,11 +2264,9 @@ Object.defineProperty(LinkView.prototype, 'sourceBBox', {
2241
2264
  }
2242
2265
  return sourceView.getNodeBBox(sourceMagnet || sourceView.el);
2243
2266
  }
2244
-
2245
2267
  });
2246
2268
 
2247
2269
  Object.defineProperty(LinkView.prototype, 'targetBBox', {
2248
-
2249
2270
  enumerable: true,
2250
2271
 
2251
2272
  get: function() {
@@ -2261,4 +2282,3 @@ Object.defineProperty(LinkView.prototype, 'targetBBox', {
2261
2282
  return targetView.getNodeBBox(targetMagnet || targetView.el);
2262
2283
  }
2263
2284
  });
2264
-