@weng-lab/genomebrowser-ui 0.1.11 → 0.2.0-beta.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.
Files changed (84) hide show
  1. package/.env.local +1 -0
  2. package/dist/TrackSelect/DataGrid/DefaultGroupingCell.d.ts +6 -0
  3. package/dist/TrackSelect/FolderList/Breadcrumb.d.ts +6 -0
  4. package/dist/TrackSelect/FolderList/FolderCard.d.ts +6 -0
  5. package/dist/TrackSelect/FolderList/FolderList.d.ts +6 -0
  6. package/dist/TrackSelect/{Data/humanBiosamples.json.d.ts → Folders/biosamples/data/human.json.d.ts} +1940 -1919
  7. package/dist/TrackSelect/{Data/mouseBiosamples.json.d.ts → Folders/biosamples/data/mouse.json.d.ts} +408 -357
  8. package/dist/TrackSelect/Folders/biosamples/human.d.ts +7 -0
  9. package/dist/TrackSelect/Folders/biosamples/mouse.d.ts +7 -0
  10. package/dist/TrackSelect/Folders/biosamples/shared/AssayToggle.d.ts +14 -0
  11. package/dist/TrackSelect/Folders/biosamples/shared/BiosampleGroupingCell.d.ts +6 -0
  12. package/dist/TrackSelect/Folders/biosamples/shared/BiosampleTreeItem.d.ts +7 -0
  13. package/dist/TrackSelect/Folders/biosamples/shared/columns.d.ts +14 -0
  14. package/dist/TrackSelect/Folders/biosamples/shared/constants.d.ts +19 -0
  15. package/dist/TrackSelect/Folders/biosamples/shared/createFolder.d.ts +24 -0
  16. package/dist/TrackSelect/Folders/biosamples/shared/treeBuilder.d.ts +25 -0
  17. package/dist/TrackSelect/Folders/biosamples/shared/types.d.ts +44 -0
  18. package/dist/TrackSelect/Folders/genes/data/human.json.d.ts +10 -0
  19. package/dist/TrackSelect/Folders/genes/data/mouse.json.d.ts +10 -0
  20. package/dist/TrackSelect/Folders/genes/human.d.ts +7 -0
  21. package/dist/TrackSelect/Folders/genes/mouse.d.ts +7 -0
  22. package/dist/TrackSelect/Folders/genes/shared/columns.d.ts +14 -0
  23. package/dist/TrackSelect/Folders/genes/shared/createFolder.d.ts +12 -0
  24. package/dist/TrackSelect/Folders/genes/shared/treeBuilder.d.ts +13 -0
  25. package/dist/TrackSelect/Folders/genes/shared/types.d.ts +26 -0
  26. package/dist/TrackSelect/Folders/index.d.ts +14 -0
  27. package/dist/TrackSelect/Folders/types.d.ts +76 -0
  28. package/dist/TrackSelect/TrackSelect.d.ts +12 -5
  29. package/dist/TrackSelect/TreeView/CustomTreeItem.d.ts +3 -0
  30. package/dist/TrackSelect/TreeView/TreeViewWrapper.d.ts +1 -1
  31. package/dist/TrackSelect/store.d.ts +1 -2
  32. package/dist/TrackSelect/types.d.ts +24 -62
  33. package/dist/genomebrowser-ui.es.js +1373 -2117
  34. package/dist/genomebrowser-ui.es.js.map +1 -1
  35. package/dist/lib.d.ts +2 -2
  36. package/package.json +3 -3
  37. package/src/TrackSelect/DataGrid/DataGridWrapper.tsx +36 -20
  38. package/src/TrackSelect/DataGrid/DefaultGroupingCell.tsx +64 -0
  39. package/src/TrackSelect/FolderList/Breadcrumb.tsx +38 -0
  40. package/src/TrackSelect/FolderList/FolderCard.tsx +51 -0
  41. package/src/TrackSelect/FolderList/FolderList.tsx +47 -0
  42. package/src/TrackSelect/Folders/NEW.md +929 -0
  43. package/src/TrackSelect/{Data → Folders/biosamples/data}/formatBiosamples.go +2 -2
  44. package/src/TrackSelect/{Data/humanBiosamples.json → Folders/biosamples/data/human.json} +1940 -1919
  45. package/src/TrackSelect/{Data/mouseBiosamples.json → Folders/biosamples/data/mouse.json} +408 -357
  46. package/src/TrackSelect/Folders/biosamples/human.ts +17 -0
  47. package/src/TrackSelect/Folders/biosamples/mouse.ts +17 -0
  48. package/src/TrackSelect/Folders/biosamples/shared/AssayToggle.tsx +65 -0
  49. package/src/TrackSelect/{DataGrid/GroupingCell.tsx → Folders/biosamples/shared/BiosampleGroupingCell.tsx} +7 -5
  50. package/src/TrackSelect/Folders/biosamples/shared/BiosampleTreeItem.tsx +15 -0
  51. package/src/TrackSelect/{DataGrid → Folders/biosamples/shared}/columns.tsx +31 -17
  52. package/src/TrackSelect/Folders/biosamples/shared/constants.tsx +116 -0
  53. package/src/TrackSelect/Folders/biosamples/shared/createFolder.ts +116 -0
  54. package/src/TrackSelect/Folders/biosamples/shared/treeBuilder.ts +227 -0
  55. package/src/TrackSelect/Folders/biosamples/shared/types.ts +48 -0
  56. package/src/TrackSelect/Folders/genes/data/human.json +7 -0
  57. package/src/TrackSelect/Folders/genes/data/mouse.json +7 -0
  58. package/src/TrackSelect/Folders/genes/human.ts +16 -0
  59. package/src/TrackSelect/Folders/genes/mouse.ts +16 -0
  60. package/src/TrackSelect/Folders/genes/shared/columns.tsx +42 -0
  61. package/src/TrackSelect/Folders/genes/shared/createFolder.ts +68 -0
  62. package/src/TrackSelect/Folders/genes/shared/treeBuilder.ts +45 -0
  63. package/src/TrackSelect/Folders/genes/shared/types.ts +29 -0
  64. package/src/TrackSelect/Folders/index.ts +27 -0
  65. package/src/TrackSelect/Folders/types.ts +95 -0
  66. package/src/TrackSelect/TrackSelect.tsx +409 -311
  67. package/src/TrackSelect/TreeView/CustomTreeItem.tsx +217 -0
  68. package/src/TrackSelect/TreeView/TreeViewWrapper.tsx +47 -42
  69. package/src/TrackSelect/store.ts +103 -46
  70. package/src/TrackSelect/types.ts +28 -74
  71. package/src/lib.ts +2 -2
  72. package/test/main.tsx +113 -169
  73. package/.claude/settings.local.json +0 -7
  74. package/dist/TrackSelect/DataGrid/CustomToolbar.d.ts +0 -12
  75. package/dist/TrackSelect/DataGrid/GroupingCell.d.ts +0 -2
  76. package/dist/TrackSelect/DataGrid/columns.d.ts +0 -4
  77. package/dist/TrackSelect/DataGrid/dataGridHelpers.d.ts +0 -49
  78. package/dist/TrackSelect/TreeView/treeViewHelpers.d.ts +0 -49
  79. package/dist/TrackSelect/consts.d.ts +0 -11
  80. package/src/TrackSelect/DataGrid/CustomToolbar.tsx +0 -152
  81. package/src/TrackSelect/DataGrid/dataGridHelpers.tsx +0 -155
  82. package/src/TrackSelect/TreeView/treeViewHelpers.tsx +0 -475
  83. package/src/TrackSelect/consts.ts +0 -92
  84. package/src/TrackSelect/issues.md +0 -404
@@ -1,155 +0,0 @@
1
- import { capitalize } from "@mui/material";
2
- import Fuse, { FuseResult } from "fuse.js";
3
- import humanTracksData from "../Data/humanBiosamples.json";
4
- import mouseTracksData from "../Data/mouseBiosamples.json";
5
- import { AssayInfo, RowInfo, SearchTracksProps, TrackInfo } from "../types";
6
- import { Assembly } from "../consts";
7
-
8
- const tracksDataByAssembly: Record<Assembly, typeof humanTracksData> = {
9
- GRCh38: humanTracksData,
10
- mm10: mouseTracksData,
11
- };
12
-
13
- export function getTracksData(assembly: Assembly) {
14
- return tracksDataByAssembly[assembly];
15
- }
16
-
17
- function formatAssayType(assay: string): string {
18
- switch (assay) {
19
- case "dnase":
20
- return "DNase";
21
- case "atac":
22
- return "ATAC";
23
- case "h3k4me3":
24
- return "H3K4me3";
25
- case "h3k27ac":
26
- return "H3K27ac";
27
- case "ctcf":
28
- return "CTCF";
29
- case "chromhmm":
30
- return "ChromHMM";
31
- case "ccre":
32
- return "cCRE";
33
- case "rnaseq":
34
- return "RNA-seq";
35
- default:
36
- return assay;
37
- }
38
- }
39
-
40
- /** Convert display assay name to JSON format */
41
- function toJsonAssayType(displayName: string): string {
42
- switch (displayName.toLowerCase()) {
43
- case "dnase":
44
- return "dnase";
45
- case "atac":
46
- return "atac";
47
- case "h3k4me3":
48
- return "h3k4me3";
49
- case "h3k27ac":
50
- return "h3k27ac";
51
- case "ctcf":
52
- return "ctcf";
53
- case "chromhmm":
54
- return "chromhmm";
55
- case "ccre":
56
- return "ccre";
57
- case "rna-seq":
58
- return "rnaseq";
59
- default:
60
- return displayName.toLowerCase();
61
- }
62
- }
63
-
64
- // use to get nested data in JSON file
65
- function getNestedValue(obj: any, path: string): any {
66
- return path.split(".").reduce((acc, key) => acc && acc[key], obj);
67
- }
68
-
69
- export function getTracksByAssayAndOntology(
70
- assay: string,
71
- ontology: string,
72
- tracksData: ReturnType<typeof getTracksData>,
73
- ): TrackInfo[] {
74
- let res: TrackInfo[] = [];
75
- const data = getNestedValue(tracksData, "tracks");
76
- const jsonAssay = toJsonAssayType(assay);
77
-
78
- data.forEach((track: TrackInfo) => {
79
- const filteredAssays =
80
- track.assays?.filter((e: AssayInfo) => e.assay === jsonAssay) || [];
81
- if (
82
- filteredAssays.length > 0 &&
83
- track.ontology === ontology.toLowerCase()
84
- ) {
85
- res.push({
86
- ...track,
87
- assays: filteredAssays,
88
- });
89
- }
90
- });
91
- return res;
92
- }
93
-
94
- /** Flatten TrackInfo into RowInfo objects for DataGrid display.
95
- * @param track TrackInfo object containing information from JSON file
96
- * @returns Array of flattened RowInfo objects, one per assay
97
- */
98
- export function flattenIntoRows(track: TrackInfo): RowInfo[] {
99
- const { ontology, lifeStage, sampleType, displayname } = track;
100
-
101
- return track.assays.map(
102
- ({ id, assay, experimentAccession, fileAccession, url }) => ({
103
- id,
104
- ontology: capitalize(ontology),
105
- lifeStage: capitalize(lifeStage),
106
- sampleType: capitalize(sampleType),
107
- displayname: capitalize(displayname),
108
- assay: formatAssayType(assay),
109
- experimentAccession,
110
- fileAccession,
111
- url,
112
- }),
113
- );
114
- }
115
-
116
- /**
117
- * Fuzzy search in tracks stored in a JSON file.
118
- *
119
- * @param jsonStructure - Dot-separated path to the data array in the JSON structure.
120
- * @param query - The search query string.
121
- * @param keyWeightMap - Array of keys to search within each track object.
122
- * Can look like ["name", "author"] or if weighted, [
123
- {
124
- name: 'title',
125
- weight: 0.3
126
- },
127
- {
128
- name: 'author',
129
- weight: 0.7
130
- }
131
- ].
132
- * @param threshold - (Optional) Threshold for the fuzzy search (default is 0.5).
133
- * Smaller = stricter match, larger = fuzzier since 0 is perfect match and 1 is worst match.
134
- * @param limit - (Optional) Maximum number of results to return (default is 10).
135
- * @returns FuseResult object containing the search results.
136
- */
137
- export function searchTracks({
138
- jsonStructure,
139
- query,
140
- keyWeightMap,
141
- threshold = 0.75,
142
- tracksData,
143
- }: SearchTracksProps & {
144
- tracksData: ReturnType<typeof getTracksData>;
145
- }): FuseResult<TrackInfo>[] {
146
- const data = getNestedValue(tracksData, jsonStructure ?? "");
147
-
148
- const fuse = new Fuse(data, {
149
- includeScore: true,
150
- shouldSort: true,
151
- threshold: threshold,
152
- keys: keyWeightMap,
153
- });
154
- return fuse.search(query);
155
- }
@@ -1,475 +0,0 @@
1
- import Folder from "@mui/icons-material/Folder";
2
- import IndeterminateCheckBoxRoundedIcon from "@mui/icons-material/IndeterminateCheckBoxRounded";
3
- import { Box, Typography, Stack } from "@mui/material";
4
- import Collapse from "@mui/material/Collapse";
5
- import { alpha, styled } from "@mui/material/styles";
6
- import { TreeViewBaseItem } from "@mui/x-tree-view";
7
- import {
8
- TreeItemCheckbox,
9
- TreeItemIconContainer,
10
- TreeItemLabel,
11
- } from "@mui/x-tree-view/TreeItem";
12
- import { TreeItemIcon } from "@mui/x-tree-view/TreeItemIcon";
13
- import { TreeItemProvider } from "@mui/x-tree-view/TreeItemProvider";
14
- import { useTreeItemModel } from "@mui/x-tree-view/hooks";
15
- import { useTreeItem } from "@mui/x-tree-view/useTreeItem";
16
- import React from "react";
17
- import {
18
- CustomLabelProps,
19
- CustomTreeItemProps,
20
- ExtendedTreeItemProps,
21
- RowInfo,
22
- } from "../types";
23
- import Fuse, { FuseResult } from "fuse.js";
24
- import { SearchTracksProps } from "../types";
25
- import { assayTypes, ontologyTypes } from "../consts";
26
-
27
- /** Format an ID like "h3k27ac-ENCFF922YMQ" to "H3K27ac - ENCFF922YMQ" */
28
- function formatIdLabel(id: string): string {
29
- const hyphenIndex = id.indexOf("-");
30
- if (hyphenIndex === -1) return id;
31
-
32
- const assayPart = id.substring(0, hyphenIndex);
33
- let accessionPart = id.substring(hyphenIndex + 1);
34
-
35
- // Truncate accession parts to 15 characters
36
- if (accessionPart.length > 25)
37
- accessionPart = accessionPart.substring(0, 15) + "…";
38
-
39
- const formattedAssay = formatAssayName(assayPart);
40
- return `${formattedAssay} - ${accessionPart}`;
41
- }
42
-
43
- function formatAssayName(assay: string): string {
44
- switch (assay.toLowerCase()) {
45
- case "dnase":
46
- return "DNase";
47
- case "atac":
48
- return "ATAC";
49
- case "h3k4me3":
50
- return "H3K4me3";
51
- case "h3k27ac":
52
- return "H3K27ac";
53
- case "ctcf":
54
- return "CTCF";
55
- case "chromhmm":
56
- return "ChromHMM";
57
- case "ccre":
58
- return "cCRE";
59
- case "rnaseq":
60
- return "RNA-seq";
61
- default:
62
- return assay;
63
- }
64
- }
65
-
66
- /**
67
- * Builds tree in the sorted by assay view
68
- * @param selectedIds: list of ids (from useSelectionStore)
69
- * @param root: Root TreeViewBaseItem
70
- * @param rowById: Mapping between an id (experimentAccession) and its RowInfo object
71
- * @returns all of the items for the RichTreeView in TreeViewWrapper
72
- */
73
- export function buildSortedAssayTreeView(
74
- selectedIds: string[],
75
- root: TreeViewBaseItem<ExtendedTreeItemProps>,
76
- rowById: Map<string, RowInfo>,
77
- ): TreeViewBaseItem<ExtendedTreeItemProps>[] {
78
- const assayMap = new Map<string, TreeViewBaseItem<ExtendedTreeItemProps>>(); // keep track of top level nodes
79
- const ontologyMap = new Map<
80
- string,
81
- TreeViewBaseItem<ExtendedTreeItemProps>
82
- >();
83
- const sampleAssayMap = new Map<
84
- string,
85
- TreeViewBaseItem<ExtendedTreeItemProps>
86
- >();
87
- let idx = 1;
88
-
89
- const selectedRows = selectedIds.reduce<RowInfo[]>((acc, id) => {
90
- const row = rowById.get(id);
91
- if (row) acc.push(row);
92
- return acc;
93
- }, []);
94
-
95
- selectedRows.forEach((row) => {
96
- let assayNode = assayMap.get(row.assay);
97
- if (!assayNode) {
98
- assayNode = {
99
- id: row.assay,
100
- isAssayItem: true,
101
- label: row.assay,
102
- icon: "removeable",
103
- children: [],
104
- allExpAccessions: [],
105
- };
106
- assayMap.set(row.assay, assayNode);
107
- root.children!.push(assayNode);
108
- }
109
-
110
- let ontologyNode = ontologyMap.get(row.ontology + row.assay);
111
- if (!ontologyNode) {
112
- ontologyNode = {
113
- id: row.ontology + "_" + idx++,
114
- isAssayItem: false,
115
- label: row.ontology,
116
- icon: "removeable",
117
- children: [],
118
- allExpAccessions: [],
119
- };
120
- assayNode.children!.push(ontologyNode);
121
- ontologyMap.set(row.ontology + row.assay, ontologyNode);
122
- }
123
-
124
- const displayNameNode: TreeViewBaseItem<ExtendedTreeItemProps> = {
125
- id: row.displayname + "_" + idx++,
126
- isAssayItem: false,
127
- label: row.displayname,
128
- icon: "removeable",
129
- children: [],
130
- allExpAccessions: [],
131
- };
132
- ontologyNode.children!.push(displayNameNode);
133
-
134
- let expNode = sampleAssayMap.get(row.displayname + row.id);
135
- if (!expNode) {
136
- expNode = {
137
- id: row.id,
138
- isAssayItem: false,
139
- label: formatIdLabel(row.id),
140
- icon: "removeable",
141
- assayName: row.assay,
142
- children: [],
143
- allExpAccessions: [row.id],
144
- };
145
- sampleAssayMap.set(row.displayname + row.assay, expNode);
146
- displayNameNode.children!.push(expNode);
147
- }
148
- assayNode.allExpAccessions!.push(row.id);
149
- ontologyNode.allExpAccessions!.push(row.id);
150
- displayNameNode.allExpAccessions!.push(row.id);
151
- root.allRowInfo!.push(row);
152
- });
153
- // standardize the order of the assay folders everytime one is added
154
- root.children!.sort((a, b): number => {
155
- return assayTypes.indexOf(a.id) - assayTypes.indexOf(b.id);
156
- });
157
- return [root];
158
- }
159
-
160
- /**
161
- * Builds tree in the sorted by assay view
162
- * @param selectedIds: list of ids (from useSelectionStore)
163
- * @param root: Root TreeViewBaseItem
164
- * @param rowById: Mapping between an id (experimentAccession) and its RowInfo object
165
- * @returns all of the items for the RichTreeView in TreeViewWrapper
166
- */
167
- export function buildTreeView(
168
- selectedIds: string[],
169
- root: TreeViewBaseItem<ExtendedTreeItemProps>,
170
- rowById: Map<string, RowInfo>,
171
- ): TreeViewBaseItem<ExtendedTreeItemProps>[] {
172
- const ontologyMap = new Map<
173
- string,
174
- TreeViewBaseItem<ExtendedTreeItemProps>
175
- >(); // keep track of top level nodes
176
- const displayNameMap = new Map<
177
- string,
178
- TreeViewBaseItem<ExtendedTreeItemProps>
179
- >();
180
- const sampleAssayMap = new Map<
181
- string,
182
- TreeViewBaseItem<ExtendedTreeItemProps>
183
- >();
184
-
185
- const selectedRows = selectedIds.reduce<RowInfo[]>((acc, id) => {
186
- const row = rowById.get(id);
187
- if (row) acc.push(row);
188
- return acc;
189
- }, []);
190
-
191
- selectedRows.forEach((row) => {
192
- if (!row) {
193
- return;
194
- }
195
- let ontologyNode = ontologyMap.get(row.ontology);
196
- if (!ontologyNode) {
197
- ontologyNode = {
198
- id: row.ontology,
199
- label: row.ontology,
200
- icon: "removeable",
201
- children: [],
202
- allExpAccessions: [],
203
- };
204
- ontologyMap.set(row.ontology, ontologyNode);
205
- root.children!.push(ontologyNode);
206
- }
207
-
208
- let displayNameNode = displayNameMap.get(row.displayname);
209
- if (!displayNameNode) {
210
- displayNameNode = {
211
- id: row.displayname,
212
- label: row.displayname,
213
- icon: "removeable",
214
- children: [],
215
- allExpAccessions: [],
216
- };
217
- ontologyNode.children!.push(displayNameNode);
218
- displayNameMap.set(row.displayname, displayNameNode);
219
- }
220
-
221
- let expNode = sampleAssayMap.get(row.displayname + row.assay);
222
- if (!expNode) {
223
- expNode = {
224
- id: row.id,
225
- label: formatIdLabel(row.id),
226
- icon: "removeable",
227
- assayName: row.assay,
228
- children: [],
229
- allExpAccessions: [row.id],
230
- };
231
- sampleAssayMap.set(row.displayname + row.assay, expNode);
232
- displayNameNode.children!.push(expNode);
233
- }
234
- ontologyNode.allExpAccessions!.push(row.id);
235
- displayNameNode.allExpAccessions!.push(row.id);
236
- root.allRowInfo!.push(row);
237
- });
238
- // standardize the order of the assay folders everytime one is added
239
- root.children!.sort((a, b): number => {
240
- return ontologyTypes.indexOf(a.id) - ontologyTypes.indexOf(b.id);
241
- });
242
- return [root];
243
- }
244
-
245
- /**
246
- * Fuzzy search of active tracks.
247
- *
248
- * @param treeItems - TreeBaseViewItems from the tree.
249
- * @param query - The search query string.
250
- * @param keyWeightMap - Array of keys to search within each track object.
251
- * Can look like ["name", "author"] or if weighted, [
252
- {
253
- name: 'title',
254
- weight: 0.3
255
- },
256
- {
257
- name: 'author',
258
- weight: 0.7
259
- }
260
- ].
261
- * @param threshold - (Optional) Threshold for the fuzzy search (default is 0.5).
262
- * Smaller = stricter match, larger = fuzzier since 0 is perfect match and 1 is worst match.
263
- * @param limit - (Optional) Maximum number of results to return (default is 10).
264
- * @returns FuseResult object containing the search results.
265
- */
266
- export function searchTreeItems({
267
- treeItems,
268
- query,
269
- keyWeightMap,
270
- threshold,
271
- limit = 10,
272
- }: SearchTracksProps): FuseResult<RowInfo>[] {
273
- const data = treeItems![0].allRowInfo ?? [];
274
- const fuse = new Fuse(data, {
275
- includeScore: true,
276
- shouldSort: true,
277
- threshold: threshold,
278
- keys: keyWeightMap,
279
- });
280
- return fuse.search(query, { limit: limit });
281
- }
282
-
283
- /**
284
- * Creates the assay icon for DataGrid and RichTreeView
285
- * @param type: assay type
286
- * @returns an icon of the assay's respective color
287
- */
288
- export function AssayIcon(type: string) {
289
- const colorMap: { [key: string]: string } = {
290
- DNase: "#06da93",
291
- ATAC: "#02c7b9",
292
- H3K4me3: "#ff2020",
293
- ChromHMM: "#0097a7",
294
- H3K27ac: "#fdc401",
295
- CTCF: "#01a6f1",
296
- cCRE: "#8b5cf6",
297
- "RNA-seq": "#f97316",
298
- };
299
- const color = colorMap[type];
300
- return (
301
- <Box
302
- sx={{
303
- width: 12,
304
- height: 12,
305
- borderRadius: "20%",
306
- bgcolor: color,
307
- }}
308
- />
309
- );
310
- }
311
-
312
- // Everything below is styling for the custom directory look of the tree view
313
- const TreeItemRoot = styled("li")(({ theme }) => ({
314
- listStyle: "none",
315
- margin: 0,
316
- padding: 0,
317
- outline: 4,
318
- color: theme.palette.grey[400],
319
- ...theme.applyStyles("light", {
320
- color: theme.palette.grey[600], // controls colors of the MUI icons
321
- }),
322
- }));
323
-
324
- const TreeItemLabelText = styled(Typography)({
325
- color: "black",
326
- fontFamily: "inherit",
327
- });
328
-
329
- function CustomLabel({
330
- icon: Icon,
331
- children,
332
- isAssayItem,
333
- assayName,
334
- ...other
335
- }: CustomLabelProps) {
336
- const variant = isAssayItem ? "subtitle2" : "body2";
337
- const fontWeight = isAssayItem ? "bold" : 500;
338
- return (
339
- <TreeItemLabel
340
- {...other}
341
- sx={{
342
- display: "flex",
343
- alignItems: "center",
344
- }}
345
- >
346
- {Icon && React.isValidElement(Icon) ? (
347
- <Box className="labelIcon" sx={{ mr: 1 }}>
348
- {Icon}
349
- </Box>
350
- ) : (
351
- <Box
352
- component={Icon as React.ElementType}
353
- className="labelIcon"
354
- color="inherit"
355
- sx={{ mr: 1, fontSize: "1.2rem" }}
356
- />
357
- )}
358
- <Stack direction="row" spacing={1} alignItems="center">
359
- {isAssayItem && AssayIcon(other.id)}
360
- {assayName && AssayIcon(assayName)}
361
- <TreeItemLabelText fontWeight={fontWeight} variant={variant}>
362
- {children}
363
- </TreeItemLabelText>
364
- </Stack>
365
- </TreeItemLabel>
366
- );
367
- }
368
-
369
- const TreeItemContent = styled("div")(({ theme }) => ({
370
- padding: theme.spacing(0.5),
371
- paddingRight: theme.spacing(2),
372
- paddingLeft: `calc(${theme.spacing(1)} + var(--TreeView-itemChildrenIndentation) * var(--TreeView-itemDepth))`,
373
- width: "100%",
374
- boxSizing: "border-box", // prevent width + padding to overflow
375
- position: "relative",
376
- display: "flex",
377
- alignItems: "center",
378
- gap: theme.spacing(1),
379
- cursor: "pointer",
380
- WebkitTapHighlightColor: "transparent",
381
- flexDirection: "row-reverse",
382
- borderRadius: theme.spacing(0.7),
383
- marginBottom: theme.spacing(0.5),
384
- marginTop: theme.spacing(0.5),
385
- fontWeight: 500,
386
- "&:hover": {
387
- backgroundColor: alpha(theme.palette.primary.main, 0.1),
388
- color: "white",
389
- ...theme.applyStyles("light", {
390
- color: theme.palette.primary.main,
391
- }),
392
- },
393
- }));
394
-
395
- const getIconFromTreeItemType = (itemType: string) => {
396
- switch (itemType) {
397
- case "folder":
398
- return Folder;
399
- case "removeable":
400
- return IndeterminateCheckBoxRoundedIcon;
401
- default:
402
- return AssayIcon(itemType);
403
- }
404
- };
405
-
406
- export const CustomTreeItem = React.forwardRef(function CustomTreeItem(
407
- props: CustomTreeItemProps,
408
- ref: React.Ref<HTMLLIElement>,
409
- ) {
410
- const { id, itemId, label, disabled, children, onRemove, ...other } = props;
411
-
412
- const {
413
- getContextProviderProps,
414
- getRootProps,
415
- getContentProps,
416
- getIconContainerProps,
417
- getCheckboxProps,
418
- getLabelProps,
419
- getGroupTransitionProps,
420
- status,
421
- } = useTreeItem({ id, itemId, children, label, disabled, rootRef: ref });
422
-
423
- const item = useTreeItemModel<ExtendedTreeItemProps>(itemId)!;
424
- const icon = getIconFromTreeItemType(item.icon);
425
-
426
- const handleRemoveIconClick = (e: React.MouseEvent) => {
427
- e.stopPropagation(); // prevent item expand/select
428
- onRemove?.(item);
429
- };
430
-
431
- return (
432
- <TreeItemProvider {...getContextProviderProps()}>
433
- <TreeItemRoot {...getRootProps(other)}>
434
- <TreeItemContent {...getContentProps()}>
435
- <TreeItemIconContainer {...getIconContainerProps()}>
436
- <TreeItemIcon status={status} />
437
- </TreeItemIconContainer>
438
- <TreeItemCheckbox {...getCheckboxProps()} />
439
- <CustomLabel
440
- {...getLabelProps({
441
- icon:
442
- item.icon === "removeable" ? (
443
- <Box
444
- onClick={handleRemoveIconClick}
445
- sx={{
446
- width: 20,
447
- height: 20,
448
- display: "flex",
449
- alignItems: "center",
450
- justifyContent: "center",
451
- borderRadius: "4px",
452
- cursor: "pointer",
453
- mr: 1,
454
- "&:hover": {
455
- backgroundColor: "rgba(0,0,0,0.1)",
456
- },
457
- }}
458
- >
459
- <IndeterminateCheckBoxRoundedIcon fontSize="small" />
460
- </Box>
461
- ) : (
462
- icon
463
- ),
464
- expandable: (status.expandable && status.expanded).toString(),
465
- isAssayItem: item.isAssayItem,
466
- assayName: item.assayName,
467
- id: item.id,
468
- })}
469
- />
470
- </TreeItemContent>
471
- {children && <Collapse {...getGroupTransitionProps()} />}
472
- </TreeItemRoot>
473
- </TreeItemProvider>
474
- );
475
- });
@@ -1,92 +0,0 @@
1
- import {
2
- getTracksByAssayAndOntology,
3
- flattenIntoRows,
4
- getTracksData,
5
- } from "./DataGrid/dataGridHelpers";
6
- import { RowInfo, TrackInfo } from "./types";
7
-
8
- export type Assembly = "GRCh38" | "mm10";
9
-
10
- export const assayTypes = [
11
- "cCRE",
12
- "DNase",
13
- "H3K4me3",
14
- "H3K27ac",
15
- "ATAC",
16
- "CTCF",
17
- "RNA-seq",
18
- "ChromHMM",
19
- ];
20
-
21
- export const ontologyTypes = [
22
- "Adipose",
23
- "Adrenal gland",
24
- "Blood",
25
- "Blood vessel",
26
- "Bone",
27
- "Bone marrow",
28
- "Brain",
29
- "Breast",
30
- "Connective tissue",
31
- "Embryo",
32
- "Epithelium",
33
- "Esophagus",
34
- "Eye",
35
- "Fallopian Tube",
36
- "Gallbladder",
37
- "Heart",
38
- "Kidney",
39
- "Large Intestine",
40
- "Limb",
41
- "Liver",
42
- "Lung",
43
- "Lymphoid Tissue",
44
- "Muscle",
45
- "Mouth",
46
- "Nerve",
47
- "Nose",
48
- "Pancreas",
49
- "Parathyroid Gland",
50
- "Ovary",
51
- "Penis",
52
- "Placenta",
53
- "Prostate",
54
- "Skin",
55
- "Small Intestine",
56
- "Spinal Cord",
57
- "Spleen",
58
- "Stomach",
59
- "Testis",
60
- "Thymus",
61
- "Thyroid",
62
- "Urinary Bladder",
63
- "Uterus",
64
- "Vagina",
65
- ];
66
-
67
- /**
68
- * Build rows and rowById for a specific assembly
69
- */
70
- export function buildRowsForAssembly(assembly: Assembly): {
71
- rows: RowInfo[];
72
- rowById: Map<string, RowInfo>;
73
- } {
74
- const tracksData = getTracksData(assembly);
75
- const rows = ontologyTypes.flatMap((ontology) =>
76
- assayTypes.flatMap((assay) =>
77
- getTracksByAssayAndOntology(
78
- assay.toLowerCase(),
79
- ontology.toLowerCase(),
80
- tracksData,
81
- ).flatMap((r: TrackInfo) =>
82
- flattenIntoRows(r).map((flat) => ({
83
- ...flat,
84
- assay,
85
- ontology,
86
- })),
87
- ),
88
- ),
89
- );
90
- const rowById = new Map<string, RowInfo>(rows.map((r) => [r.id, r]));
91
- return { rows, rowById };
92
- }