@weng-lab/genomebrowser-ui 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@weng-lab/genomebrowser-ui",
3
3
  "private": false,
4
- "version": "0.1.2",
4
+ "version": "0.1.4",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "publishConfig": {
@@ -31,7 +31,7 @@ export function DataGridWrapper(props: DataGridProps) {
31
31
  sortedAssay,
32
32
  handleSelection,
33
33
  rows,
34
- selectedIds
34
+ selectedTracks
35
35
  } = props;
36
36
 
37
37
  const CustomToolbarWrapper = useMemo(() => {
@@ -89,7 +89,7 @@ export function DataGridWrapper(props: DataGridProps) {
89
89
  columnVisibilityModel={{ displayname: false }} // so you don't see a second name column
90
90
  onRowSelectionModelChange={handleSelection}
91
91
  rowSelectionPropagation={{ descendants: true }}
92
- rowSelectionModel={{ type: "include", ids: new Set(selectedIds) }}
92
+ rowSelectionModel={{ type: "include", ids: new Set(selectedTracks.keys()) }}
93
93
  slots={{
94
94
  toolbar: CustomToolbarWrapper,
95
95
  }}
@@ -1,12 +1,7 @@
1
1
  import { capitalize } from "@mui/material";
2
2
  import Fuse, { FuseResult } from "fuse.js";
3
3
  import tracksData from "../Data/modifiedHumanTracks.json";
4
- import {
5
- AssayInfo,
6
- RowInfo,
7
- SearchTracksProps,
8
- TrackInfo
9
- } from "../types";
4
+ import { AssayInfo, RowInfo, SearchTracksProps, TrackInfo } from "../types";
10
5
 
11
6
  function formatAssayType(assay: string): string {
12
7
  switch (assay) {
@@ -62,7 +57,7 @@ export function getTracksByAssayAndOntology(
62
57
  */
63
58
  export function flattenIntoRow(track: TrackInfo): RowInfo {
64
59
  const { ontology, lifeStage, sampleType, displayname } = track;
65
- const { assay, experimentAccession, fileAccession } = track.assays[0];
60
+ const { assay, experimentAccession, fileAccession, url } = track.assays[0];
66
61
 
67
62
  return {
68
63
  ontology: capitalize(ontology),
@@ -72,6 +67,7 @@ export function flattenIntoRow(track: TrackInfo): RowInfo {
72
67
  assay: formatAssayType(assay),
73
68
  experimentAccession,
74
69
  fileAccession,
70
+ url,
75
71
  };
76
72
  }
77
73
 
@@ -111,4 +107,4 @@ export function searchTracks({
111
107
  keys: keyWeightMap,
112
108
  });
113
109
  return fuse.search(query);
114
- }
110
+ }
@@ -14,8 +14,8 @@ import {
14
14
  buildTreeView,
15
15
  searchTreeItems,
16
16
  } from "./TreeView/treeViewHelpers";
17
- import { SearchTracksProps, ExtendedTreeItemProps } from "./types";
18
- import { rows, rowById, getActiveTracks } from "./consts";
17
+ import { SearchTracksProps, ExtendedTreeItemProps, RowInfo } from "./types";
18
+ import { rows, rowById } from "./consts";
19
19
  import React, { useState, useMemo, useEffect } from "react";
20
20
  import { TreeViewBaseItem } from "@mui/x-tree-view";
21
21
  import { GridRowSelectionModel } from "@mui/x-data-grid";
@@ -37,21 +37,21 @@ export default function TrackSelect({ store }: TrackSelectProps) {
37
37
  const [sortedAssay, setSortedAssay] = useState(false);
38
38
  const [searchQuery, setSearchQuery] = useState("");
39
39
  const [isSearchResult, setIsSearchResult] = useState(false);
40
- const selectedIds = store((s) => s.selectedIds);
40
+ const selectedTracks = store((s) => s.selectedTracks);
41
41
  const setSelected = store((s) => s.setSelected);
42
42
  const clear = store((s) => s.clear);
43
43
  const MAX_ACTIVE = store((s) => s.maxTracks);
44
44
 
45
- // Derive active tracks from selectedIds (filters out auto-generated group IDs)
45
+ // Derive active tracks from selectedTracks (all keys are real track IDs)
46
46
  const activeTracks = useMemo(
47
- () => getActiveTracks(selectedIds),
48
- [selectedIds],
47
+ () => new Set(selectedTracks.keys()),
48
+ [selectedTracks],
49
49
  );
50
50
 
51
51
  const treeItems = useMemo(() => {
52
52
  return sortedAssay
53
53
  ? buildSortedAssayTreeView(
54
- Array.from(selectedIds),
54
+ Array.from(selectedTracks.keys()),
55
55
  {
56
56
  id: "1",
57
57
  isAssayItem: false,
@@ -63,7 +63,7 @@ export default function TrackSelect({ store }: TrackSelectProps) {
63
63
  rowById,
64
64
  )
65
65
  : buildTreeView(
66
- Array.from(selectedIds),
66
+ Array.from(selectedTracks.keys()),
67
67
  {
68
68
  id: "1",
69
69
  isAssayItem: false,
@@ -74,7 +74,7 @@ export default function TrackSelect({ store }: TrackSelectProps) {
74
74
  },
75
75
  rowById,
76
76
  );
77
- }, [selectedIds, sortedAssay]);
77
+ }, [selectedTracks, sortedAssay]);
78
78
 
79
79
  const [filteredRows, setFilteredRows] = useState(rows);
80
80
  const [filteredTreeItems, setFilteredTreeItems] = useState([
@@ -176,15 +176,23 @@ export default function TrackSelect({ store }: TrackSelectProps) {
176
176
  const handleSelection = (newSelection: GridRowSelectionModel) => {
177
177
  const idsSet =
178
178
  (newSelection && (newSelection as any).ids) ?? new Set<string>();
179
- const newActiveTracks = getActiveTracks(idsSet);
179
+
180
+ // Build a Map of only real track IDs (filter out auto-generated group IDs)
181
+ const newTracks = new Map<string, RowInfo>();
182
+ idsSet.forEach((id: string) => {
183
+ const row = rowById.get(id);
184
+ if (row) {
185
+ newTracks.set(id, row);
186
+ }
187
+ });
180
188
 
181
189
  // Block only if the new selection would exceed the limit
182
- if (newActiveTracks.size > MAX_ACTIVE) {
190
+ if (newTracks.size > MAX_ACTIVE) {
183
191
  setLimitDialogOpen(true);
184
192
  return;
185
193
  }
186
194
 
187
- setSelected(idsSet);
195
+ setSelected(newTracks);
188
196
  };
189
197
 
190
198
  return (
@@ -214,7 +222,7 @@ export default function TrackSelect({ store }: TrackSelectProps) {
214
222
  ? `${filteredRows.length} Search Results`
215
223
  : `${rows.length} Available Tracks`
216
224
  }
217
- selectedIds={selectedIds}
225
+ selectedTracks={selectedTracks}
218
226
  handleSelection={handleSelection}
219
227
  sortedAssay={sortedAssay}
220
228
  />
@@ -223,7 +231,7 @@ export default function TrackSelect({ store }: TrackSelectProps) {
223
231
  <TreeViewWrapper
224
232
  store={store}
225
233
  items={filteredTreeItems}
226
- selectedIds={selectedIds}
234
+ selectedTracks={selectedTracks}
227
235
  activeTracks={activeTracks}
228
236
  isSearchResult={isSearchResult}
229
237
  />
@@ -1,26 +1,27 @@
1
1
  import { create, StoreApi, UseBoundStore } from "zustand";
2
- import { SelectionState, SelectionAction } from "./types";
2
+ import { SelectionState, SelectionAction, RowInfo } from "./types";
3
3
 
4
4
  export type SelectionStoreInstance = UseBoundStore<
5
5
  StoreApi<SelectionState & SelectionAction>
6
6
  >;
7
7
 
8
- export function createSelectionStore() {
9
- return create<SelectionState & SelectionAction>((set) => ({
8
+ export function createSelectionStore(initialTracks?: Map<string, RowInfo>) {
9
+ return create<SelectionState & SelectionAction>((set, get) => ({
10
10
  maxTracks: 30,
11
- selectedIds: new Set<string>(),
12
- setSelected: (ids: Set<string>) =>
11
+ selectedTracks: initialTracks ? new Map(initialTracks) : new Map<string, RowInfo>(),
12
+ selectedIds: () => new Set(get().selectedTracks.keys()),
13
+ setSelected: (tracks: Map<string, RowInfo>) =>
13
14
  set(() => ({
14
- selectedIds: new Set(ids),
15
+ selectedTracks: new Map(tracks),
15
16
  })),
16
17
  removeIds: (removedIds: Set<string>) =>
17
18
  set((state) => {
18
- const next = new Set(state.selectedIds);
19
+ const next = new Map(state.selectedTracks);
19
20
  removedIds.forEach((id) => {
20
21
  next.delete(id);
21
22
  });
22
- return { selectedIds: next };
23
+ return { selectedTracks: next };
23
24
  }),
24
- clear: () => set(() => ({ selectedIds: new Set<string>() })),
25
+ clear: () => set(() => ({ selectedTracks: new Map<string, RowInfo>() })),
25
26
  }));
26
27
  }
@@ -48,6 +48,7 @@ export type RowInfo = {
48
48
  assay: string;
49
49
  experimentAccession: string;
50
50
  fileAccession: string;
51
+ url: string;
51
52
  };
52
53
 
53
54
  /**
@@ -70,7 +71,7 @@ export type ExtendedTreeItemProps = {
70
71
  export type TreeViewWrapperProps = {
71
72
  store: SelectionStoreInstance;
72
73
  items: TreeViewBaseItem<ExtendedTreeItemProps>[];
73
- selectedIds: Set<string>;
74
+ selectedTracks: Map<string, RowInfo>;
74
75
  activeTracks: Set<string>; // doesn't have the autogenerated row groupings to provide accurate number of tracks
75
76
  isSearchResult: boolean;
76
77
  };
@@ -89,15 +90,16 @@ export interface CustomTreeItemProps
89
90
  }
90
91
 
91
92
  /**
92
- * Types for useSelectionStore to keep track of selected DataGrid row ids/tracks
93
+ * Types for useSelectionStore to keep track of selected DataGrid rows/tracks
93
94
  */
94
95
  export type SelectionState = {
95
96
  maxTracks: number;
96
- selectedIds: Set<string>;
97
+ selectedTracks: Map<string, RowInfo>;
97
98
  };
98
99
 
99
100
  export type SelectionAction = {
100
- setSelected: (ids: Set<string>) => void;
101
+ selectedIds: () => Set<string>;
102
+ setSelected: (tracks: Map<string, RowInfo>) => void;
101
103
  removeIds: (removedIds: Set<string>) => void;
102
104
  clear: () => void;
103
105
  };
@@ -124,7 +126,7 @@ interface BaseTableProps extends Omit<DataGridPremiumProps, "columns"> {
124
126
 
125
127
  type DataGridWrapperProps = {
126
128
  rows: RowInfo[];
127
- selectedIds: Set<string>;
129
+ selectedTracks: Map<string, RowInfo>;
128
130
  handleSelection: (newSelection: GridRowSelectionModel) => void;
129
131
  sortedAssay: boolean;
130
132
  };