@abi-software/map-utilities 1.1.3-beta.0 → 1.1.3-beta.10

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.
@@ -25,9 +25,9 @@ import cytoscape from 'cytoscape'
25
25
 
26
26
  //==============================================================================
27
27
 
28
- export class ConnectivityGraph
28
+ export class ConnectivityGraph extends EventTarget
29
29
  {
30
- cy = null
30
+ cyg = null
31
31
  nodes = []
32
32
  edges = []
33
33
  axons = []
@@ -37,8 +37,9 @@ export class ConnectivityGraph
37
37
 
38
38
  constructor(labelCache, graphCanvas)
39
39
  {
40
- this.labelCache = labelCache;
41
- this.graphCanvas = graphCanvas;
40
+ super()
41
+ this.labelCache = labelCache;
42
+ this.graphCanvas = graphCanvas;
42
43
  }
43
44
 
44
45
  async addConnectivity(knowledge)
@@ -69,15 +70,56 @@ export class ConnectivityGraph
69
70
  showConnectivity(graphCanvas)
70
71
  //================
71
72
  {
72
- this.cy = new CytoscapeGraph(this, graphCanvas)
73
+ this.cyg = new CytoscapeGraph(this, graphCanvas)
74
+
75
+ this.cyg.on('tap-node', (event) => {
76
+ const tapEvent = new CustomEvent('tap-node', {
77
+ detail: event.detail
78
+ })
79
+ this.dispatchEvent(tapEvent);
80
+ });
73
81
  }
74
82
 
75
83
  clearConnectivity()
76
84
  //=================
77
85
  {
78
- if (this.cy) {
79
- this.cy.remove()
80
- this.cy = null
86
+ if (this.cyg?.cy) {
87
+ this.cyg.cy.remove()
88
+ this.cyg.cy = null
89
+ }
90
+ }
91
+
92
+ reset()
93
+ //=====
94
+ {
95
+ if (this.cyg?.cy) {
96
+ this.cyg.cy.reset()
97
+ }
98
+ }
99
+
100
+ zoom(val)
101
+ //=======
102
+ {
103
+ if (this.cyg?.cy) {
104
+ const currentZoom = this.cyg.cy.zoom()
105
+ const width = this.cyg.cy.width()
106
+ const height = this.cyg.cy.height()
107
+ const positionToRender = {
108
+ x: width/2,
109
+ y: height/2,
110
+ }
111
+ this.cyg.cy.zoom({
112
+ level: currentZoom + val,
113
+ renderedPosition: positionToRender,
114
+ })
115
+ }
116
+ }
117
+
118
+ enableZoom(option)
119
+ //================
120
+ {
121
+ if (this.cyg?.cy) {
122
+ this.cyg.cy.userZoomingEnabled(option)
81
123
  }
82
124
  }
83
125
 
@@ -124,6 +166,12 @@ export class ConnectivityGraph
124
166
  }
125
167
  return result
126
168
  }
169
+
170
+ on(eventName, callback)
171
+ //=====================
172
+ {
173
+ this.addEventListener(eventName, callback)
174
+ }
127
175
  }
128
176
 
129
177
  //==============================================================================
@@ -132,54 +180,81 @@ const GRAPH_STYLE = [
132
180
  {
133
181
  'selector': 'node',
134
182
  'style': {
135
- 'label': 'data(label)',
136
- 'background-color': '#80F0F0',
183
+ 'label': function(ele) { return trimLabel(ele.data('label')) },
184
+ // 'background-color': '#80F0F0',
185
+ 'background-color': 'transparent',
186
+ 'background-opacity': '0',
137
187
  'text-valign': 'center',
138
188
  'text-wrap': 'wrap',
189
+ 'width': '80px',
190
+ 'height': '80px',
139
191
  'text-max-width': '80px',
140
- 'font-size': '6px'
192
+ 'font-size': '6px',
193
+ 'shape': 'round-rectangle',
194
+ 'border-width': 1,
195
+ 'border-style': 'solid',
196
+ 'border-color': 'gray',
141
197
  }
142
198
  },
143
199
  {
144
200
  'selector': 'node[axon]',
145
201
  'style': {
146
- 'background-color': 'green'
202
+ // 'background-color': 'green',
203
+ 'shape': 'round-diamond',
147
204
  }
148
205
  },
149
206
  {
150
207
  'selector': 'node[dendrite]',
151
208
  'style': {
152
- 'background-color': 'red'
209
+ // 'background-color': 'red',
210
+ 'shape': 'ellipse',
153
211
  }
154
212
  },
155
213
  {
156
214
  'selector': 'node[both-a-d]',
157
215
  'style': {
158
- 'background-color': 'gray'
216
+ // 'background-color': 'gray',
217
+ 'shape': 'round-rectangle',
159
218
  }
160
219
  },
161
220
  {
162
221
  'selector': 'edge',
163
222
  'style': {
164
- 'width': 2,
165
- 'line-color': '#9dbaea',
166
- 'target-arrow-color': '#9dbaea',
223
+ 'width': 1,
224
+ 'line-color': 'dimgray',
225
+ 'target-arrow-color': 'dimgray',
167
226
  'target-arrow-shape': 'triangle',
168
227
  'curve-style': 'bezier'
169
228
  }
170
229
  }
171
230
  ]
172
231
 
232
+ function trimLabel(label) {
233
+ const labels = label.split('\n')
234
+ const half = labels.length/2
235
+ const trimLabels = labels.slice(half)
236
+ return trimLabels.join('\n')
237
+ }
238
+
239
+ function capitalizeLabels(input) {
240
+ return input.split('\n').map(label => {
241
+ if (label && label[0] >= 'a' && label[0] <= 'z') {
242
+ return label.charAt(0).toUpperCase() + label.slice(1)
243
+ }
244
+ return label
245
+ }).join('\n')
246
+ }
247
+
173
248
  //==============================================================================
174
249
 
175
- class CytoscapeGraph
250
+ class CytoscapeGraph extends EventTarget
176
251
  {
177
252
  cy
178
253
  tooltip
179
254
 
180
255
  constructor(connectivityGraph, graphCanvas)
181
256
  {
182
- // const graphCanvas = document.getElementById('graph-canvas')
257
+ super()
183
258
  this.cy = cytoscape({
184
259
  container: graphCanvas,
185
260
  elements: connectivityGraph.elements,
@@ -189,15 +264,19 @@ class CytoscapeGraph
189
264
  roots: connectivityGraph.roots
190
265
  },
191
266
  directed: true,
192
- style: GRAPH_STYLE
267
+ style: GRAPH_STYLE,
268
+ minZoom: 0.5,
269
+ maxZoom: 10,
270
+ wheelSensitivity: 0.4,
193
271
  }).on('mouseover', 'node', this.overNode.bind(this))
194
272
  .on('mouseout', 'node', this.exitNode.bind(this))
195
273
  .on('position', 'node', this.moveNode.bind(this))
274
+ .on('tap', this.tapNode.bind(this))
196
275
 
197
276
  this.tooltip = document.createElement('div')
198
- this.tooltip.id = 'tooltip'
277
+ this.tooltip.className = 'cy-graph-tooltip'
199
278
  this.tooltip.hidden = true
200
- this.graphCanvas?.lastChild?.appendChild(this.tooltip)
279
+ graphCanvas?.lastChild?.appendChild(this.tooltip)
201
280
  }
202
281
 
203
282
  remove()
@@ -220,10 +299,14 @@ class CytoscapeGraph
220
299
  //==============
221
300
  {
222
301
  const node = event.target
223
- this.tooltip.innerText = node.data().label
302
+ const label = capitalizeLabels(node.data().label)
303
+
304
+ this.tooltip.innerText = label
224
305
  this.tooltip.style.left = `${event.renderedPosition.x}px`
225
306
  this.tooltip.style.top = `${event.renderedPosition.y}px`
307
+ this.tooltip.style.maxWidth = '240px'
226
308
  this.tooltip.hidden = false
309
+
227
310
  this.checkRightBoundary(event.renderedPosition.x)
228
311
  }
229
312
 
@@ -241,6 +324,23 @@ class CytoscapeGraph
241
324
  {
242
325
  this.tooltip.hidden = true
243
326
  }
327
+
328
+ tapNode(event)
329
+ //============
330
+ {
331
+ const node = event.target
332
+ const data = node.data()
333
+ const tapEvent = new CustomEvent('tap-node', {
334
+ detail: data
335
+ })
336
+ this.dispatchEvent(tapEvent);
337
+ }
338
+
339
+ on(eventName, callback)
340
+ //=====================
341
+ {
342
+ this.addEventListener(eventName, callback)
343
+ }
244
344
  }
245
345
 
246
346
  //==============================================================================
@@ -6,12 +6,21 @@
6
6
  {{ title }}
7
7
  </div>
8
8
  </el-col>
9
+ <el-col v-if="enableFilter" :span="12">
10
+ <div>
11
+ <el-input
12
+ class="tree-filter-input"
13
+ v-model="filterText"
14
+ placeholder="Filter keyword"
15
+ />
16
+ </div>
17
+ </el-col>
9
18
  </el-row>
10
19
  <div class="tree-container" ref="treeContainer">
11
20
  <div :class="['tree-tooltip', tooltipAtBottom ? 'bottom' : '']" >
12
21
  <el-popover
13
22
  ref="tooltip"
14
- :visible="tooltipVisible"
23
+ :visible="tooltipVisible && tooltipLabel !== ''"
15
24
  placement="top"
16
25
  :show-arrow="false"
17
26
  :teleported="false"
@@ -38,6 +47,7 @@
38
47
  :default-expanded-keys="expandedKeys"
39
48
  @check="checkChanged"
40
49
  :indent="8"
50
+ :filter-node-method="filterNode"
41
51
  :class="[mapType === 'flatmap' ? 'hide_grandchildren_checkbox': '']"
42
52
  >
43
53
  <template #default="{ node, data }">
@@ -138,10 +148,15 @@ export default {
138
148
  type: [String, Array],
139
149
  required: true,
140
150
  },
151
+ enableFilter: {
152
+ type: Boolean,
153
+ default: true,
154
+ }
141
155
  },
142
156
  data: function () {
143
157
  return {
144
158
  defaultExpandedKeys: ["All"],
159
+ filterText: "",
145
160
  myPopperClass: "hide-scaffold-colour-popup",
146
161
  tooltipVisible: false,
147
162
  tooltipLabel: "",
@@ -178,8 +193,17 @@ export default {
178
193
  else this.myPopperClass = "hide-scaffold-colour-popup";
179
194
  },
180
195
  },
196
+ filterText: {
197
+ handler: function (value) {
198
+ if (this.$refs.regionTree) this.$refs.regionTree.filter(value);
199
+ },
200
+ },
181
201
  },
182
202
  methods: {
203
+ filterNode: function(value, data) {
204
+ if (!value) return true;
205
+ return data.label ? data.label.toLowerCase().includes(value.toLowerCase()) : false;
206
+ },
183
207
  setColour: function (nodeData, value) {
184
208
  this.$emit("setColour", nodeData, value);
185
209
  },
@@ -250,6 +274,9 @@ export default {
250
274
  unmounted: function () {
251
275
  this.sortedPrimitiveGroups = undefined;
252
276
  },
277
+ mounted: function() {
278
+ if (this.$refs.regionTree) this.$refs.regionTree.filter(this.filterText);
279
+ }
253
280
  };
254
281
  </script>
255
282
 
@@ -265,6 +292,7 @@ export default {
265
292
  }
266
293
 
267
294
  .selections-container {
295
+ width: 260px;
268
296
  padding-top: 5px;
269
297
  }
270
298
 
@@ -278,6 +306,15 @@ export default {
278
306
  margin-left: 8px;
279
307
  }
280
308
 
309
+ :deep(.tree-filter-input) {
310
+ .el-input__inner {
311
+ height: 20px;
312
+ }
313
+ .el-input__wrapper.is-focus{
314
+ box-shadow: 0 0 0 1px $app-primary-color;
315
+ }
316
+ }
317
+
281
318
  .tree-container {
282
319
  width: 260px;
283
320
  border: 1px solid rgb(144, 147, 153);
@@ -18,6 +18,7 @@ declare module 'vue' {
18
18
  ElCol: typeof import('element-plus/es')['ElCol']
19
19
  ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
20
20
  ElIcon: typeof import('element-plus/es')['ElIcon']
21
+ ElIconAim: typeof import('@element-plus/icons-vue')['Aim']
21
22
  ElIconArrowDown: typeof import('@element-plus/icons-vue')['ArrowDown']
22
23
  ElIconArrowUp: typeof import('@element-plus/icons-vue')['ArrowUp']
23
24
  ElIconClose: typeof import('@element-plus/icons-vue')['Close']
@@ -25,7 +26,11 @@ declare module 'vue' {
25
26
  ElIconDelete: typeof import('@element-plus/icons-vue')['Delete']
26
27
  ElIconEdit: typeof import('@element-plus/icons-vue')['Edit']
27
28
  ElIconFinished: typeof import('@element-plus/icons-vue')['Finished']
29
+ ElIconLock: typeof import('@element-plus/icons-vue')['Lock']
30
+ ElIconUnlock: typeof import('@element-plus/icons-vue')['Unlock']
28
31
  ElIconWarning: typeof import('@element-plus/icons-vue')['Warning']
32
+ ElIconZoomIn: typeof import('@element-plus/icons-vue')['ZoomIn']
33
+ ElIconZoomOut: typeof import('@element-plus/icons-vue')['ZoomOut']
29
34
  ElInput: typeof import('element-plus/es')['ElInput']
30
35
  ElMain: typeof import('element-plus/es')['ElMain']
31
36
  ElOption: typeof import('element-plus/es')['ElOption']