@jbrowse/plugin-data-management 2.9.0 → 2.10.0

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.
@@ -122,9 +122,6 @@ const DefaultAddTrackWorkflow = (0, mobx_react_1.observer)(function ({ model, })
122
122
  };
123
123
  jobsManager.queueJob(newEntry);
124
124
  }
125
- else {
126
- session.notify('Open a new view, or use the track selector in an existing view, to view this track', 'info');
127
- }
128
125
  model.clearData();
129
126
  if ((0, util_1.isSessionModelWithWidgets)(session)) {
130
127
  session.hideWidget(model);
@@ -134,10 +131,6 @@ const DefaultAddTrackWorkflow = (0, mobx_react_1.observer)(function ({ model, })
134
131
  setTrackErrorMessage('Failed to add track.\nThe configuration of this file is not currently supported.');
135
132
  }
136
133
  }
137
- function handleBack() {
138
- setTrackErrorMessage(undefined);
139
- setActiveStep(activeStep - 1);
140
- }
141
134
  function isNextDisabled() {
142
135
  switch (activeStep) {
143
136
  case 0:
@@ -154,7 +147,10 @@ const DefaultAddTrackWorkflow = (0, mobx_react_1.observer)(function ({ model, })
154
147
  react_1.default.createElement(material_1.StepContent, null,
155
148
  getStepContent(idx),
156
149
  react_1.default.createElement("div", { className: classes.actionsContainer },
157
- react_1.default.createElement(material_1.Button, { disabled: activeStep === 0, onClick: handleBack, className: classes.button }, "Back"),
150
+ react_1.default.createElement(material_1.Button, { disabled: activeStep === 0, onClick: () => {
151
+ setTrackErrorMessage(undefined);
152
+ setActiveStep(activeStep - 1);
153
+ }, className: classes.button }, "Back"),
158
154
  react_1.default.createElement(material_1.Button, { disabled: isNextDisabled(), variant: "contained", color: "primary", onClick: handleNext, className: classes.button, "data-testid": "addTrackNextButton" }, activeStep === steps.length - 1 ? 'Add' : 'Next')),
159
155
  trackErrorMessage ? (react_1.default.createElement("div", { className: classes.alertContainer },
160
156
  react_1.default.createElement(material_1.Alert, { severity: "error" }, trackErrorMessage))) : null)))))));
@@ -47,7 +47,9 @@ const DropdownTrackSelector = (0, mobx_react_1.observer)(function ({ model, trac
47
47
  checked: view.tracks.some((f) => f.configuration === t),
48
48
  onClick: () => {
49
49
  if (!open) {
50
- model.view.toggleTrack(t.trackId);
50
+ if (model.view.toggleTrack(t.trackId)) {
51
+ model.addToRecentlyUsed(t.trackId);
52
+ }
51
53
  }
52
54
  },
53
55
  })),
@@ -6,14 +6,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
7
  const material_1 = require("@mui/material");
8
8
  const mobx_react_1 = require("mobx-react");
9
+ const mui_1 = require("tss-react/mui");
9
10
  // icons
10
11
  const Grade_1 = __importDefault(require("@mui/icons-material/Grade"));
11
12
  const DropdownTrackSelector_1 = __importDefault(require("./DropdownTrackSelector"));
12
- const mui_1 = require("tss-react/mui");
13
13
  const useStyles = (0, mui_1.makeStyles)()({
14
14
  smallBadge: {
15
15
  height: 14,
16
16
  },
17
+ margin: {
18
+ marginRight: 10,
19
+ },
17
20
  });
18
21
  const FavoriteTracks = (0, mobx_react_1.observer)(function ({ model, }) {
19
22
  const { classes } = useStyles();
@@ -36,7 +39,7 @@ const FavoriteTracks = (0, mobx_react_1.observer)(function ({ model, }) {
36
39
  react_1.default.createElement(material_1.Badge, { classes: { badge: classes.smallBadge }, color: "secondary", anchorOrigin: {
37
40
  vertical: 'bottom',
38
41
  horizontal: 'right',
39
- }, style: { marginRight: 10 }, badgeContent: model.favoritesCounter },
42
+ }, className: classes.margin, badgeContent: model.favoritesCounter },
40
43
  react_1.default.createElement(Grade_1.default, null))))) : null;
41
44
  });
42
45
  exports.default = FavoriteTracks;
@@ -34,18 +34,6 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
34
34
  * #property
35
35
  */
36
36
  view: import("mobx-state-tree").IMaybe<import("mobx-state-tree").IReferenceType<import("mobx-state-tree").IAnyType>>;
37
- /**
38
- * #property
39
- * this is removed in postProcessSnapshot, so is generally only loaded
40
- * from localstorage
41
- */
42
- favorites: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").IArrayType<import("mobx-state-tree").ISimpleType<string>>, [undefined]>;
43
- /**
44
- * #property
45
- * this is removed in postProcessSnapshot, so is generally only loaded
46
- * from localstorage
47
- */
48
- recentlyUsed: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").IArrayType<import("mobx-state-tree").ISimpleType<string>>, [undefined]>;
49
37
  /**
50
38
  * #property
51
39
  */
@@ -54,11 +42,10 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
54
42
  showSparse: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
55
43
  showFilters: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
56
44
  showOptions: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
57
- panelWidth: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<number>, [undefined]>;
58
- }, {
59
- /**
45
+ panelWidth: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<number>, [undefined]>; /**
60
46
  * #property
61
47
  */
48
+ }, {
62
49
  visible: Record<string, boolean>;
63
50
  widths: Record<string, number | undefined>;
64
51
  useShoppingCart: boolean;
@@ -69,7 +56,9 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
69
56
  setUseShoppingCart(f: boolean): void;
70
57
  setFilterText(str: string): void;
71
58
  setShowSparse(f: boolean): void;
72
- setShowOptions(f: boolean): void;
59
+ setShowOptions(f: boolean): void; /**
60
+ * #getter
61
+ */
73
62
  setShowFilters(f: boolean): void;
74
63
  } & {
75
64
  readonly allTrackConfigurations: ({
@@ -86,9 +75,7 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
86
75
  setSubschema(slotName: string, data: unknown): any;
87
76
  } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>;
88
77
  readonly name: string;
89
- readonly category: string; /**
90
- * #action
91
- */
78
+ readonly category: string;
92
79
  readonly adapter: string;
93
80
  readonly description: string;
94
81
  readonly metadata: any;
@@ -106,9 +93,7 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
106
93
  setSubschema(slotName: string, data: unknown): any;
107
94
  } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>;
108
95
  readonly name: string;
109
- readonly category: string; /**
110
- * #action
111
- */
96
+ readonly category: string;
112
97
  readonly adapter: string;
113
98
  readonly description: string;
114
99
  readonly metadata: any;
@@ -119,6 +104,8 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
119
104
  afterAttach(): void;
120
105
  }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>, [undefined]>;
121
106
  }, {
107
+ favorites: string[];
108
+ recentlyUsed: string[];
122
109
  selection: ({
123
110
  [x: string]: any;
124
111
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
@@ -185,6 +172,14 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
185
172
  * #action
186
173
  */
187
174
  setRecentlyUsedCounter(val: number): void;
175
+ /**
176
+ * #action
177
+ */
178
+ setRecentlyUsed(str: string[]): void;
179
+ /**
180
+ * #action
181
+ */
182
+ setFavorites(str: string[]): void;
188
183
  /**
189
184
  * #action
190
185
  */
@@ -244,6 +239,14 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
244
239
  */
245
240
  readonly assemblyNames: string[];
246
241
  } & {
242
+ /**
243
+ * #getter
244
+ */
245
+ readonly recentlyUsedLocalStorageKey: string;
246
+ /**
247
+ * #getter
248
+ */
249
+ readonly favoritesLocalStorageKey: string;
247
250
  /**
248
251
  * #getter
249
252
  */
@@ -261,11 +264,22 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
261
264
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
262
265
  setSubschema(slotName: string, data: unknown): any;
263
266
  } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>)[];
267
+ /**
268
+ * #getter
269
+ */
264
270
  readonly allTrackConfigurations: ({
265
271
  [x: string]: any;
266
272
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
267
273
  setSubschema(slotName: string, data: unknown): any;
268
274
  } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>)[];
275
+ /**
276
+ * #getter
277
+ */
278
+ readonly allTrackConfigurationTrackIdSet: Map<any, {
279
+ [x: string]: any;
280
+ } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
281
+ setSubschema(slotName: string, data: unknown): any;
282
+ } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>>;
269
283
  } & {
270
284
  /**
271
285
  * #getter
@@ -335,22 +349,7 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
335
349
  readonly hasAnySubcategories: boolean;
336
350
  } & {
337
351
  afterAttach(): void;
338
- }, import("mobx-state-tree")._NotCustomized, {
339
- type: "HierarchicalTrackSelectorWidget";
340
- view: import("mobx-state-tree").ReferenceIdentifier | undefined;
341
- id: string;
342
- collapsed: import("mobx").IKeyValueMap<boolean>;
343
- initialized: boolean | undefined;
344
- sortTrackNames: boolean | undefined;
345
- sortCategories: boolean | undefined;
346
- faceted: import("mobx-state-tree").ModelSnapshotType<{
347
- filterText: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
348
- showSparse: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
349
- showFilters: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
350
- showOptions: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
351
- panelWidth: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<number>, [undefined]>;
352
- }>;
353
- }>;
352
+ }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>;
354
353
  export type HierarchicalTrackSelectorStateModel = ReturnType<typeof stateTreeFactory>;
355
354
  export type HierarchicalTrackSelectorModel = Instance<HierarchicalTrackSelectorStateModel>;
356
355
  export {};
@@ -25,8 +25,7 @@ function postF() {
25
25
  ].join('-')
26
26
  : 'empty';
27
27
  }
28
- const lsKeyFavoritesF = () => `favoriteTracks-${postF()}}`;
29
- const lsKeyRecentlyUsedF = () => `recentlyUsedTracks-${postF()}}`;
28
+ const MAX_RECENTLY_USED = 10;
30
29
  /**
31
30
  * #stateModel HierarchicalTrackSelectorWidget
32
31
  */
@@ -61,24 +60,14 @@ function stateTreeFactory(pluginManager) {
61
60
  * #property
62
61
  */
63
62
  view: mobx_state_tree_1.types.safeReference(pluginManager.pluggableMstType('view', 'stateModel')),
64
- /**
65
- * #property
66
- * this is removed in postProcessSnapshot, so is generally only loaded
67
- * from localstorage
68
- */
69
- favorites: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.array(mobx_state_tree_1.types.string), () => JSON.parse((0, util_1.localStorageGetItem)(lsKeyFavoritesF()) || '[]')),
70
- /**
71
- * #property
72
- * this is removed in postProcessSnapshot, so is generally only loaded
73
- * from localstorage
74
- */
75
- recentlyUsed: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.array(mobx_state_tree_1.types.string), () => JSON.parse((0, util_1.localStorageGetItem)(lsKeyRecentlyUsedF()) || '[]')),
76
63
  /**
77
64
  * #property
78
65
  */
79
66
  faceted: mobx_state_tree_1.types.optional((0, facetedModel_1.facetedStateTreeF)(), {}),
80
67
  })
81
68
  .volatile(() => ({
69
+ favorites: [],
70
+ recentlyUsed: [],
82
71
  selection: [],
83
72
  filterText: '',
84
73
  recentlyUsedCounter: 0,
@@ -147,19 +136,19 @@ function stateTreeFactory(pluginManager) {
147
136
  */
148
137
  addToFavorites(trackId) {
149
138
  self.favoritesCounter += 1;
150
- self.favorites.push(trackId);
139
+ self.favorites = [...self.favorites, trackId];
151
140
  },
152
141
  /**
153
142
  * #action
154
143
  */
155
144
  removeFromFavorites(trackId) {
156
- self.favorites.remove(trackId);
145
+ self.favorites = self.favorites.filter(f => f !== trackId);
157
146
  },
158
147
  /**
159
148
  * #action
160
149
  */
161
150
  clearFavorites() {
162
- self.favorites.clear();
151
+ self.favorites = [];
163
152
  },
164
153
  /**
165
154
  * #action
@@ -167,6 +156,18 @@ function stateTreeFactory(pluginManager) {
167
156
  setRecentlyUsedCounter(val) {
168
157
  self.recentlyUsedCounter = val;
169
158
  },
159
+ /**
160
+ * #action
161
+ */
162
+ setRecentlyUsed(str) {
163
+ self.recentlyUsed = str;
164
+ },
165
+ /**
166
+ * #action
167
+ */
168
+ setFavorites(str) {
169
+ self.favorites = str;
170
+ },
170
171
  /**
171
172
  * #action
172
173
  */
@@ -177,19 +178,19 @@ function stateTreeFactory(pluginManager) {
177
178
  * #action
178
179
  */
179
180
  addToRecentlyUsed(id) {
180
- self.recentlyUsedCounter += 1;
181
181
  if (!self.recentlyUsed.includes(id)) {
182
- if (self.recentlyUsed.length >= 10) {
183
- self.recentlyUsed.shift();
184
- }
185
- self.recentlyUsed.push(id);
182
+ self.recentlyUsedCounter = Math.min(self.recentlyUsedCounter + 1, MAX_RECENTLY_USED);
183
+ self.recentlyUsed =
184
+ self.recentlyUsed.length >= MAX_RECENTLY_USED
185
+ ? [...self.recentlyUsed.slice(1), id]
186
+ : [...self.recentlyUsed, id];
186
187
  }
187
188
  },
188
189
  /**
189
190
  * #action
190
191
  */
191
192
  clearRecentlyUsed() {
192
- self.recentlyUsed.clear();
193
+ self.recentlyUsed = [];
193
194
  },
194
195
  /**
195
196
  * #action
@@ -276,6 +277,22 @@ function stateTreeFactory(pluginManager) {
276
277
  },
277
278
  }))
278
279
  .views(self => ({
280
+ /**
281
+ * #getter
282
+ */
283
+ get recentlyUsedLocalStorageKey() {
284
+ return `recentlyUsedTracks-${[postF(), self.assemblyNames.join(',')]
285
+ .filter(f => !!f)
286
+ .join('-')}`;
287
+ },
288
+ /**
289
+ * #getter
290
+ */
291
+ get favoritesLocalStorageKey() {
292
+ // this has a extra } at the end because that's how it was initially
293
+ // released
294
+ return `favoriteTracks-${postF()}}`;
295
+ },
279
296
  /**
280
297
  * #getter
281
298
  */
@@ -300,6 +317,9 @@ function stateTreeFactory(pluginManager) {
300
317
  ...(0, filterTracks_1.filterTracks)((0, util_1.getSession)(self).tracks, self),
301
318
  ].filter(util_1.notEmpty);
302
319
  },
320
+ /**
321
+ * #getter
322
+ */
303
323
  get allTrackConfigurations() {
304
324
  const { connectionInstances = [] } = (0, util_1.getSession)(self);
305
325
  return [
@@ -307,6 +327,12 @@ function stateTreeFactory(pluginManager) {
307
327
  ...connectionInstances === null || connectionInstances === void 0 ? void 0 : connectionInstances.flatMap(c => c.tracks),
308
328
  ];
309
329
  },
330
+ /**
331
+ * #getter
332
+ */
333
+ get allTrackConfigurationTrackIdSet() {
334
+ return new Map(this.allTrackConfigurations.map(t => [t.trackId, t]));
335
+ },
310
336
  }))
311
337
  .views(self => ({
312
338
  /**
@@ -314,14 +340,18 @@ function stateTreeFactory(pluginManager) {
314
340
  * filters out tracks that are not in the favorites group
315
341
  */
316
342
  get favoriteTracks() {
317
- return self.allTrackConfigurations.filter(t => self.favoritesSet.has(t.trackId));
343
+ return self.favorites
344
+ .filter(t => self.allTrackConfigurationTrackIdSet.has(t))
345
+ .map(t => self.allTrackConfigurationTrackIdSet.get(t));
318
346
  },
319
347
  /**
320
348
  * #getter
321
349
  * filters out tracks that are not in the recently used group
322
350
  */
323
351
  get recentlyUsedTracks() {
324
- return self.allTrackConfigurations.filter(t => self.recentlyUsedSet.has(t.trackId));
352
+ return self.recentlyUsed
353
+ .filter(t => self.allTrackConfigurationTrackIdSet.has(t))
354
+ .map(t => self.allTrackConfigurationTrackIdSet.get(t));
325
355
  },
326
356
  }))
327
357
  .views(self => ({
@@ -437,15 +467,17 @@ function stateTreeFactory(pluginManager) {
437
467
  }))
438
468
  .actions(self => ({
439
469
  afterAttach() {
470
+ // this should be the first autorun to properly initialize
471
+ (0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
472
+ self.setRecentlyUsed(JSON.parse((0, util_1.localStorageGetItem)(self.recentlyUsedLocalStorageKey) || '[]'));
473
+ self.setFavorites(JSON.parse((0, util_1.localStorageGetItem)(self.favoritesLocalStorageKey) || '[]'));
474
+ }));
475
+ // this should be the second autorun
440
476
  (0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
441
- (0, util_1.localStorageSetItem)(lsKeyFavoritesF(), JSON.stringify(self.favorites));
442
- (0, util_1.localStorageSetItem)(lsKeyRecentlyUsedF(), JSON.stringify(self.recentlyUsed));
477
+ (0, util_1.localStorageSetItem)(self.favoritesLocalStorageKey, JSON.stringify(self.favorites));
478
+ (0, util_1.localStorageSetItem)(self.recentlyUsedLocalStorageKey, JSON.stringify(self.recentlyUsed));
443
479
  }));
444
480
  },
445
- }))
446
- .postProcessSnapshot(snap => {
447
- const { favorites: _, recentlyUsed: __, ...rest } = snap;
448
- return rest;
449
- });
481
+ }));
450
482
  }
451
483
  exports.default = stateTreeFactory;
@@ -94,9 +94,6 @@ const DefaultAddTrackWorkflow = observer(function ({ model, }) {
94
94
  };
95
95
  jobsManager.queueJob(newEntry);
96
96
  }
97
- else {
98
- session.notify('Open a new view, or use the track selector in an existing view, to view this track', 'info');
99
- }
100
97
  model.clearData();
101
98
  if (isSessionModelWithWidgets(session)) {
102
99
  session.hideWidget(model);
@@ -106,10 +103,6 @@ const DefaultAddTrackWorkflow = observer(function ({ model, }) {
106
103
  setTrackErrorMessage('Failed to add track.\nThe configuration of this file is not currently supported.');
107
104
  }
108
105
  }
109
- function handleBack() {
110
- setTrackErrorMessage(undefined);
111
- setActiveStep(activeStep - 1);
112
- }
113
106
  function isNextDisabled() {
114
107
  switch (activeStep) {
115
108
  case 0:
@@ -126,7 +119,10 @@ const DefaultAddTrackWorkflow = observer(function ({ model, }) {
126
119
  React.createElement(StepContent, null,
127
120
  getStepContent(idx),
128
121
  React.createElement("div", { className: classes.actionsContainer },
129
- React.createElement(Button, { disabled: activeStep === 0, onClick: handleBack, className: classes.button }, "Back"),
122
+ React.createElement(Button, { disabled: activeStep === 0, onClick: () => {
123
+ setTrackErrorMessage(undefined);
124
+ setActiveStep(activeStep - 1);
125
+ }, className: classes.button }, "Back"),
130
126
  React.createElement(Button, { disabled: isNextDisabled(), variant: "contained", color: "primary", onClick: handleNext, className: classes.button, "data-testid": "addTrackNextButton" }, activeStep === steps.length - 1 ? 'Add' : 'Next')),
131
127
  trackErrorMessage ? (React.createElement("div", { className: classes.alertContainer },
132
128
  React.createElement(Alert, { severity: "error" }, trackErrorMessage))) : null)))))));
@@ -19,7 +19,9 @@ const DropdownTrackSelector = observer(function ({ model, tracks, extraMenuItems
19
19
  checked: view.tracks.some((f) => f.configuration === t),
20
20
  onClick: () => {
21
21
  if (!open) {
22
- model.view.toggleTrack(t.trackId);
22
+ if (model.view.toggleTrack(t.trackId)) {
23
+ model.addToRecentlyUsed(t.trackId);
24
+ }
23
25
  }
24
26
  },
25
27
  })),
@@ -1,14 +1,17 @@
1
1
  import React from 'react';
2
2
  import { Badge, Tooltip } from '@mui/material';
3
3
  import { observer } from 'mobx-react';
4
+ import { makeStyles } from 'tss-react/mui';
4
5
  // icons
5
6
  import GradeIcon from '@mui/icons-material/Grade';
6
7
  import DropdownTrackSelector from './DropdownTrackSelector';
7
- import { makeStyles } from 'tss-react/mui';
8
8
  const useStyles = makeStyles()({
9
9
  smallBadge: {
10
10
  height: 14,
11
11
  },
12
+ margin: {
13
+ marginRight: 10,
14
+ },
12
15
  });
13
16
  const FavoriteTracks = observer(function ({ model, }) {
14
17
  const { classes } = useStyles();
@@ -31,7 +34,7 @@ const FavoriteTracks = observer(function ({ model, }) {
31
34
  React.createElement(Badge, { classes: { badge: classes.smallBadge }, color: "secondary", anchorOrigin: {
32
35
  vertical: 'bottom',
33
36
  horizontal: 'right',
34
- }, style: { marginRight: 10 }, badgeContent: model.favoritesCounter },
37
+ }, className: classes.margin, badgeContent: model.favoritesCounter },
35
38
  React.createElement(GradeIcon, null))))) : null;
36
39
  });
37
40
  export default FavoriteTracks;
@@ -34,18 +34,6 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
34
34
  * #property
35
35
  */
36
36
  view: import("mobx-state-tree").IMaybe<import("mobx-state-tree").IReferenceType<import("mobx-state-tree").IAnyType>>;
37
- /**
38
- * #property
39
- * this is removed in postProcessSnapshot, so is generally only loaded
40
- * from localstorage
41
- */
42
- favorites: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").IArrayType<import("mobx-state-tree").ISimpleType<string>>, [undefined]>;
43
- /**
44
- * #property
45
- * this is removed in postProcessSnapshot, so is generally only loaded
46
- * from localstorage
47
- */
48
- recentlyUsed: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").IArrayType<import("mobx-state-tree").ISimpleType<string>>, [undefined]>;
49
37
  /**
50
38
  * #property
51
39
  */
@@ -54,11 +42,10 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
54
42
  showSparse: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
55
43
  showFilters: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
56
44
  showOptions: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
57
- panelWidth: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<number>, [undefined]>;
58
- }, {
59
- /**
45
+ panelWidth: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<number>, [undefined]>; /**
60
46
  * #property
61
47
  */
48
+ }, {
62
49
  visible: Record<string, boolean>;
63
50
  widths: Record<string, number | undefined>;
64
51
  useShoppingCart: boolean;
@@ -69,7 +56,9 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
69
56
  setUseShoppingCart(f: boolean): void;
70
57
  setFilterText(str: string): void;
71
58
  setShowSparse(f: boolean): void;
72
- setShowOptions(f: boolean): void;
59
+ setShowOptions(f: boolean): void; /**
60
+ * #getter
61
+ */
73
62
  setShowFilters(f: boolean): void;
74
63
  } & {
75
64
  readonly allTrackConfigurations: ({
@@ -86,9 +75,7 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
86
75
  setSubschema(slotName: string, data: unknown): any;
87
76
  } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>;
88
77
  readonly name: string;
89
- readonly category: string; /**
90
- * #action
91
- */
78
+ readonly category: string;
92
79
  readonly adapter: string;
93
80
  readonly description: string;
94
81
  readonly metadata: any;
@@ -106,9 +93,7 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
106
93
  setSubschema(slotName: string, data: unknown): any;
107
94
  } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>;
108
95
  readonly name: string;
109
- readonly category: string; /**
110
- * #action
111
- */
96
+ readonly category: string;
112
97
  readonly adapter: string;
113
98
  readonly description: string;
114
99
  readonly metadata: any;
@@ -119,6 +104,8 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
119
104
  afterAttach(): void;
120
105
  }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>, [undefined]>;
121
106
  }, {
107
+ favorites: string[];
108
+ recentlyUsed: string[];
122
109
  selection: ({
123
110
  [x: string]: any;
124
111
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
@@ -185,6 +172,14 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
185
172
  * #action
186
173
  */
187
174
  setRecentlyUsedCounter(val: number): void;
175
+ /**
176
+ * #action
177
+ */
178
+ setRecentlyUsed(str: string[]): void;
179
+ /**
180
+ * #action
181
+ */
182
+ setFavorites(str: string[]): void;
188
183
  /**
189
184
  * #action
190
185
  */
@@ -244,6 +239,14 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
244
239
  */
245
240
  readonly assemblyNames: string[];
246
241
  } & {
242
+ /**
243
+ * #getter
244
+ */
245
+ readonly recentlyUsedLocalStorageKey: string;
246
+ /**
247
+ * #getter
248
+ */
249
+ readonly favoritesLocalStorageKey: string;
247
250
  /**
248
251
  * #getter
249
252
  */
@@ -261,11 +264,22 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
261
264
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
262
265
  setSubschema(slotName: string, data: unknown): any;
263
266
  } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>)[];
267
+ /**
268
+ * #getter
269
+ */
264
270
  readonly allTrackConfigurations: ({
265
271
  [x: string]: any;
266
272
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
267
273
  setSubschema(slotName: string, data: unknown): any;
268
274
  } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>)[];
275
+ /**
276
+ * #getter
277
+ */
278
+ readonly allTrackConfigurationTrackIdSet: Map<any, {
279
+ [x: string]: any;
280
+ } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
281
+ setSubschema(slotName: string, data: unknown): any;
282
+ } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>>;
269
283
  } & {
270
284
  /**
271
285
  * #getter
@@ -335,22 +349,7 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
335
349
  readonly hasAnySubcategories: boolean;
336
350
  } & {
337
351
  afterAttach(): void;
338
- }, import("mobx-state-tree")._NotCustomized, {
339
- type: "HierarchicalTrackSelectorWidget";
340
- view: import("mobx-state-tree").ReferenceIdentifier | undefined;
341
- id: string;
342
- collapsed: import("mobx").IKeyValueMap<boolean>;
343
- initialized: boolean | undefined;
344
- sortTrackNames: boolean | undefined;
345
- sortCategories: boolean | undefined;
346
- faceted: import("mobx-state-tree").ModelSnapshotType<{
347
- filterText: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
348
- showSparse: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
349
- showFilters: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
350
- showOptions: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
351
- panelWidth: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<number>, [undefined]>;
352
- }>;
353
- }>;
352
+ }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>;
354
353
  export type HierarchicalTrackSelectorStateModel = ReturnType<typeof stateTreeFactory>;
355
354
  export type HierarchicalTrackSelectorModel = Instance<HierarchicalTrackSelectorStateModel>;
356
355
  export {};
@@ -23,8 +23,7 @@ function postF() {
23
23
  ].join('-')
24
24
  : 'empty';
25
25
  }
26
- const lsKeyFavoritesF = () => `favoriteTracks-${postF()}}`;
27
- const lsKeyRecentlyUsedF = () => `recentlyUsedTracks-${postF()}}`;
26
+ const MAX_RECENTLY_USED = 10;
28
27
  /**
29
28
  * #stateModel HierarchicalTrackSelectorWidget
30
29
  */
@@ -59,24 +58,14 @@ export default function stateTreeFactory(pluginManager) {
59
58
  * #property
60
59
  */
61
60
  view: types.safeReference(pluginManager.pluggableMstType('view', 'stateModel')),
62
- /**
63
- * #property
64
- * this is removed in postProcessSnapshot, so is generally only loaded
65
- * from localstorage
66
- */
67
- favorites: types.optional(types.array(types.string), () => JSON.parse(localStorageGetItem(lsKeyFavoritesF()) || '[]')),
68
- /**
69
- * #property
70
- * this is removed in postProcessSnapshot, so is generally only loaded
71
- * from localstorage
72
- */
73
- recentlyUsed: types.optional(types.array(types.string), () => JSON.parse(localStorageGetItem(lsKeyRecentlyUsedF()) || '[]')),
74
61
  /**
75
62
  * #property
76
63
  */
77
64
  faceted: types.optional(facetedStateTreeF(), {}),
78
65
  })
79
66
  .volatile(() => ({
67
+ favorites: [],
68
+ recentlyUsed: [],
80
69
  selection: [],
81
70
  filterText: '',
82
71
  recentlyUsedCounter: 0,
@@ -145,19 +134,19 @@ export default function stateTreeFactory(pluginManager) {
145
134
  */
146
135
  addToFavorites(trackId) {
147
136
  self.favoritesCounter += 1;
148
- self.favorites.push(trackId);
137
+ self.favorites = [...self.favorites, trackId];
149
138
  },
150
139
  /**
151
140
  * #action
152
141
  */
153
142
  removeFromFavorites(trackId) {
154
- self.favorites.remove(trackId);
143
+ self.favorites = self.favorites.filter(f => f !== trackId);
155
144
  },
156
145
  /**
157
146
  * #action
158
147
  */
159
148
  clearFavorites() {
160
- self.favorites.clear();
149
+ self.favorites = [];
161
150
  },
162
151
  /**
163
152
  * #action
@@ -165,6 +154,18 @@ export default function stateTreeFactory(pluginManager) {
165
154
  setRecentlyUsedCounter(val) {
166
155
  self.recentlyUsedCounter = val;
167
156
  },
157
+ /**
158
+ * #action
159
+ */
160
+ setRecentlyUsed(str) {
161
+ self.recentlyUsed = str;
162
+ },
163
+ /**
164
+ * #action
165
+ */
166
+ setFavorites(str) {
167
+ self.favorites = str;
168
+ },
168
169
  /**
169
170
  * #action
170
171
  */
@@ -175,19 +176,19 @@ export default function stateTreeFactory(pluginManager) {
175
176
  * #action
176
177
  */
177
178
  addToRecentlyUsed(id) {
178
- self.recentlyUsedCounter += 1;
179
179
  if (!self.recentlyUsed.includes(id)) {
180
- if (self.recentlyUsed.length >= 10) {
181
- self.recentlyUsed.shift();
182
- }
183
- self.recentlyUsed.push(id);
180
+ self.recentlyUsedCounter = Math.min(self.recentlyUsedCounter + 1, MAX_RECENTLY_USED);
181
+ self.recentlyUsed =
182
+ self.recentlyUsed.length >= MAX_RECENTLY_USED
183
+ ? [...self.recentlyUsed.slice(1), id]
184
+ : [...self.recentlyUsed, id];
184
185
  }
185
186
  },
186
187
  /**
187
188
  * #action
188
189
  */
189
190
  clearRecentlyUsed() {
190
- self.recentlyUsed.clear();
191
+ self.recentlyUsed = [];
191
192
  },
192
193
  /**
193
194
  * #action
@@ -274,6 +275,22 @@ export default function stateTreeFactory(pluginManager) {
274
275
  },
275
276
  }))
276
277
  .views(self => ({
278
+ /**
279
+ * #getter
280
+ */
281
+ get recentlyUsedLocalStorageKey() {
282
+ return `recentlyUsedTracks-${[postF(), self.assemblyNames.join(',')]
283
+ .filter(f => !!f)
284
+ .join('-')}`;
285
+ },
286
+ /**
287
+ * #getter
288
+ */
289
+ get favoritesLocalStorageKey() {
290
+ // this has a extra } at the end because that's how it was initially
291
+ // released
292
+ return `favoriteTracks-${postF()}}`;
293
+ },
277
294
  /**
278
295
  * #getter
279
296
  */
@@ -298,6 +315,9 @@ export default function stateTreeFactory(pluginManager) {
298
315
  ...filterTracks(getSession(self).tracks, self),
299
316
  ].filter(notEmpty);
300
317
  },
318
+ /**
319
+ * #getter
320
+ */
301
321
  get allTrackConfigurations() {
302
322
  const { connectionInstances = [] } = getSession(self);
303
323
  return [
@@ -305,6 +325,12 @@ export default function stateTreeFactory(pluginManager) {
305
325
  ...connectionInstances === null || connectionInstances === void 0 ? void 0 : connectionInstances.flatMap(c => c.tracks),
306
326
  ];
307
327
  },
328
+ /**
329
+ * #getter
330
+ */
331
+ get allTrackConfigurationTrackIdSet() {
332
+ return new Map(this.allTrackConfigurations.map(t => [t.trackId, t]));
333
+ },
308
334
  }))
309
335
  .views(self => ({
310
336
  /**
@@ -312,14 +338,18 @@ export default function stateTreeFactory(pluginManager) {
312
338
  * filters out tracks that are not in the favorites group
313
339
  */
314
340
  get favoriteTracks() {
315
- return self.allTrackConfigurations.filter(t => self.favoritesSet.has(t.trackId));
341
+ return self.favorites
342
+ .filter(t => self.allTrackConfigurationTrackIdSet.has(t))
343
+ .map(t => self.allTrackConfigurationTrackIdSet.get(t));
316
344
  },
317
345
  /**
318
346
  * #getter
319
347
  * filters out tracks that are not in the recently used group
320
348
  */
321
349
  get recentlyUsedTracks() {
322
- return self.allTrackConfigurations.filter(t => self.recentlyUsedSet.has(t.trackId));
350
+ return self.recentlyUsed
351
+ .filter(t => self.allTrackConfigurationTrackIdSet.has(t))
352
+ .map(t => self.allTrackConfigurationTrackIdSet.get(t));
323
353
  },
324
354
  }))
325
355
  .views(self => ({
@@ -435,14 +465,16 @@ export default function stateTreeFactory(pluginManager) {
435
465
  }))
436
466
  .actions(self => ({
437
467
  afterAttach() {
468
+ // this should be the first autorun to properly initialize
469
+ addDisposer(self, autorun(() => {
470
+ self.setRecentlyUsed(JSON.parse(localStorageGetItem(self.recentlyUsedLocalStorageKey) || '[]'));
471
+ self.setFavorites(JSON.parse(localStorageGetItem(self.favoritesLocalStorageKey) || '[]'));
472
+ }));
473
+ // this should be the second autorun
438
474
  addDisposer(self, autorun(() => {
439
- localStorageSetItem(lsKeyFavoritesF(), JSON.stringify(self.favorites));
440
- localStorageSetItem(lsKeyRecentlyUsedF(), JSON.stringify(self.recentlyUsed));
475
+ localStorageSetItem(self.favoritesLocalStorageKey, JSON.stringify(self.favorites));
476
+ localStorageSetItem(self.recentlyUsedLocalStorageKey, JSON.stringify(self.recentlyUsed));
441
477
  }));
442
478
  },
443
- }))
444
- .postProcessSnapshot(snap => {
445
- const { favorites: _, recentlyUsed: __, ...rest } = snap;
446
- return rest;
447
- });
479
+ }));
448
480
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-data-management",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "description": "JBrowse 2 linear genome view",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -39,7 +39,6 @@
39
39
  "@gmod/ucsc-hub": "^0.3.0",
40
40
  "@mui/icons-material": "^5.0.1",
41
41
  "@mui/x-data-grid": "^6.0.1",
42
- "clsx": "^2.0.0",
43
42
  "react-virtualized-auto-sizer": "^1.0.2",
44
43
  "react-vtree": "^3.0.0-beta.1",
45
44
  "react-window": "^1.8.6"
@@ -61,5 +60,5 @@
61
60
  "distModule": "esm/index.js",
62
61
  "srcModule": "src/index.ts",
63
62
  "module": "esm/index.js",
64
- "gitHead": "a50b6f67cf8c8f3c65a7b8cd858de2fcca1f2909"
63
+ "gitHead": "223d8bfb68fd1bacaf22852639ad5920f1b7f43b"
65
64
  }