@jbrowse/plugin-data-management 2.9.0 → 2.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/AddTrackWidget/components/DefaultAddTrackWorkflow.js +4 -8
  2. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.d.ts +1 -2
  3. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.js +6 -5
  4. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.d.ts +3 -3
  5. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js +7 -7
  6. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js +3 -9
  7. package/dist/HierarchicalTrackSelectorWidget/components/faceted/util.d.ts +6 -0
  8. package/dist/HierarchicalTrackSelectorWidget/components/faceted/util.js +10 -0
  9. package/dist/HierarchicalTrackSelectorWidget/components/tree/DropdownTrackSelector.js +3 -1
  10. package/dist/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.js +5 -2
  11. package/dist/HierarchicalTrackSelectorWidget/facetedModel.d.ts +2 -2
  12. package/dist/HierarchicalTrackSelectorWidget/facetedModel.js +3 -7
  13. package/dist/HierarchicalTrackSelectorWidget/model.d.ts +43 -40
  14. package/dist/HierarchicalTrackSelectorWidget/model.js +71 -32
  15. package/esm/AddTrackWidget/components/DefaultAddTrackWorkflow.js +4 -8
  16. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.d.ts +1 -2
  17. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.js +6 -5
  18. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.d.ts +3 -3
  19. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js +7 -7
  20. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js +3 -9
  21. package/esm/HierarchicalTrackSelectorWidget/components/faceted/util.d.ts +6 -0
  22. package/esm/HierarchicalTrackSelectorWidget/components/faceted/util.js +6 -0
  23. package/esm/HierarchicalTrackSelectorWidget/components/tree/DropdownTrackSelector.js +3 -1
  24. package/esm/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.js +5 -2
  25. package/esm/HierarchicalTrackSelectorWidget/facetedModel.d.ts +2 -2
  26. package/esm/HierarchicalTrackSelectorWidget/facetedModel.js +3 -7
  27. package/esm/HierarchicalTrackSelectorWidget/model.d.ts +43 -40
  28. package/esm/HierarchicalTrackSelectorWidget/model.js +71 -32
  29. package/package.json +2 -3
@@ -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)))))));
@@ -1,11 +1,10 @@
1
1
  import React from 'react';
2
2
  import { HierarchicalTrackSelectorModel } from '../../model';
3
- declare const FacetFilter: ({ column, vals, width, model, }: {
3
+ declare const FacetFilter: ({ column, vals, model, }: {
4
4
  column: {
5
5
  field: string;
6
6
  };
7
7
  vals: [string, number][];
8
- width: number;
9
8
  model: HierarchicalTrackSelectorModel;
10
9
  }) => React.JSX.Element;
11
10
  export default FacetFilter;
@@ -25,15 +25,16 @@ function ExpandButton({ visible, onClick, }) {
25
25
  return (React.createElement(Tooltip, { title: "Minimize/expand this facet filter" },
26
26
  React.createElement(IconButton, { onClick: () => onClick(), size: "small" }, visible ? React.createElement(MinimizeIcon, null) : React.createElement(AddIcon, null))));
27
27
  }
28
- const FacetFilter = observer(function ({ column, vals, width, model, }) {
28
+ const FacetFilter = observer(function ({ column, vals, model, }) {
29
29
  const { classes } = useStyles();
30
30
  const [visible, setVisible] = useState(true);
31
31
  const { faceted } = model;
32
32
  const { filters } = faceted;
33
- return (React.createElement(FormControl, { key: column.field, className: classes.facet, style: { width } },
34
- React.createElement("div", { style: { display: 'flex' } },
35
- React.createElement(Typography, null, column.field),
36
- React.createElement(ClearButton, { onClick: () => model.faceted.setFilter(column.field, []) }),
33
+ const { field } = column;
34
+ return (React.createElement(FormControl, { className: classes.facet, fullWidth: true },
35
+ React.createElement("div", null,
36
+ React.createElement(Typography, { component: "span" }, field),
37
+ React.createElement(ClearButton, { onClick: () => faceted.setFilter(field, []) }),
37
38
  React.createElement(ExpandButton, { visible: visible, onClick: () => setVisible(!visible) })),
38
39
  visible ? (React.createElement(Select, { multiple: true, native: true, className: classes.select, value: filters.get(column.field) || [], onChange: event => {
39
40
  faceted.setFilter(column.field,
@@ -1,11 +1,11 @@
1
1
  import React from 'react';
2
2
  import { HierarchicalTrackSelectorModel } from '../../model';
3
- declare const FacetFilters: ({ rows, columns, width, model, }: {
4
- rows: Record<string, unknown>[];
3
+ import { Row } from './util';
4
+ declare const FacetFilters: ({ rows, columns, model, }: {
5
+ rows: Row[];
5
6
  columns: {
6
7
  field: string;
7
8
  }[];
8
- width: number;
9
9
  model: HierarchicalTrackSelectorModel;
10
10
  }) => React.JSX.Element;
11
11
  export default FacetFilters;
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
- import FacetFilter from './FacetFilter';
3
2
  import { observer } from 'mobx-react';
4
- const FacetFilters = observer(function ({ rows, columns, width, model, }) {
3
+ // locals
4
+ import FacetFilter from './FacetFilter';
5
+ import { getRowStr } from './util';
6
+ const FacetFilters = observer(function ({ rows, columns, model, }) {
5
7
  var _a, _b;
6
8
  const { faceted } = model;
7
9
  const { filters } = faceted;
@@ -26,7 +28,7 @@ const FacetFilters = observer(function ({ rows, columns, width, model, }) {
26
28
  for (const facet of ret) {
27
29
  const elt = uniqs.get(facet);
28
30
  for (const row of currentRows) {
29
- const key = `${row[facet] || ''}`;
31
+ const key = getRowStr(facet, row);
30
32
  const val = elt.get(key);
31
33
  // we don't allow filtering on empty yet
32
34
  if (key) {
@@ -41,10 +43,8 @@ const FacetFilters = observer(function ({ rows, columns, width, model, }) {
41
43
  const filter = ((_b = filters.get(facet)) === null || _b === void 0 ? void 0 : _b.length)
42
44
  ? new Set(filters.get(facet))
43
45
  : undefined;
44
- currentRows = currentRows.filter(row => {
45
- return filter !== undefined ? filter.has(row[facet]) : true;
46
- });
46
+ currentRows = currentRows.filter(row => filter !== undefined ? filter.has(getRowStr(facet, row)) : true);
47
47
  }
48
- return (React.createElement("div", null, facets.map(column => (React.createElement(FacetFilter, { key: column.field, vals: [...uniqs.get(column.field)], column: column, width: width, model: model })))));
48
+ return (React.createElement("div", null, facets.map(c => (React.createElement(FacetFilter, { key: c.field, vals: [...uniqs.get(c.field)], column: c, model: model })))));
49
49
  });
50
50
  export default FacetFilters;
@@ -29,11 +29,10 @@ const frac = 0.75;
29
29
  const FacetedSelector = observer(function FacetedSelector({ model, }) {
30
30
  var _a;
31
31
  const { classes } = useStyles();
32
- const { view, selection, faceted } = model;
32
+ const { view, selection, shownTrackIds, faceted } = model;
33
33
  const { rows, panelWidth, showFilters, useShoppingCart, showOptions, filteredRows, filteredNonMetadataKeys, filteredMetadataKeys, visible, widths, } = faceted;
34
34
  const { pluginManager } = getEnv(model);
35
35
  const { ref, scrollLeft } = useResizeBar();
36
- const tracks = view.tracks;
37
36
  const widthsDebounced = useDebounce(widths, 200);
38
37
  const columns = [
39
38
  {
@@ -75,7 +74,6 @@ const FacetedSelector = observer(function FacetedSelector({ model, }) {
75
74
  });
76
75
  }),
77
76
  ];
78
- const shownTrackIds = new Set(tracks.map(t => t.configuration.trackId));
79
77
  return (React.createElement(React.Fragment, null,
80
78
  React.createElement(FacetedHeader, { model: model }),
81
79
  React.createElement("div", { ref: ref, style: {
@@ -125,11 +123,7 @@ const FacetedSelector = observer(function FacetedSelector({ model, }) {
125
123
  }, columns: columns, rowHeight: 25 })),
126
124
  showFilters ? (React.createElement(React.Fragment, null,
127
125
  React.createElement(ResizeHandle, { vertical: true, onDrag: dist => faceted.setPanelWidth(panelWidth - dist), className: classes.resizeHandle }),
128
- React.createElement("div", { style: {
129
- width: panelWidth,
130
- overflowY: 'auto',
131
- overflowX: 'hidden',
132
- } },
133
- React.createElement(FacetFilters, { model: model, width: panelWidth - 10, rows: rows, columns: columns })))) : null)));
126
+ React.createElement("div", { style: { width: panelWidth, overflow: 'auto' } },
127
+ React.createElement(FacetFilters, { model: model, rows: rows, columns: columns })))) : null)));
134
128
  });
135
129
  export default FacetedSelector;
@@ -0,0 +1,6 @@
1
+ export interface Row {
2
+ id: string;
3
+ metadata?: Record<string, unknown>;
4
+ [key: string]: unknown;
5
+ }
6
+ export declare function getRowStr(facet: string, row: Row): string;
@@ -0,0 +1,6 @@
1
+ export function getRowStr(facet, row) {
2
+ var _a;
3
+ return `${(facet.startsWith('metadata.')
4
+ ? (_a = row.metadata) === null || _a === void 0 ? void 0 : _a[facet.replace('metadata.', '')]
5
+ : row[facet]) || ''}`;
6
+ }
@@ -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;
@@ -81,7 +81,7 @@ export declare function facetedStateTreeF(): import("mobx-state-tree").IModelTyp
81
81
  readonly category: string;
82
82
  readonly adapter: string;
83
83
  readonly description: string;
84
- readonly metadata: any;
84
+ readonly metadata: Record<string, unknown>;
85
85
  }[];
86
86
  } & {
87
87
  /**
@@ -111,7 +111,7 @@ export declare function facetedStateTreeF(): import("mobx-state-tree").IModelTyp
111
111
  readonly category: string;
112
112
  readonly adapter: string;
113
113
  readonly description: string;
114
- readonly metadata: any;
114
+ readonly metadata: Record<string, unknown>;
115
115
  }[];
116
116
  } & {
117
117
  /**
@@ -5,6 +5,7 @@ import { getTrackName } from '@jbrowse/core/util/tracks';
5
5
  import { getSession, localStorageGetItem, measureGridWidth, } from '@jbrowse/core/util';
6
6
  import { autorun, observable } from 'mobx';
7
7
  import { getRootKeys, findNonSparseKeys } from './facetedUtil';
8
+ import { getRowStr } from './components/faceted/util';
8
9
  const nonMetadataKeys = ['category', 'adapter', 'description'];
9
10
  /**
10
11
  * #stateModel FacetedModel
@@ -98,13 +99,10 @@ export function facetedStateTreeF() {
98
99
  get rows() {
99
100
  const session = getSession(self);
100
101
  const { allTrackConfigurations, filterText } = self;
101
- // metadata is spread onto the object for easier access and sorting
102
- // by the mui data grid (it's unable to sort by nested objects)
103
102
  return allTrackConfigurations
104
103
  .filter(conf => matches(filterText, conf, session))
105
104
  .map(track => {
106
105
  var _a, _b;
107
- const metadata = readConfObject(track, 'metadata');
108
106
  return {
109
107
  id: track.trackId,
110
108
  conf: track,
@@ -112,7 +110,7 @@ export function facetedStateTreeF() {
112
110
  category: (_a = readConfObject(track, 'category')) === null || _a === void 0 ? void 0 : _a.join(', '),
113
111
  adapter: (_b = readConfObject(track, 'adapter')) === null || _b === void 0 ? void 0 : _b.type,
114
112
  description: readConfObject(track, 'description'),
115
- metadata,
113
+ metadata: readConfObject(track, 'metadata'),
116
114
  };
117
115
  });
118
116
  },
@@ -156,9 +154,7 @@ export function facetedStateTreeF() {
156
154
  const arrFilters = [...self.filters.entries()]
157
155
  .filter(f => f[1].length > 0)
158
156
  .map(([key, val]) => [key, new Set(val)]);
159
- return self.rows.filter(row =>
160
- // @ts-expect-error
161
- arrFilters.every(([key, val]) => val.has(row[key])));
157
+ return self.rows.filter(row => arrFilters.every(([key, val]) => val.has(getRowStr(key, row))));
162
158
  },
163
159
  }))
164
160
  .actions(self => ({
@@ -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
  */
@@ -56,13 +44,14 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
56
44
  showOptions: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
57
45
  panelWidth: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<number>, [undefined]>;
58
46
  }, {
59
- /**
47
+ visible: Record<string, boolean>;
48
+ widths: Record<string, number | undefined>; /**
60
49
  * #property
61
50
  */
62
- visible: Record<string, boolean>;
63
- widths: Record<string, number | undefined>;
64
51
  useShoppingCart: boolean;
65
- filters: import("mobx").ObservableMap<string, string[]>;
52
+ filters: import("mobx").ObservableMap<string, string[]>; /**
53
+ * #property
54
+ */
66
55
  } & {
67
56
  setFilter(key: string, value: string[]): void;
68
57
  setPanelWidth(width: number): void;
@@ -86,12 +75,10 @@ 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
- readonly metadata: any;
81
+ readonly metadata: Record<string, unknown>;
95
82
  }[];
96
83
  } & {
97
84
  readonly filteredNonMetadataKeys: string[] | readonly ["category", "adapter", "description"];
@@ -106,12 +93,10 @@ 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
- readonly metadata: any;
99
+ readonly metadata: Record<string, unknown>;
115
100
  }[];
116
101
  } & {
117
102
  setVisible(args: Record<string, boolean>): void;
@@ -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 & {
@@ -128,6 +115,10 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
128
115
  recentlyUsedCounter: number;
129
116
  favoritesCounter: number;
130
117
  } & {
118
+ /**
119
+ * #getter
120
+ */
121
+ readonly shownTrackIds: Set<string>;
131
122
  /**
132
123
  * #getter
133
124
  */
@@ -185,6 +176,14 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
185
176
  * #action
186
177
  */
187
178
  setRecentlyUsedCounter(val: number): void;
179
+ /**
180
+ * #action
181
+ */
182
+ setRecentlyUsed(str: string[]): void;
183
+ /**
184
+ * #action
185
+ */
186
+ setFavorites(str: string[]): void;
188
187
  /**
189
188
  * #action
190
189
  */
@@ -244,6 +243,14 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
244
243
  */
245
244
  readonly assemblyNames: string[];
246
245
  } & {
246
+ /**
247
+ * #getter
248
+ */
249
+ readonly recentlyUsedLocalStorageKey: string;
250
+ /**
251
+ * #getter
252
+ */
253
+ readonly favoritesLocalStorageKey: string;
247
254
  /**
248
255
  * #getter
249
256
  */
@@ -261,11 +268,22 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
261
268
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
262
269
  setSubschema(slotName: string, data: unknown): any;
263
270
  } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>)[];
271
+ /**
272
+ * #getter
273
+ */
264
274
  readonly allTrackConfigurations: ({
265
275
  [x: string]: any;
266
276
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
267
277
  setSubschema(slotName: string, data: unknown): any;
268
278
  } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>)[];
279
+ /**
280
+ * #getter
281
+ */
282
+ readonly allTrackConfigurationTrackIdSet: Map<any, {
283
+ [x: string]: any;
284
+ } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
285
+ setSubschema(slotName: string, data: unknown): any;
286
+ } & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>>;
269
287
  } & {
270
288
  /**
271
289
  * #getter
@@ -335,22 +353,7 @@ export default function stateTreeFactory(pluginManager: PluginManager): import("
335
353
  readonly hasAnySubcategories: boolean;
336
354
  } & {
337
355
  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
- }>;
356
+ }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>;
354
357
  export type HierarchicalTrackSelectorStateModel = ReturnType<typeof stateTreeFactory>;
355
358
  export type HierarchicalTrackSelectorModel = Instance<HierarchicalTrackSelectorStateModel>;
356
359
  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,30 +58,27 @@ 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,
83
72
  favoritesCounter: 0,
84
73
  }))
85
74
  .views(self => ({
75
+ /**
76
+ * #getter
77
+ */
78
+ get shownTrackIds() {
79
+ var _a, _b;
80
+ return new Set((_b = (_a = self.view) === null || _a === void 0 ? void 0 : _a.tracks) === null || _b === void 0 ? void 0 : _b.map((t) => t.configuration.trackId));
81
+ },
86
82
  /**
87
83
  * #getter
88
84
  */
@@ -145,19 +141,19 @@ export default function stateTreeFactory(pluginManager) {
145
141
  */
146
142
  addToFavorites(trackId) {
147
143
  self.favoritesCounter += 1;
148
- self.favorites.push(trackId);
144
+ self.favorites = [...self.favorites, trackId];
149
145
  },
150
146
  /**
151
147
  * #action
152
148
  */
153
149
  removeFromFavorites(trackId) {
154
- self.favorites.remove(trackId);
150
+ self.favorites = self.favorites.filter(f => f !== trackId);
155
151
  },
156
152
  /**
157
153
  * #action
158
154
  */
159
155
  clearFavorites() {
160
- self.favorites.clear();
156
+ self.favorites = [];
161
157
  },
162
158
  /**
163
159
  * #action
@@ -165,6 +161,18 @@ export default function stateTreeFactory(pluginManager) {
165
161
  setRecentlyUsedCounter(val) {
166
162
  self.recentlyUsedCounter = val;
167
163
  },
164
+ /**
165
+ * #action
166
+ */
167
+ setRecentlyUsed(str) {
168
+ self.recentlyUsed = str;
169
+ },
170
+ /**
171
+ * #action
172
+ */
173
+ setFavorites(str) {
174
+ self.favorites = str;
175
+ },
168
176
  /**
169
177
  * #action
170
178
  */
@@ -175,19 +183,19 @@ export default function stateTreeFactory(pluginManager) {
175
183
  * #action
176
184
  */
177
185
  addToRecentlyUsed(id) {
178
- self.recentlyUsedCounter += 1;
179
186
  if (!self.recentlyUsed.includes(id)) {
180
- if (self.recentlyUsed.length >= 10) {
181
- self.recentlyUsed.shift();
182
- }
183
- self.recentlyUsed.push(id);
187
+ self.recentlyUsedCounter = Math.min(self.recentlyUsedCounter + 1, MAX_RECENTLY_USED);
188
+ self.recentlyUsed =
189
+ self.recentlyUsed.length >= MAX_RECENTLY_USED
190
+ ? [...self.recentlyUsed.slice(1), id]
191
+ : [...self.recentlyUsed, id];
184
192
  }
185
193
  },
186
194
  /**
187
195
  * #action
188
196
  */
189
197
  clearRecentlyUsed() {
190
- self.recentlyUsed.clear();
198
+ self.recentlyUsed = [];
191
199
  },
192
200
  /**
193
201
  * #action
@@ -274,6 +282,22 @@ export default function stateTreeFactory(pluginManager) {
274
282
  },
275
283
  }))
276
284
  .views(self => ({
285
+ /**
286
+ * #getter
287
+ */
288
+ get recentlyUsedLocalStorageKey() {
289
+ return `recentlyUsedTracks-${[postF(), self.assemblyNames.join(',')]
290
+ .filter(f => !!f)
291
+ .join('-')}`;
292
+ },
293
+ /**
294
+ * #getter
295
+ */
296
+ get favoritesLocalStorageKey() {
297
+ // this has a extra } at the end because that's how it was initially
298
+ // released
299
+ return `favoriteTracks-${postF()}}`;
300
+ },
277
301
  /**
278
302
  * #getter
279
303
  */
@@ -298,6 +322,9 @@ export default function stateTreeFactory(pluginManager) {
298
322
  ...filterTracks(getSession(self).tracks, self),
299
323
  ].filter(notEmpty);
300
324
  },
325
+ /**
326
+ * #getter
327
+ */
301
328
  get allTrackConfigurations() {
302
329
  const { connectionInstances = [] } = getSession(self);
303
330
  return [
@@ -305,6 +332,12 @@ export default function stateTreeFactory(pluginManager) {
305
332
  ...connectionInstances === null || connectionInstances === void 0 ? void 0 : connectionInstances.flatMap(c => c.tracks),
306
333
  ];
307
334
  },
335
+ /**
336
+ * #getter
337
+ */
338
+ get allTrackConfigurationTrackIdSet() {
339
+ return new Map(this.allTrackConfigurations.map(t => [t.trackId, t]));
340
+ },
308
341
  }))
309
342
  .views(self => ({
310
343
  /**
@@ -312,14 +345,18 @@ export default function stateTreeFactory(pluginManager) {
312
345
  * filters out tracks that are not in the favorites group
313
346
  */
314
347
  get favoriteTracks() {
315
- return self.allTrackConfigurations.filter(t => self.favoritesSet.has(t.trackId));
348
+ return self.favorites
349
+ .filter(t => self.allTrackConfigurationTrackIdSet.has(t))
350
+ .map(t => self.allTrackConfigurationTrackIdSet.get(t));
316
351
  },
317
352
  /**
318
353
  * #getter
319
354
  * filters out tracks that are not in the recently used group
320
355
  */
321
356
  get recentlyUsedTracks() {
322
- return self.allTrackConfigurations.filter(t => self.recentlyUsedSet.has(t.trackId));
357
+ return self.recentlyUsed
358
+ .filter(t => self.allTrackConfigurationTrackIdSet.has(t))
359
+ .map(t => self.allTrackConfigurationTrackIdSet.get(t));
323
360
  },
324
361
  }))
325
362
  .views(self => ({
@@ -435,14 +472,16 @@ export default function stateTreeFactory(pluginManager) {
435
472
  }))
436
473
  .actions(self => ({
437
474
  afterAttach() {
475
+ // this should be the first autorun to properly initialize
476
+ addDisposer(self, autorun(() => {
477
+ self.setRecentlyUsed(JSON.parse(localStorageGetItem(self.recentlyUsedLocalStorageKey) || '[]'));
478
+ self.setFavorites(JSON.parse(localStorageGetItem(self.favoritesLocalStorageKey) || '[]'));
479
+ }));
480
+ // this should be the second autorun
438
481
  addDisposer(self, autorun(() => {
439
- localStorageSetItem(lsKeyFavoritesF(), JSON.stringify(self.favorites));
440
- localStorageSetItem(lsKeyRecentlyUsedF(), JSON.stringify(self.recentlyUsed));
482
+ localStorageSetItem(self.favoritesLocalStorageKey, JSON.stringify(self.favorites));
483
+ localStorageSetItem(self.recentlyUsedLocalStorageKey, JSON.stringify(self.recentlyUsed));
441
484
  }));
442
485
  },
443
- }))
444
- .postProcessSnapshot(snap => {
445
- const { favorites: _, recentlyUsed: __, ...rest } = snap;
446
- return rest;
447
- });
486
+ }));
448
487
  }
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.1",
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": "442b5f87efddfdf4ccf520b4d9dd01ddd370cb07"
65
64
  }