@weng-lab/genomebrowser-ui 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +7 -0
- package/dist/TrackSelect/consts.d.ts +1 -0
- package/dist/TrackSelect/types.d.ts +7 -5
- package/dist/genomebrowser-ui.es.js +584 -579
- package/dist/genomebrowser-ui.es.js.map +1 -1
- package/package.json +3 -3
- package/src/TrackSelect/DataGrid/DataGridWrapper.tsx +2 -2
- package/src/TrackSelect/DataGrid/dataGridHelpers.tsx +4 -8
- package/src/TrackSelect/TrackSelect.tsx +22 -14
- package/src/TrackSelect/store.ts +9 -8
- package/src/TrackSelect/types.ts +7 -5
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +4 -1
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weng-lab/genomebrowser-ui",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
10
|
-
"
|
|
11
|
-
"types": "dist/
|
|
10
|
+
"module": "dist/genomebrowser-ui.es.js",
|
|
11
|
+
"types": "dist/lib.d.ts",
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"fuse.js": "^7.1.0",
|
|
14
14
|
"zustand": "^5.0.8"
|
|
@@ -31,7 +31,7 @@ export function DataGridWrapper(props: DataGridProps) {
|
|
|
31
31
|
sortedAssay,
|
|
32
32
|
handleSelection,
|
|
33
33
|
rows,
|
|
34
|
-
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
45
|
+
// Derive active tracks from selectedTracks (all keys are real track IDs)
|
|
46
46
|
const activeTracks = useMemo(
|
|
47
|
-
() =>
|
|
48
|
-
[
|
|
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(
|
|
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(
|
|
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
|
-
}, [
|
|
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
|
-
|
|
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 (
|
|
190
|
+
if (newTracks.size > MAX_ACTIVE) {
|
|
183
191
|
setLimitDialogOpen(true);
|
|
184
192
|
return;
|
|
185
193
|
}
|
|
186
194
|
|
|
187
|
-
setSelected(
|
|
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
|
-
|
|
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
|
-
|
|
234
|
+
selectedTracks={selectedTracks}
|
|
227
235
|
activeTracks={activeTracks}
|
|
228
236
|
isSearchResult={isSearchResult}
|
|
229
237
|
/>
|
package/src/TrackSelect/store.ts
CHANGED
|
@@ -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
8
|
export function createSelectionStore() {
|
|
9
|
-
return create<SelectionState & SelectionAction>((set) => ({
|
|
9
|
+
return create<SelectionState & SelectionAction>((set, get) => ({
|
|
10
10
|
maxTracks: 30,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
selectedTracks: new Map<string, RowInfo>(),
|
|
12
|
+
selectedIds: () => new Set(get().selectedTracks.keys()),
|
|
13
|
+
setSelected: (tracks: Map<string, RowInfo>) =>
|
|
13
14
|
set(() => ({
|
|
14
|
-
|
|
15
|
+
selectedTracks: new Map(tracks),
|
|
15
16
|
})),
|
|
16
17
|
removeIds: (removedIds: Set<string>) =>
|
|
17
18
|
set((state) => {
|
|
18
|
-
const next = new
|
|
19
|
+
const next = new Map(state.selectedTracks);
|
|
19
20
|
removedIds.forEach((id) => {
|
|
20
21
|
next.delete(id);
|
|
21
22
|
});
|
|
22
|
-
return {
|
|
23
|
+
return { selectedTracks: next };
|
|
23
24
|
}),
|
|
24
|
-
clear: () => set(() => ({
|
|
25
|
+
clear: () => set(() => ({ selectedTracks: new Map<string, RowInfo>() })),
|
|
25
26
|
}));
|
|
26
27
|
}
|
package/src/TrackSelect/types.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
93
|
+
* Types for useSelectionStore to keep track of selected DataGrid rows/tracks
|
|
93
94
|
*/
|
|
94
95
|
export type SelectionState = {
|
|
95
96
|
maxTracks: number;
|
|
96
|
-
|
|
97
|
+
selectedTracks: Map<string, RowInfo>;
|
|
97
98
|
};
|
|
98
99
|
|
|
99
100
|
export type SelectionAction = {
|
|
100
|
-
|
|
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
|
-
|
|
129
|
+
selectedTracks: Map<string, RowInfo>;
|
|
128
130
|
handleSelection: (newSelection: GridRowSelectionModel) => void;
|
|
129
131
|
sortedAssay: boolean;
|
|
130
132
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|