pdbe-molstar 3.3.0 → 3.3.2
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/README.md +2 -0
- package/build/pdbe-molstar-component.js +2 -2
- package/build/pdbe-molstar-light.css +2 -2
- package/build/pdbe-molstar-plugin.js +2 -2
- package/build/pdbe-molstar-plugin.js.LICENSE.txt +1 -1
- package/build/pdbe-molstar.css +2 -2
- package/lib/domain-annotations/prop.d.ts +1 -8
- package/lib/domain-annotations/prop.js +7 -1
- package/lib/extensions/state-gallery/behavior.d.ts +5 -2
- package/lib/extensions/state-gallery/behavior.js +4 -3
- package/lib/extensions/state-gallery/config.d.ts +4 -0
- package/lib/extensions/state-gallery/config.js +3 -0
- package/lib/extensions/state-gallery/manager.d.ts +34 -5
- package/lib/extensions/state-gallery/manager.js +54 -37
- package/lib/extensions/state-gallery/titles.d.ts +26 -0
- package/lib/extensions/state-gallery/titles.js +68 -0
- package/lib/extensions/state-gallery/ui.d.ts +11 -3
- package/lib/extensions/state-gallery/ui.js +31 -23
- package/lib/helpers.d.ts +5 -4
- package/lib/helpers.js +2 -0
- package/lib/plugin-custom-state.d.ts +11 -3
- package/lib/plugin-custom-state.js +23 -20
- package/lib/spec.d.ts +3 -1
- package/lib/styles/pdbe-molstar/_index.scss +9 -2
- package/lib/viewer.d.ts +6 -8
- package/lib/viewer.js +39 -14
- package/package.json +7 -7
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.StateGalleryManager = void 0;
|
|
3
|
+
exports.StateGalleryManager = exports.ImageCategory = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const camera_1 = require("molstar/lib/mol-canvas3d/camera");
|
|
6
6
|
const linear_algebra_1 = require("molstar/lib/mol-math/linear-algebra");
|
|
@@ -11,13 +11,24 @@ const rxjs_1 = require("rxjs");
|
|
|
11
11
|
const helpers_1 = require("../../helpers");
|
|
12
12
|
const behavior_1 = require("./behavior");
|
|
13
13
|
const config_1 = require("./config");
|
|
14
|
-
const
|
|
14
|
+
const titles_1 = require("./titles");
|
|
15
|
+
/** Categories of images/states */
|
|
16
|
+
exports.ImageCategory = ['Entry', 'Assemblies', 'Entities', 'Ligands', 'Modified residues', 'Domains', 'Miscellaneous'];
|
|
17
|
+
/** Provides functionality to get list of images (3D states) for an entry, load individual images, keeps track of the currently loaded image.
|
|
18
|
+
* Use async `StateGalleryManager.create()` to create an instance. */
|
|
15
19
|
class StateGalleryManager {
|
|
16
|
-
constructor(plugin,
|
|
20
|
+
constructor(plugin,
|
|
21
|
+
/** Entry identifier, i.e. '1cbs' */
|
|
22
|
+
entryId,
|
|
23
|
+
/** Data retrieved from API */
|
|
24
|
+
data,
|
|
25
|
+
/** Config values */
|
|
26
|
+
options) {
|
|
17
27
|
this.plugin = plugin;
|
|
18
28
|
this.entryId = entryId;
|
|
19
29
|
this.data = data;
|
|
20
30
|
this.options = options;
|
|
31
|
+
/** BehaviorSubjects for current state of the manager */
|
|
21
32
|
this.events = {
|
|
22
33
|
/** Image that has been requested to load most recently. */
|
|
23
34
|
requestedImage: new rxjs_1.BehaviorSubject(undefined),
|
|
@@ -29,6 +40,7 @@ class StateGalleryManager {
|
|
|
29
40
|
/** True if at least one image has been loaded (this is to skip animation on the first load) */
|
|
30
41
|
this.firstLoaded = false;
|
|
31
42
|
this.loader = new helpers_1.PreemptiveQueue((filename) => this._load(filename));
|
|
43
|
+
/** Cache for MOLJ states from API */
|
|
32
44
|
this.cache = {};
|
|
33
45
|
const allImages = listImages(data, true);
|
|
34
46
|
this.images = removeWithSuffixes(allImages, ['_side', '_top']); // removing images in different orientation than 'front'
|
|
@@ -41,13 +53,15 @@ class StateGalleryManager {
|
|
|
41
53
|
(_c = customState.manager) === null || _c === void 0 ? void 0 : _c.next(this);
|
|
42
54
|
});
|
|
43
55
|
this.events.requestedImage.subscribe(img => {
|
|
44
|
-
var _a, _b, _c
|
|
56
|
+
var _a, _b, _c;
|
|
45
57
|
const customState = (0, behavior_1.StateGalleryCustomState)(this.plugin);
|
|
46
|
-
(_a = customState.
|
|
47
|
-
if (((
|
|
48
|
-
(
|
|
58
|
+
(_a = customState.requestedImage) === null || _a === void 0 ? void 0 : _a.next(img);
|
|
59
|
+
if (((_b = customState.manager) === null || _b === void 0 ? void 0 : _b.value) !== this)
|
|
60
|
+
(_c = customState.manager) === null || _c === void 0 ? void 0 : _c.next(this);
|
|
49
61
|
});
|
|
50
62
|
}
|
|
63
|
+
/** Create an instance of `StateGalleryManager` and retrieve list of images from API.
|
|
64
|
+
* Options that are not provided will use values from plugin config. */
|
|
51
65
|
static create(plugin, entryId, options) {
|
|
52
66
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
53
67
|
const fullOptions = Object.assign(Object.assign({}, (0, config_1.getStateGalleryConfig)(plugin)), options);
|
|
@@ -58,6 +72,7 @@ class StateGalleryManager {
|
|
|
58
72
|
return new this(plugin, entryId, data, fullOptions);
|
|
59
73
|
});
|
|
60
74
|
}
|
|
75
|
+
/** Load an image (3D state). Do not call directly; use `load` instead, which handles concurrent requests. */
|
|
61
76
|
_load(filename) {
|
|
62
77
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
63
78
|
if (!this.plugin.canvas3d)
|
|
@@ -82,6 +97,7 @@ class StateGalleryManager {
|
|
|
82
97
|
this.firstLoaded = true;
|
|
83
98
|
});
|
|
84
99
|
}
|
|
100
|
+
/** Request to load an image (3D state). When there are multiple concurrent requests, some requests may be skipped (will resolve to `{ status: 'cancelled' }` or `{ status: 'skipped' }`) as only the last request is really important. */
|
|
85
101
|
load(img) {
|
|
86
102
|
var _a;
|
|
87
103
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
@@ -108,6 +124,7 @@ class StateGalleryManager {
|
|
|
108
124
|
}
|
|
109
125
|
});
|
|
110
126
|
}
|
|
127
|
+
/** Move to next/previous image in the list. */
|
|
111
128
|
shift(shift) {
|
|
112
129
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
113
130
|
const current = this.events.requestedImage.value;
|
|
@@ -117,16 +134,19 @@ class StateGalleryManager {
|
|
|
117
134
|
return yield this.load(this.images[iNew]);
|
|
118
135
|
});
|
|
119
136
|
}
|
|
137
|
+
/** Request to load the previous image in the list */
|
|
120
138
|
loadPrevious() {
|
|
121
139
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
122
140
|
return yield this.shift(-1);
|
|
123
141
|
});
|
|
124
142
|
}
|
|
143
|
+
/** Request to load the next image in the list */
|
|
125
144
|
loadNext() {
|
|
126
145
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
127
146
|
return yield this.shift(1);
|
|
128
147
|
});
|
|
129
148
|
}
|
|
149
|
+
/** Fetch a MOLJ state from API */
|
|
130
150
|
fetchSnapshot(filename) {
|
|
131
151
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
132
152
|
const url = (0, helpers_1.combineUrl)(this.options.ServerUrl, `${filename}.molj`);
|
|
@@ -134,6 +154,7 @@ class StateGalleryManager {
|
|
|
134
154
|
return data;
|
|
135
155
|
});
|
|
136
156
|
}
|
|
157
|
+
/** Get MOLJ state for the image (get from cache or fetch from API) */
|
|
137
158
|
getSnapshot(filename) {
|
|
138
159
|
var _a;
|
|
139
160
|
var _b;
|
|
@@ -141,6 +162,7 @@ class StateGalleryManager {
|
|
|
141
162
|
return (_a = (_b = this.cache)[filename]) !== null && _a !== void 0 ? _a : (_b[filename] = yield this.fetchSnapshot(filename));
|
|
142
163
|
});
|
|
143
164
|
}
|
|
165
|
+
/** Get full image information based on filename. Return `undefined` if image with given filename is not in the list. */
|
|
144
166
|
getImageByFilename(filename) {
|
|
145
167
|
const index = this.filenameIndex.get(filename);
|
|
146
168
|
if (index === undefined)
|
|
@@ -149,6 +171,7 @@ class StateGalleryManager {
|
|
|
149
171
|
}
|
|
150
172
|
}
|
|
151
173
|
exports.StateGalleryManager = StateGalleryManager;
|
|
174
|
+
/** Get the list of images, captions etc. for an entry from API */
|
|
152
175
|
function getData(plugin, serverUrl, entryId) {
|
|
153
176
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
154
177
|
const url = (0, helpers_1.combineUrl)(serverUrl, entryId + '.json');
|
|
@@ -168,62 +191,52 @@ function listImages(data, byCategory = false) {
|
|
|
168
191
|
const out = [];
|
|
169
192
|
// Entry
|
|
170
193
|
for (const img of (_c = (_b = (_a = data === null || data === void 0 ? void 0 : data.entry) === null || _a === void 0 ? void 0 : _a.all) === null || _b === void 0 ? void 0 : _b.image) !== null && _c !== void 0 ? _c : []) {
|
|
171
|
-
|
|
172
|
-
? 'Deposited model (color by entity)'
|
|
173
|
-
: img.filename.includes('_chain')
|
|
174
|
-
? 'Deposited model (color by chain)'
|
|
175
|
-
: undefined;
|
|
176
|
-
out.push(Object.assign(Object.assign({}, img), { category: 'Entry', simple_title: title }));
|
|
194
|
+
out.push(Object.assign(Object.assign(Object.assign({}, img), { category: 'Entry' }), titles_1.ImageTitles.entry(img)));
|
|
177
195
|
}
|
|
178
196
|
// Validation
|
|
179
197
|
for (const img of (_g = (_f = (_e = (_d = data === null || data === void 0 ? void 0 : data.validation) === null || _d === void 0 ? void 0 : _d.geometry) === null || _e === void 0 ? void 0 : _e.deposited) === null || _f === void 0 ? void 0 : _f.image) !== null && _g !== void 0 ? _g : []) {
|
|
180
|
-
out.push(Object.assign(Object.assign({}, img), { category: 'Entry',
|
|
198
|
+
out.push(Object.assign(Object.assign(Object.assign({}, img), { category: 'Entry' }), titles_1.ImageTitles.validation(img)));
|
|
181
199
|
}
|
|
182
200
|
// Bfactor
|
|
183
201
|
for (const img of (_k = (_j = (_h = data === null || data === void 0 ? void 0 : data.entry) === null || _h === void 0 ? void 0 : _h.bfactor) === null || _j === void 0 ? void 0 : _j.image) !== null && _k !== void 0 ? _k : []) {
|
|
184
|
-
out.push(Object.assign(Object.assign({}, img), { category: 'Entry'
|
|
202
|
+
out.push(Object.assign(Object.assign(Object.assign({}, img), { category: 'Entry' }), titles_1.ImageTitles.bfactor(img)));
|
|
185
203
|
}
|
|
186
204
|
// Assembly
|
|
187
205
|
const assemblies = data === null || data === void 0 ? void 0 : data.assembly;
|
|
188
|
-
for (const
|
|
189
|
-
for (const img of (_l = assemblies[
|
|
190
|
-
|
|
191
|
-
? `Assembly ${ass} (color by entity)`
|
|
192
|
-
: img.filename.includes('_chain')
|
|
193
|
-
? `Assembly ${ass} (color by chain)`
|
|
194
|
-
: undefined;
|
|
195
|
-
out.push(Object.assign(Object.assign({}, img), { category: 'Assemblies', simple_title: title }));
|
|
206
|
+
for (const assemblyId in assemblies) {
|
|
207
|
+
for (const img of (_l = assemblies[assemblyId].image) !== null && _l !== void 0 ? _l : []) {
|
|
208
|
+
out.push(Object.assign(Object.assign(Object.assign({}, img), { category: 'Assemblies' }), titles_1.ImageTitles.assembly(img, { assemblyId })));
|
|
196
209
|
}
|
|
197
210
|
}
|
|
198
211
|
// Entity
|
|
199
212
|
const entities = data === null || data === void 0 ? void 0 : data.entity;
|
|
200
|
-
for (const
|
|
201
|
-
for (const img of (_m = entities[
|
|
202
|
-
out.push(Object.assign(Object.assign({}, img), { category: 'Entities',
|
|
213
|
+
for (const entityId in entities) {
|
|
214
|
+
for (const img of (_m = entities[entityId].image) !== null && _m !== void 0 ? _m : []) {
|
|
215
|
+
out.push(Object.assign(Object.assign(Object.assign({}, img), { category: 'Entities' }), titles_1.ImageTitles.entity(img, { entityId })));
|
|
203
216
|
}
|
|
204
217
|
}
|
|
205
218
|
// Ligand
|
|
206
219
|
const ligands = (_o = data === null || data === void 0 ? void 0 : data.entry) === null || _o === void 0 ? void 0 : _o.ligands;
|
|
207
|
-
for (const
|
|
208
|
-
for (const img of (_p = ligands[
|
|
209
|
-
out.push(Object.assign(Object.assign({}, img), { category: 'Ligands',
|
|
220
|
+
for (const compId in ligands) {
|
|
221
|
+
for (const img of (_p = ligands[compId].image) !== null && _p !== void 0 ? _p : []) {
|
|
222
|
+
out.push(Object.assign(Object.assign(Object.assign({}, img), { category: 'Ligands' }), titles_1.ImageTitles.ligand(img, { compId })));
|
|
210
223
|
}
|
|
211
224
|
}
|
|
212
225
|
// Modres
|
|
213
226
|
const modres = (_q = data === null || data === void 0 ? void 0 : data.entry) === null || _q === void 0 ? void 0 : _q.mod_res;
|
|
214
|
-
for (const
|
|
215
|
-
for (const img of (_r = modres[
|
|
216
|
-
out.push(Object.assign(Object.assign({}, img), { category: 'Modified residues',
|
|
227
|
+
for (const compId in modres) {
|
|
228
|
+
for (const img of (_r = modres[compId].image) !== null && _r !== void 0 ? _r : []) {
|
|
229
|
+
out.push(Object.assign(Object.assign(Object.assign({}, img), { category: 'Modified residues' }), titles_1.ImageTitles.modres(img, { compId })));
|
|
217
230
|
}
|
|
218
231
|
}
|
|
219
232
|
// Domain
|
|
220
|
-
for (const
|
|
221
|
-
const dbs = entities[
|
|
233
|
+
for (const entityId in entities) {
|
|
234
|
+
const dbs = entities[entityId].database;
|
|
222
235
|
for (const db in dbs) {
|
|
223
236
|
const domains = dbs[db];
|
|
224
|
-
for (const
|
|
225
|
-
for (const img of (_s = domains[
|
|
226
|
-
out.push(Object.assign(Object.assign({}, img), { category: 'Domains',
|
|
237
|
+
for (const familyId in domains) {
|
|
238
|
+
for (const img of (_s = domains[familyId].image) !== null && _s !== void 0 ? _s : []) {
|
|
239
|
+
out.push(Object.assign(Object.assign(Object.assign({}, img), { category: 'Domains' }), titles_1.ImageTitles.domain(img, { db, familyId, entityId })));
|
|
227
240
|
}
|
|
228
241
|
}
|
|
229
242
|
}
|
|
@@ -250,6 +263,7 @@ function pushImages(out, data) {
|
|
|
250
263
|
}
|
|
251
264
|
return out;
|
|
252
265
|
}
|
|
266
|
+
/** Return a filtered list of images, removing all images with filename ending in one of `suffixes` */
|
|
253
267
|
function removeWithSuffixes(images, suffixes) {
|
|
254
268
|
return images.filter(img => !suffixes.some(suffix => img.filename.endsWith(suffix)));
|
|
255
269
|
}
|
|
@@ -278,12 +292,15 @@ function getCameraFromSnapshot(snapshot) {
|
|
|
278
292
|
const json = JSON.parse(snapshot);
|
|
279
293
|
return (_d = (_c = (_b = (_a = json === null || json === void 0 ? void 0 : json.entries) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.snapshot) === null || _c === void 0 ? void 0 : _c.camera) === null || _d === void 0 ? void 0 : _d.current;
|
|
280
294
|
}
|
|
295
|
+
/** Recalculate camera distance from target in `snapshot` based on `snapshot.radius`,
|
|
296
|
+
* keeping target, direction, and up from snapshot but using camera mode and FOV from `camera`. */
|
|
281
297
|
function refocusCameraSnapshot(camera, snapshot) {
|
|
282
298
|
if (snapshot === undefined)
|
|
283
299
|
return undefined;
|
|
284
300
|
const dir = linear_algebra_1.Vec3.sub((0, linear_algebra_1.Vec3)(), snapshot.target, snapshot.position);
|
|
285
301
|
return camera.getInvariantFocus(snapshot.target, snapshot.radius, snapshot.up, dir);
|
|
286
302
|
}
|
|
303
|
+
/** Get current camera positioning */
|
|
287
304
|
function getCurrentCamera(plugin) {
|
|
288
305
|
if (!plugin.canvas3d)
|
|
289
306
|
return camera_1.Camera.createDefaultSnapshot();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Image } from './manager';
|
|
2
|
+
type Titles = Pick<Image, 'title' | 'subtitle'>;
|
|
3
|
+
/** Functions for creating informative image (3D state) titles for display in UI */
|
|
4
|
+
export declare const ImageTitles: {
|
|
5
|
+
entry(img: Image): Titles;
|
|
6
|
+
validation(img: Image): Titles;
|
|
7
|
+
bfactor(img: Image): Titles;
|
|
8
|
+
assembly(img: Image, info: {
|
|
9
|
+
assemblyId: string;
|
|
10
|
+
}): Titles;
|
|
11
|
+
entity(img: Image, info: {
|
|
12
|
+
entityId: string;
|
|
13
|
+
}): Titles;
|
|
14
|
+
ligand(img: Image, info: {
|
|
15
|
+
compId: string;
|
|
16
|
+
}): Titles;
|
|
17
|
+
modres(img: Image, info: {
|
|
18
|
+
compId: string;
|
|
19
|
+
}): Titles;
|
|
20
|
+
domain(img: Image, info: {
|
|
21
|
+
db: string;
|
|
22
|
+
familyId: string;
|
|
23
|
+
entityId: string;
|
|
24
|
+
}): Titles;
|
|
25
|
+
};
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ImageTitles = void 0;
|
|
4
|
+
/** Functions for creating informative image (3D state) titles for display in UI */
|
|
5
|
+
exports.ImageTitles = {
|
|
6
|
+
entry(img) {
|
|
7
|
+
if (img.filename.includes('_chemically_distinct_molecules')) {
|
|
8
|
+
return { title: 'Deposited model (color by entity)' };
|
|
9
|
+
}
|
|
10
|
+
if (img.filename.includes('_chain')) {
|
|
11
|
+
return { title: 'Deposited model (color by chain)' };
|
|
12
|
+
}
|
|
13
|
+
return {};
|
|
14
|
+
},
|
|
15
|
+
validation(img) {
|
|
16
|
+
return { title: 'Geometry validation' };
|
|
17
|
+
},
|
|
18
|
+
bfactor(img) {
|
|
19
|
+
return { title: 'B-factor' };
|
|
20
|
+
},
|
|
21
|
+
assembly(img, info) {
|
|
22
|
+
if (img.filename.includes('_chemically_distinct_molecules')) {
|
|
23
|
+
return { title: `Assembly ${info.assemblyId} (color by entity)` };
|
|
24
|
+
}
|
|
25
|
+
if (img.filename.includes('_chain')) {
|
|
26
|
+
return { title: `Assembly ${info.assemblyId} (color by chain)` };
|
|
27
|
+
}
|
|
28
|
+
return {};
|
|
29
|
+
},
|
|
30
|
+
entity(img, info) {
|
|
31
|
+
const entityName = getSpans(img.description)[1];
|
|
32
|
+
return {
|
|
33
|
+
title: `Entity ${info.entityId}`,
|
|
34
|
+
subtitle: entityName,
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
ligand(img, info) {
|
|
38
|
+
const ligandName = getParenthesis(getSpans(img.description)[0]);
|
|
39
|
+
return {
|
|
40
|
+
title: `Ligand environment for ${info.compId}`,
|
|
41
|
+
subtitle: ligandName,
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
modres(img, info) {
|
|
45
|
+
const modresName = getParenthesis(getSpans(img.description)[1]);
|
|
46
|
+
return {
|
|
47
|
+
title: `Modified residue ${info.compId}`,
|
|
48
|
+
subtitle: modresName,
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
domain(img, info) {
|
|
52
|
+
const familyName = getParenthesis(getSpans(img.description)[1]);
|
|
53
|
+
return {
|
|
54
|
+
title: `${info.db} ${info.familyId} (entity ${info.entityId})`,
|
|
55
|
+
subtitle: familyName,
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
/** Get contents of `<span ...>...</span>` tags from an HTML string */
|
|
60
|
+
function getSpans(text) {
|
|
61
|
+
const matches = (text !== null && text !== void 0 ? text : '').matchAll(/<span [^>]*>([^<]*)<\/span>/g);
|
|
62
|
+
return Array.from(matches).map(match => match[1]);
|
|
63
|
+
}
|
|
64
|
+
/** Get content of parenthesis (`(...)`) from a string */
|
|
65
|
+
function getParenthesis(text) {
|
|
66
|
+
var _a;
|
|
67
|
+
return (_a = text === null || text === void 0 ? void 0 : text.match(/\((.*)\)/)) === null || _a === void 0 ? void 0 : _a[1];
|
|
68
|
+
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { CollapsableControls, CollapsableState } from 'molstar/lib/mol-plugin-ui/base';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { StateGalleryManager } from './manager';
|
|
4
|
+
/** React state for `StateGalleryControls` */
|
|
4
5
|
interface StateGalleryControlsState {
|
|
6
|
+
/** Content of "Entry ID" text field */
|
|
5
7
|
entryId: string;
|
|
6
8
|
manager: StateGalleryManager | undefined;
|
|
9
|
+
/** `true` when initializing manager (fetching list of images) */
|
|
7
10
|
isLoading: boolean;
|
|
11
|
+
/** Mirrors `this.plugin.behaviors.state.isBusy` (`true` when loading a specific image) */
|
|
8
12
|
isBusy: boolean;
|
|
9
13
|
}
|
|
10
14
|
/** "3D State Gallery" section in Structure Tools (right panel) */
|
|
@@ -12,12 +16,16 @@ export declare class StateGalleryControls extends CollapsableControls<{}, StateG
|
|
|
12
16
|
protected defaultState(): StateGalleryControlsState & CollapsableState;
|
|
13
17
|
componentDidMount(): void;
|
|
14
18
|
private get values();
|
|
15
|
-
private setEntryId;
|
|
16
|
-
private load;
|
|
17
19
|
private onChangeValues;
|
|
20
|
+
/** Load entry given by `this.state.entryId` */
|
|
21
|
+
private load;
|
|
18
22
|
private loadDisabled;
|
|
19
23
|
protected renderControls(): React.JSX.Element | null;
|
|
20
24
|
}
|
|
21
|
-
/**
|
|
25
|
+
/** Part of "3D State Gallery" section related to a specific entry */
|
|
26
|
+
export declare function StateGalleryManagerControls(props: {
|
|
27
|
+
manager: StateGalleryManager;
|
|
28
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
/** Box in viewport with image title and arrows to move between images (3D states) */
|
|
22
30
|
export declare function StateGalleryTitleBox(): import("react/jsx-runtime").JSX.Element | null;
|
|
23
31
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.StateGalleryTitleBox = exports.StateGalleryControls = void 0;
|
|
3
|
+
exports.StateGalleryTitleBox = exports.StateGalleryManagerControls = exports.StateGalleryControls = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
6
|
const base_1 = require("molstar/lib/mol-plugin-ui/base");
|
|
@@ -13,18 +13,18 @@ const helpers_1 = require("../../helpers");
|
|
|
13
13
|
const icons_2 = require("../../ui/icons");
|
|
14
14
|
const behavior_1 = require("./behavior");
|
|
15
15
|
const manager_1 = require("./manager");
|
|
16
|
+
/** Parameter definition for ParameterControls part of "3D State Gallery" section */
|
|
16
17
|
const Params = {
|
|
17
|
-
entryId: param_definition_1.ParamDefinition.Text(),
|
|
18
|
+
entryId: param_definition_1.ParamDefinition.Text(undefined, { label: 'Entry ID' }),
|
|
18
19
|
};
|
|
19
20
|
/** "3D State Gallery" section in Structure Tools (right panel) */
|
|
20
21
|
class StateGalleryControls extends base_1.CollapsableControls {
|
|
21
22
|
constructor() {
|
|
22
23
|
super(...arguments);
|
|
23
|
-
this.
|
|
24
|
-
this.setState(
|
|
25
|
-
entryId,
|
|
26
|
-
}));
|
|
24
|
+
this.onChangeValues = (values) => {
|
|
25
|
+
this.setState({ entryId: values.entryId });
|
|
27
26
|
};
|
|
27
|
+
/** Load entry given by `this.state.entryId` */
|
|
28
28
|
this.load = () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
29
29
|
if (this.loadDisabled())
|
|
30
30
|
return;
|
|
@@ -32,9 +32,6 @@ class StateGalleryControls extends base_1.CollapsableControls {
|
|
|
32
32
|
const manager = yield manager_1.StateGalleryManager.create(this.plugin, this.state.entryId);
|
|
33
33
|
this.setState({ manager, isLoading: false, description: this.state.entryId.toUpperCase() });
|
|
34
34
|
});
|
|
35
|
-
this.onChangeValues = (values) => {
|
|
36
|
-
this.setEntryId(values.entryId);
|
|
37
|
-
};
|
|
38
35
|
this.loadDisabled = () => { var _a; return !this.state.entryId || this.state.entryId === ((_a = this.state.manager) === null || _a === void 0 ? void 0 : _a.entryId) || this.state.isBusy || this.state.isLoading; };
|
|
39
36
|
}
|
|
40
37
|
defaultState() {
|
|
@@ -59,7 +56,7 @@ class StateGalleryControls extends base_1.CollapsableControls {
|
|
|
59
56
|
if (this.state.entryId === '' && sel.structures.length > 0) {
|
|
60
57
|
const id = (_a = sel.structures[0].cell.obj) === null || _a === void 0 ? void 0 : _a.data.model.entryId;
|
|
61
58
|
if (id) {
|
|
62
|
-
this.
|
|
59
|
+
this.setState({ entryId: id.toLowerCase() });
|
|
63
60
|
}
|
|
64
61
|
}
|
|
65
62
|
});
|
|
@@ -73,11 +70,12 @@ class StateGalleryControls extends base_1.CollapsableControls {
|
|
|
73
70
|
renderControls() {
|
|
74
71
|
return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(parameters_1.ParameterControls, { params: Params, values: this.values, onChangeValues: this.onChangeValues, onEnter: this.load }), (!this.state.manager || this.state.manager.entryId !== this.state.entryId) &&
|
|
75
72
|
(0, jsx_runtime_1.jsx)(common_1.Button, Object.assign({ icon: this.state.isLoading ? undefined : icons_1.CheckSvg, title: 'Load', disabled: this.loadDisabled(), onClick: this.load, className: 'msp-btn-block' }, { children: this.state.isLoading ? 'Loading...' : 'Load' })), this.state.manager &&
|
|
76
|
-
(0, jsx_runtime_1.jsx)(
|
|
73
|
+
(0, jsx_runtime_1.jsx)(StateGalleryManagerControls, { manager: this.state.manager })] });
|
|
77
74
|
}
|
|
78
75
|
}
|
|
79
76
|
exports.StateGalleryControls = StateGalleryControls;
|
|
80
|
-
|
|
77
|
+
/** Part of "3D State Gallery" section related to a specific entry */
|
|
78
|
+
function StateGalleryManagerControls(props) {
|
|
81
79
|
var _a;
|
|
82
80
|
const images = props.manager.images;
|
|
83
81
|
const nImages = images.length;
|
|
@@ -108,21 +106,23 @@ function ManagerControls(props) {
|
|
|
108
106
|
}
|
|
109
107
|
return (0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'pdbemolstar-state-gallery-controls', onKeyDown: handleKeyDown, tabIndex: -1, ref: keyDownTargetRef }, { children: [(0, jsx_runtime_1.jsx)(common_1.ExpandGroup, Object.assign({ header: 'States', initiallyExpanded: true }, { children: (0, jsx_runtime_1.jsx)("div", Object.assign({ style: { marginBottom: 8 } }, { children: categories.groups.map(cat => {
|
|
110
108
|
var _a;
|
|
111
|
-
return (0, jsx_runtime_1.jsx)(common_1.ExpandGroup, Object.assign({ header: cat, initiallyExpanded: true }, { children: (_a = categories.members.get(cat)) === null || _a === void 0 ? void 0 : _a.map(img => (0, jsx_runtime_1.jsx)(
|
|
109
|
+
return (0, jsx_runtime_1.jsx)(common_1.ExpandGroup, Object.assign({ header: cat, initiallyExpanded: true }, { children: (_a = categories.members.get(cat)) === null || _a === void 0 ? void 0 : _a.map(img => (0, jsx_runtime_1.jsx)(ImageButton, { img: img, isSelected: img === selected, status: status, onClick: () => props.manager.load(img) }, img.filename)) }), cat);
|
|
112
110
|
}) })) }), 'states'), (0, jsx_runtime_1.jsx)(common_1.ExpandGroup, Object.assign({ header: 'Description', initiallyExpanded: true }, { children: (0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'pdbemolstar-state-gallery-legend' }, { children: (0, jsx_runtime_1.jsx)("div", { dangerouslySetInnerHTML: { __html: (_a = selected === null || selected === void 0 ? void 0 : selected.description) !== null && _a !== void 0 ? _a : '' } }) })) }), 'description')] }));
|
|
113
111
|
}
|
|
114
|
-
|
|
112
|
+
exports.StateGalleryManagerControls = StateGalleryManagerControls;
|
|
113
|
+
/** Button with image title */
|
|
114
|
+
function ImageButton(props) {
|
|
115
115
|
var _a;
|
|
116
116
|
const { img, isSelected, status, onClick } = props;
|
|
117
117
|
const icon = !isSelected ? icons_2.EmptyIconSvg : (status === 'loading') ? icons_2.HourglassBottomSvg : (status === 'error') ? icons_1.ErrorSvg : icons_1.CheckSvg;
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
return (0, jsx_runtime_1.jsx)(common_1.Button, Object.assign({ className: 'msp-action-menu-button pdbemolstar-state-gallery-state-button', icon: icon, onClick: onClick, title: `${errorMsg}${title}`, style: { fontWeight: isSelected ? 'bold' : undefined } }, { children: title }));
|
|
118
|
+
const tooltip = imageTooltip(img, isSelected ? status : 'ready');
|
|
119
|
+
return (0, jsx_runtime_1.jsxs)(common_1.Button, Object.assign({ className: 'msp-action-menu-button pdbemolstar-state-gallery-state-button', icon: icon, onClick: onClick, title: tooltip, style: { fontWeight: isSelected ? 'bold' : undefined } }, { children: [(_a = img.title) !== null && _a !== void 0 ? _a : img.filename, img.subtitle && (0, jsx_runtime_1.jsxs)("small", { children: ["\u2002 ", img.subtitle] })] }));
|
|
121
120
|
}
|
|
122
|
-
/** Box in viewport with
|
|
121
|
+
/** Box in viewport with image title and arrows to move between images (3D states) */
|
|
123
122
|
function StateGalleryTitleBox() {
|
|
123
|
+
var _a;
|
|
124
124
|
const plugin = react_1.default.useContext(base_1.PluginReactContext);
|
|
125
|
-
const [
|
|
125
|
+
const [image, setImage] = react_1.default.useState(undefined);
|
|
126
126
|
const [manager, setManager] = react_1.default.useState(undefined);
|
|
127
127
|
const [status, setStatus] = react_1.default.useState('ready');
|
|
128
128
|
const loadingCounter = (0, react_1.useRef)(0);
|
|
@@ -130,8 +130,8 @@ function StateGalleryTitleBox() {
|
|
|
130
130
|
var _a, _b, _c;
|
|
131
131
|
const customState = (0, behavior_1.StateGalleryCustomState)(plugin);
|
|
132
132
|
const subs = [
|
|
133
|
-
(_a = customState.
|
|
134
|
-
(_b = customState.manager) === null || _b === void 0 ? void 0 : _b.subscribe(
|
|
133
|
+
(_a = customState.requestedImage) === null || _a === void 0 ? void 0 : _a.subscribe(img => setImage(img)),
|
|
134
|
+
(_b = customState.manager) === null || _b === void 0 ? void 0 : _b.subscribe(mgr => setManager(mgr)),
|
|
135
135
|
(_c = customState.status) === null || _c === void 0 ? void 0 : _c.subscribe(status => {
|
|
136
136
|
const counter = ++loadingCounter.current;
|
|
137
137
|
if (status === 'loading') {
|
|
@@ -145,10 +145,18 @@ function StateGalleryTitleBox() {
|
|
|
145
145
|
];
|
|
146
146
|
return () => subs.forEach(sub => sub === null || sub === void 0 ? void 0 : sub.unsubscribe());
|
|
147
147
|
}, [plugin]);
|
|
148
|
-
if (
|
|
148
|
+
if (image === undefined)
|
|
149
149
|
return null;
|
|
150
150
|
return (0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'pdbemolstar-state-gallery-title-box' }, { children: [manager &&
|
|
151
|
-
(0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(common_1.Button, { className: 'msp-btn-icon', title: 'Previous state', icon: icons_2.ChevronLeftSvg, onClick: () => manager.loadPrevious() }) }), (0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'pdbemolstar-state-gallery-title', title:
|
|
151
|
+
(0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(common_1.Button, { className: 'msp-btn-icon', title: 'Previous state', icon: icons_2.ChevronLeftSvg, onClick: () => manager.loadPrevious() }) }), (0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'pdbemolstar-state-gallery-title', title: imageTooltip(image, status) }, { children: [(0, jsx_runtime_1.jsx)("div", Object.assign({ className: 'pdbemolstar-state-gallery-title-icon' }, { children: (0, jsx_runtime_1.jsx)(icons_1.Icon, { svg: status === 'error' ? icons_1.ErrorSvg : status === 'loading' ? icons_2.HourglassBottomSvg : icons_2.EmptyIconSvg }) })), (0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'pdbemolstar-state-gallery-title-text' }, { children: [(_a = image.title) !== null && _a !== void 0 ? _a : image.filename, (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)("small", { children: image.subtitle })] }))] })), manager &&
|
|
152
152
|
(0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(common_1.Button, { className: 'msp-btn-icon', title: 'Next state', icon: icons_2.ChevronRightSvg, onClick: () => manager === null || manager === void 0 ? void 0 : manager.loadNext() }) })] }));
|
|
153
153
|
}
|
|
154
154
|
exports.StateGalleryTitleBox = StateGalleryTitleBox;
|
|
155
|
+
/** Return tooltip text for an image */
|
|
156
|
+
function imageTooltip(img, status) {
|
|
157
|
+
var _a;
|
|
158
|
+
const tooltip = (status === 'error' ? '[Failed to load] \n' : status === 'loading' ? '[Loading] \n' : '')
|
|
159
|
+
+ ((_a = img.title) !== null && _a !== void 0 ? _a : img.filename)
|
|
160
|
+
+ (img.subtitle ? `: ${img.subtitle}` : '');
|
|
161
|
+
return tooltip;
|
|
162
|
+
}
|
package/lib/helpers.d.ts
CHANGED
|
@@ -179,12 +179,13 @@ export declare class PreemptiveQueue<X, Y> {
|
|
|
179
179
|
/** Request handling loop. Resolves when there are no more requests. Not to be awaited, should run in the background. */
|
|
180
180
|
private handleRequests;
|
|
181
181
|
}
|
|
182
|
+
/** Functions for working with plugin config items */
|
|
182
183
|
export declare namespace PluginConfigUtils {
|
|
183
|
-
type
|
|
184
|
+
/** Type of config definition for given type of config values T */
|
|
185
|
+
type ConfigFor<T extends object> = {
|
|
184
186
|
[key in keyof T]: PluginConfigItem<T[key]>;
|
|
185
187
|
};
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}, defaults: T): T;
|
|
188
|
+
/** Retrieve config values for items in `configItems` from the current plugin config */
|
|
189
|
+
function getConfigValues<T extends object>(plugin: PluginContext | undefined, configItems: ConfigFor<T>, defaults: T): T;
|
|
189
190
|
}
|
|
190
191
|
export {};
|
package/lib/helpers.js
CHANGED
|
@@ -507,8 +507,10 @@ class PreemptiveQueue {
|
|
|
507
507
|
}
|
|
508
508
|
}
|
|
509
509
|
exports.PreemptiveQueue = PreemptiveQueue;
|
|
510
|
+
/** Functions for working with plugin config items */
|
|
510
511
|
var PluginConfigUtils;
|
|
511
512
|
(function (PluginConfigUtils) {
|
|
513
|
+
/** Retrieve config values for items in `configItems` from the current plugin config */
|
|
512
514
|
function getConfigValues(plugin, configItems, defaults) {
|
|
513
515
|
var _a;
|
|
514
516
|
const values = {};
|
|
@@ -59,9 +59,11 @@ export interface PluginCustomState {
|
|
|
59
59
|
};
|
|
60
60
|
};
|
|
61
61
|
superpositionError?: string;
|
|
62
|
+
/** Space for extensions to save their plugin-bound custom state. Only access via `ExtensionCustomState`! */
|
|
62
63
|
extensions?: {
|
|
63
64
|
[extensionId: string]: {} | undefined;
|
|
64
65
|
};
|
|
66
|
+
/** Registry for custom UI components. Only access via `PluginCustomControls`! */
|
|
65
67
|
customControls?: {
|
|
66
68
|
[region in PluginCustomControlRegion]?: PluginCustomControlRegistry;
|
|
67
69
|
};
|
|
@@ -83,9 +85,15 @@ export interface Segment {
|
|
|
83
85
|
/** Access `plugin.customState` only through this function to get proper typing.
|
|
84
86
|
* Supports getting and setting properties. */
|
|
85
87
|
export declare function PluginCustomState(plugin: PluginContext): PluginCustomState;
|
|
86
|
-
|
|
87
|
-
export declare
|
|
88
|
-
|
|
88
|
+
/** Functions for accessing plugin-bound custom state for extensions. */
|
|
89
|
+
export declare const ExtensionCustomState: {
|
|
90
|
+
/** Get plugin-bound custom state for a specific extension. If not present, initialize with empty object. */
|
|
91
|
+
get<T extends {}>(plugin: PluginContext, extensionId: string): Partial<T>;
|
|
92
|
+
/** Remove plugin-bound custom state for a specific extension (if present). */
|
|
93
|
+
clear(plugin: PluginContext, extensionId: string): void;
|
|
94
|
+
/** Return function which gets plugin-bound custom state for a specific extension. */
|
|
95
|
+
getter<StateType extends {}>(extensionId: string): (plugin: PluginContext) => Partial<StateType>;
|
|
96
|
+
};
|
|
89
97
|
/** UI region where custom controls can be registered */
|
|
90
98
|
export type PluginCustomControlRegion = 'structure-tools' | 'viewport-top-center' | 'viewport-top-left';
|
|
91
99
|
/** Collection of registered custom controls in a UI region */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PluginCustomControls = exports.
|
|
3
|
+
exports.PluginCustomControls = exports.ExtensionCustomState = exports.PluginCustomState = void 0;
|
|
4
4
|
;
|
|
5
5
|
;
|
|
6
6
|
/** Access `plugin.customState` only through this function to get proper typing.
|
|
@@ -10,25 +10,28 @@ function PluginCustomState(plugin) {
|
|
|
10
10
|
return (_a = plugin.customState) !== null && _a !== void 0 ? _a : (plugin.customState = {});
|
|
11
11
|
}
|
|
12
12
|
exports.PluginCustomState = PluginCustomState;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
13
|
+
/** Functions for accessing plugin-bound custom state for extensions. */
|
|
14
|
+
exports.ExtensionCustomState = {
|
|
15
|
+
/** Get plugin-bound custom state for a specific extension. If not present, initialize with empty object. */
|
|
16
|
+
get(plugin, extensionId) {
|
|
17
|
+
var _a, _b;
|
|
18
|
+
var _c;
|
|
19
|
+
const extensionStates = (_a = (_c = PluginCustomState(plugin)).extensions) !== null && _a !== void 0 ? _a : (_c.extensions = {});
|
|
20
|
+
const extensionState = (_b = extensionStates[extensionId]) !== null && _b !== void 0 ? _b : (extensionStates[extensionId] = {});
|
|
21
|
+
return extensionState;
|
|
22
|
+
},
|
|
23
|
+
/** Remove plugin-bound custom state for a specific extension (if present). */
|
|
24
|
+
clear(plugin, extensionId) {
|
|
25
|
+
var _a;
|
|
26
|
+
var _b;
|
|
27
|
+
const extensionStates = (_a = (_b = PluginCustomState(plugin)).extensions) !== null && _a !== void 0 ? _a : (_b.extensions = {});
|
|
28
|
+
delete extensionStates[extensionId];
|
|
29
|
+
},
|
|
30
|
+
/** Return function which gets plugin-bound custom state for a specific extension. */
|
|
31
|
+
getter(extensionId) {
|
|
32
|
+
return (plugin) => this.get(plugin, extensionId);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
32
35
|
/** Functions for registering/unregistering custom UI controls */
|
|
33
36
|
exports.PluginCustomControls = {
|
|
34
37
|
/** Get custom controls in the specified UI `region`. */
|
package/lib/spec.d.ts
CHANGED
|
@@ -77,7 +77,9 @@ export interface InitParams {
|
|
|
77
77
|
};
|
|
78
78
|
/** Display 3D State Gallery */
|
|
79
79
|
galleryView: boolean;
|
|
80
|
-
/**
|
|
80
|
+
/** Set default visual style.
|
|
81
|
+
* Leave undefined to use default visual styles for each component type (polymer, ligand etc.).
|
|
82
|
+
* Use a `VisualStylesSpec` object to define more detailed visual styles for individual component types, e.g. `{polymer: {type: 'putty', size: 'uniform'}, ligand: 'ball-and-stick'}` */
|
|
81
83
|
visualStyle?: VisualStylesSpec;
|
|
82
84
|
/** Molstar renders multiple visuals (polymer, ligand, water...) visuals by default. This option is to exclude any of these default visuals */
|
|
83
85
|
hideStructure: ('polymer' | 'het' | 'water' | 'carbs' | 'nonStandard' | 'coarse')[];
|