@abi-software/map-utilities 1.1.3-beta.1 → 1.1.3-beta.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abi-software/map-utilities",
3
- "version": "1.1.3-beta.1",
3
+ "version": "1.1.3-beta.3",
4
4
  "files": [
5
5
  "dist/*",
6
6
  "src/*",
@@ -1,13 +1,56 @@
1
1
  <template>
2
- <div class="connectivity-graph">
2
+ <div class="connectivity-graph" v-loading="loading">
3
3
  <div ref="graphCanvas" class="graph-canvas"></div>
4
- <div class="node-key">
5
- <div class="key-head">Node type:</div>
6
- <div>
7
- <div><span>Node:</span><span class="key-box" style="background: #80F0F0"/></div>
8
- <div><span>Axon:</span><span class="key-box" style="background: green"/></div>
9
- <div><span>Dendrite:</span><span class="key-box" style="background: red"/></div>
10
- <div><span>Both:</span><span class="key-box" style="background: gray"/></div>
4
+ <div class="control-panel">
5
+ <div class="node-key">
6
+ <div class="key-head">Node type:</div>
7
+ <div>
8
+ <div><span>Node:</span><span class="key-box" style="background: #80F0F0"/></div>
9
+ <div><span>Axon:</span><span class="key-box" style="background: green"/></div>
10
+ <div><span>Dendrite:</span><span class="key-box" style="background: red"/></div>
11
+ <div><span>Both:</span><span class="key-box" style="background: gray"/></div>
12
+ </div>
13
+ </div>
14
+ <div class="tools">
15
+ <el-tooltip
16
+ :content="resetLabel"
17
+ placement="bottom"
18
+ effect="control-tooltip"
19
+ >
20
+ <el-button
21
+ class="control-button"
22
+ :class="theme"
23
+ size="small"
24
+ @click="reset"
25
+ >
26
+ <el-icon color="white">
27
+ <el-icon-aim />
28
+ </el-icon>
29
+ <span class="visually-hidden">{{ resetLabel }}</span>
30
+ </el-button>
31
+ </el-tooltip>
32
+ <el-tooltip
33
+ :content="zoomLockLabel"
34
+ placement="bottom"
35
+ effect="control-tooltip"
36
+ >
37
+ <el-button
38
+ class="control-button"
39
+ :class="theme"
40
+ size="small"
41
+ @click="toggleZoom"
42
+ >
43
+ <el-icon color="white">
44
+ <template v-if="zoomEnabled">
45
+ <el-icon-lock />
46
+ </template>
47
+ <template v-else>
48
+ <el-icon-unlock />
49
+ </template>
50
+ </el-icon>
51
+ <span class="visually-hidden">{{ zoomLockLabel }}</span>
52
+ </el-button>
53
+ </el-tooltip>
11
54
  </div>
12
55
  </div>
13
56
  </div>
@@ -17,6 +60,10 @@
17
60
  import { ConnectivityGraph } from './graph';
18
61
 
19
62
  const MIN_SCHEMA_VERSION = 1.3;
63
+ const RESET_LABEL = 'Reset position';
64
+ const ZOOM_LOCK_LABEL = 'Lock zoom (to scroll)';
65
+ const ZOOM_UNLOCK_LABEL = 'Unlock zoom';
66
+ const APP_PRIMARY_COLOR = '#8300bf';
20
67
 
21
68
  export default {
22
69
  name: 'ConnectivityGraph',
@@ -35,28 +82,62 @@ export default {
35
82
  },
36
83
  data: function () {
37
84
  return {
38
- cy: null,
85
+ loading: true,
39
86
  connectivityGraph: null,
87
+ selectedSource: '',
88
+ pathList: [],
89
+ schemaVersion: '',
40
90
  knowledgeByPath: new Map(),
41
91
  labelledTerms: new Set(),
42
92
  labelCache: new Map(),
93
+ resetLabel: RESET_LABEL,
94
+ zoomLockLabel: ZOOM_LOCK_LABEL,
95
+ iconColor: APP_PRIMARY_COLOR,
96
+ zoomEnabled: false,
43
97
  };
44
98
  },
45
99
  mounted() {
100
+ this.loadCacheData();
46
101
  this.run().then((res) => {
47
102
  this.showGraph(this.entry);
48
103
  });
49
104
  },
50
105
  methods: {
106
+ loadCacheData: function () {
107
+ const selectedSource = sessionStorage.getItem('connectivity-graph-source');
108
+ const labelCache = sessionStorage.getItem('connectivity-graph-labels');
109
+ const pathList = sessionStorage.getItem('connectivity-graph-pathlist');
110
+ const schemaVersion = sessionStorage.getItem('connectivity-graph-schema-version');
111
+
112
+ if (selectedSource) {
113
+ this.selectedSource = selectedSource;
114
+ }
115
+ if (pathList) {
116
+ this.pathList = JSON.parse(pathList);
117
+ }
118
+ if (labelCache) {
119
+ const labelCacheObj = JSON.parse(labelCache);
120
+ this.labelCache = new Map(Object.entries(labelCacheObj));
121
+ }
122
+ if (schemaVersion) {
123
+ this.schemaVersion = schemaVersion;
124
+ }
125
+ },
51
126
  run: async function () {
52
- const schemaVersion = await this.getSchemaVersion();
53
- if (schemaVersion < MIN_SCHEMA_VERSION) {
127
+ if (!this.schemaVersion) {
128
+ this.schemaVersion = await this.getSchemaVersion();
129
+ sessionStorage.setItem('connectivity-graph-schema-version', this.schemaVersion);
130
+ }
131
+ if (this.schemaVersion < MIN_SCHEMA_VERSION) {
54
132
  console.warn('No Server!');
55
133
  return;
56
134
  }
57
135
  this.showSpinner();
58
- const selectedSource = await this.setSourceList();
59
- await this.setPathList(selectedSource)
136
+ if (!this.selectedSource) {
137
+ this.selectedSource = await this.setSourceList();
138
+ sessionStorage.setItem('connectivity-graph-source', this.selectedSource);
139
+ }
140
+ await this.setPathList(this.selectedSource)
60
141
  this.hideSpinner();
61
142
  },
62
143
  showGraph: async function (neuronPath) {
@@ -108,28 +189,33 @@ export default {
108
189
  }
109
190
  return firstSource;
110
191
  },
111
- setPathList: async function (source) {
192
+ loadPathData: async function (source) {
112
193
  const data = await this.query(
113
194
  `select entity, knowledge from knowledge
114
195
  where entity like 'ilxtr:%' and source=?
115
196
  order by entity`,
116
197
  [source]);
117
- const pathList = [];
198
+ const pathList = data ? data.values : [];
199
+ return pathList;
200
+ },
201
+ setPathList: async function (source) {
202
+ if (!this.pathList.length) {
203
+ this.pathList = await this.loadPathData(source);
204
+ sessionStorage.setItem('connectivity-graph-pathlist', JSON.stringify(this.pathList));
205
+ }
118
206
  this.knowledgeByPath.clear();
119
207
  this.labelledTerms = new Set();
120
- for (const [key, jsonKnowledge] of data.values) {
208
+ for (const [key, jsonKnowledge] of this.pathList) {
121
209
  const knowledge = JSON.parse(jsonKnowledge);
122
210
  if ('connectivity' in knowledge) {
123
- const label = knowledge.label || key;
124
- const shortLabel = (label === key.slice(6).replace('-prime', "'").replaceAll('-', ' '))
125
- ? ''
126
- : (label.length < 50) ? label : `${label.slice(0, 50)}...`;
127
- pathList.push(key);
128
211
  this.knowledgeByPath.set(key, knowledge);
129
212
  this.cacheLabels(knowledge);
130
213
  }
131
214
  }
132
- await this.getCachedTermLabels();
215
+
216
+ if (!this.labelCache.size) {
217
+ await this.getCachedTermLabels();
218
+ }
133
219
  return '';
134
220
  },
135
221
  getSchemaVersion: async function () {
@@ -156,14 +242,16 @@ export default {
156
242
  },
157
243
  getCachedTermLabels: async function () {
158
244
  if (this.labelledTerms.size) {
159
- const termLabels = await this.query(`
245
+ const data = await this.query(`
160
246
  select entity, label from labels
161
247
  where entity in (?${', ?'.repeat(this.labelledTerms.size-1)})`,
162
248
  [...this.labelledTerms.values()]
163
249
  );
164
- for (const termLabel of termLabels.values) {
250
+ for (const termLabel of data.values) {
165
251
  this.labelCache.set(termLabel[0], termLabel[1]);
166
252
  }
253
+ const labelCacheObj = Object.fromEntries(this.labelCache);
254
+ sessionStorage.setItem('connectivity-graph-labels', JSON.stringify(labelCacheObj));
167
255
  }
168
256
  },
169
257
  cacheNodeLabels: function (node) {
@@ -178,10 +266,21 @@ export default {
178
266
  }
179
267
  },
180
268
  showSpinner: function () {
181
- // show loading spinner
269
+ this.loading = true;
182
270
  },
183
271
  hideSpinner: function () {
184
- // hide loading spinner
272
+ this.loading = false;
273
+ },
274
+ reset: function () {
275
+ this.connectivityGraph.reset();
276
+ },
277
+ /**
278
+ * Enable/disable user zoom for scrolling
279
+ */
280
+ toggleZoom: function () {
281
+ this.zoomEnabled = !this.zoomEnabled;
282
+ this.zoomLockLabel = this.zoomEnabled ? ZOOM_UNLOCK_LABEL : ZOOM_LOCK_LABEL;
283
+ this.connectivityGraph.enableZoom(!this.zoomEnabled);
185
284
  },
186
285
  },
187
286
  };
@@ -201,10 +300,13 @@ export default {
201
300
  border: solid 1px #e4e7ed;
202
301
  }
203
302
 
204
- .node-key {
303
+ .control-panel {
205
304
  position: absolute;
206
305
  top: 1rem;
207
306
  right: 1rem;
307
+ }
308
+
309
+ .node-key {
208
310
  border: 1px solid $app-primary-color;
209
311
  padding: 4px;
210
312
  background-color: rgba(240, 240, 240, 0.8);
@@ -227,4 +329,61 @@ export default {
227
329
  width: 12px;
228
330
  height: 12px;
229
331
  }
332
+
333
+ .tools {
334
+ margin-top: 0.5rem;
335
+ display: flex;
336
+ flex-direction: row;
337
+ gap: 0.5rem;
338
+ align-items: flex-end;
339
+ justify-content: flex-end;
340
+ }
341
+
342
+ .control-button {
343
+ margin: 0 !important;
344
+ padding: 0.25rem !important;
345
+ font-size: 14px !important;
346
+ border-color: $app-primary-color !important;
347
+ background: $app-primary-color !important;
348
+ transition: all 0.25s ease;
349
+
350
+ svg {
351
+ margin: 0;
352
+ }
353
+
354
+ &,
355
+ &:focus,
356
+ &:active {
357
+ box-shadow: none !important;
358
+ }
359
+
360
+ &:hover {
361
+ background: $lightPurple !important;
362
+ }
363
+ }
364
+
365
+ .visually-hidden {
366
+ clip: rect(0 0 0 0);
367
+ clip-path: inset(50%);
368
+ height: 1px;
369
+ overflow: hidden;
370
+ position: absolute;
371
+ white-space: nowrap;
372
+ width: 1px;
373
+ }
374
+ </style>
375
+
376
+ <style lang="scss">
377
+ .el-popper.is-control-tooltip {
378
+ padding: 4px 10px;
379
+ font-family: Asap;
380
+ background: #f3ecf6 !important;
381
+ border: 1px solid $app-primary-color;
382
+
383
+ & .el-popper__arrow::before {
384
+ border: 1px solid;
385
+ border-color: $app-primary-color;
386
+ background: #f3ecf6;
387
+ }
388
+ }
230
389
  </style>
@@ -27,7 +27,7 @@ import cytoscape from 'cytoscape'
27
27
 
28
28
  export class ConnectivityGraph
29
29
  {
30
- cy = null
30
+ cyg = null
31
31
  nodes = []
32
32
  edges = []
33
33
  axons = []
@@ -69,15 +69,31 @@ export class ConnectivityGraph
69
69
  showConnectivity(graphCanvas)
70
70
  //================
71
71
  {
72
- this.cy = new CytoscapeGraph(this, graphCanvas)
72
+ this.cyg = new CytoscapeGraph(this, graphCanvas)
73
73
  }
74
74
 
75
75
  clearConnectivity()
76
76
  //=================
77
77
  {
78
- if (this.cy) {
79
- this.cy.remove()
80
- this.cy = null
78
+ if (this.cyg?.cy) {
79
+ this.cyg.cy.remove()
80
+ this.cyg.cy = null
81
+ }
82
+ }
83
+
84
+ reset()
85
+ //=================
86
+ {
87
+ if (this.cyg?.cy) {
88
+ this.cyg.cy.reset()
89
+ }
90
+ }
91
+
92
+ enableZoom(option)
93
+ //=================
94
+ {
95
+ if (this.cyg?.cy) {
96
+ this.cyg.cy.userZoomingEnabled(option)
81
97
  }
82
98
  }
83
99
 
@@ -179,7 +195,6 @@ class CytoscapeGraph
179
195
 
180
196
  constructor(connectivityGraph, graphCanvas)
181
197
  {
182
- // const graphCanvas = document.getElementById('graph-canvas')
183
198
  this.cy = cytoscape({
184
199
  container: graphCanvas,
185
200
  elements: connectivityGraph.elements,
@@ -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,6 +26,8 @@ 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']
29
32
  ElInput: typeof import('element-plus/es')['ElInput']
30
33
  ElMain: typeof import('element-plus/es')['ElMain']