@weng-lab/genomebrowser-ui 0.3.6 → 0.4.0-beta.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.
- package/.env.local +1 -1
- package/dist/TrackSelect/Folders/biosamples/shared/BiosampleViewSelector.d.ts +7 -0
- package/dist/TrackSelect/Folders/biosamples/shared/createFolder.d.ts +1 -13
- package/dist/TrackSelect/Folders/biosamples/shared/toTrack.d.ts +20 -0
- package/dist/TrackSelect/Folders/biosamples/shared/types.d.ts +4 -13
- package/dist/TrackSelect/Folders/genes/shared/columns.d.ts +2 -2
- package/dist/TrackSelect/Folders/genes/shared/createFolder.d.ts +1 -3
- package/dist/TrackSelect/Folders/genes/shared/toTrack.d.ts +18 -0
- package/dist/TrackSelect/Folders/genes/shared/types.d.ts +2 -0
- package/dist/TrackSelect/Folders/index.d.ts +6 -12
- package/dist/TrackSelect/Folders/mohd/data/human.json.d.ts +2948 -0
- package/dist/TrackSelect/Folders/mohd/human.d.ts +1 -0
- package/dist/TrackSelect/Folders/mohd/shared/MohdGroupingCell.d.ts +2 -0
- package/dist/TrackSelect/Folders/mohd/shared/MohdTreeItem.d.ts +3 -0
- package/dist/TrackSelect/Folders/mohd/shared/MohdViewSelector.d.ts +7 -0
- package/dist/TrackSelect/Folders/mohd/shared/columns.d.ts +5 -0
- package/dist/TrackSelect/Folders/mohd/shared/config.d.ts +42 -0
- package/dist/TrackSelect/Folders/mohd/shared/createFolder.d.ts +9 -0
- package/dist/TrackSelect/Folders/mohd/shared/toTrack.d.ts +9 -0
- package/dist/TrackSelect/Folders/mohd/shared/types.d.ts +40 -0
- package/dist/TrackSelect/Folders/other-tracks/shared/toTrack.d.ts +5 -0
- package/dist/TrackSelect/Folders/other-tracks/shared/types.d.ts +1 -0
- package/dist/TrackSelect/Folders/types.d.ts +23 -55
- package/dist/TrackSelect/TrackSelect.d.ts +10 -7
- package/dist/TrackSelect/TreeView/TreeViewWrapper.d.ts +1 -1
- package/dist/TrackSelect/buildSelectedTree.d.ts +15 -0
- package/dist/TrackSelect/managedTracks.d.ts +13 -0
- package/dist/TrackSelect/resolveFolderView.d.ts +2 -0
- package/dist/TrackSelect/trackContext.d.ts +5 -0
- package/dist/TrackSelect/types.d.ts +12 -33
- package/dist/genomebrowser-ui.es.js +2231 -1732
- package/dist/genomebrowser-ui.es.js.map +1 -1
- package/dist/lib.d.ts +4 -4
- package/dist/muiLicense.d.ts +1 -0
- package/package.json +6 -3
- package/src/TrackSelect/Dialogs/ClearDialog.tsx +3 -8
- package/src/TrackSelect/Dialogs/ResetDialog.tsx +5 -4
- package/src/TrackSelect/FolderList/FolderCard.tsx +1 -1
- package/src/TrackSelect/Folders/biosamples/shared/BiosampleViewSelector.tsx +33 -0
- package/src/TrackSelect/Folders/biosamples/shared/createFolder.ts +39 -58
- package/src/TrackSelect/Folders/biosamples/shared/toTrack.ts +138 -0
- package/src/TrackSelect/Folders/biosamples/shared/types.ts +4 -16
- package/src/TrackSelect/Folders/genes/shared/columns.tsx +2 -2
- package/src/TrackSelect/Folders/genes/shared/createFolder.ts +11 -31
- package/src/TrackSelect/Folders/genes/shared/toTrack.ts +59 -0
- package/src/TrackSelect/Folders/genes/shared/types.ts +2 -0
- package/src/TrackSelect/Folders/index.ts +14 -17
- package/src/TrackSelect/Folders/mohd/data/human.json +2945 -0
- package/src/TrackSelect/Folders/mohd/human.ts +10 -0
- package/src/TrackSelect/Folders/mohd/shared/MohdGroupingCell.tsx +68 -0
- package/src/TrackSelect/Folders/mohd/shared/MohdTreeItem.tsx +17 -0
- package/src/TrackSelect/Folders/mohd/shared/MohdViewSelector.tsx +33 -0
- package/src/TrackSelect/Folders/mohd/shared/columns.tsx +79 -0
- package/src/TrackSelect/Folders/mohd/shared/config.tsx +71 -0
- package/src/TrackSelect/Folders/mohd/shared/createFolder.ts +144 -0
- package/src/TrackSelect/Folders/mohd/shared/toTrack.ts +164 -0
- package/src/TrackSelect/Folders/mohd/shared/types.ts +46 -0
- package/src/TrackSelect/Folders/other-tracks/shared/createFolder.ts +13 -14
- package/src/TrackSelect/Folders/other-tracks/shared/toTrack.ts +17 -0
- package/src/TrackSelect/Folders/other-tracks/shared/types.ts +1 -0
- package/src/TrackSelect/Folders/types.ts +26 -69
- package/src/TrackSelect/TrackSelect.tsx +301 -257
- package/src/TrackSelect/TreeView/CustomTreeItem.tsx +9 -9
- package/src/TrackSelect/TreeView/TreeViewWrapper.tsx +84 -6
- package/src/TrackSelect/buildSelectedTree.ts +145 -0
- package/src/TrackSelect/managedTracks.ts +92 -0
- package/src/TrackSelect/resolveFolderView.ts +20 -0
- package/src/TrackSelect/trackContext.ts +9 -0
- package/src/TrackSelect/types.ts +14 -39
- package/src/lib.ts +13 -7
- package/src/muiLicense.ts +9 -0
- package/src/vite-env.d.ts +9 -0
- package/test/TrackSelect.test.tsx +435 -0
- package/test/main.tsx +36 -352
- package/test/mocks/logo-test.tsx +11 -0
- package/test/mohdDisplay.test.tsx +45 -0
- package/test/startup.test.ts +206 -0
- package/test/trackSelectState.test.ts +176 -0
- package/vite.config.ts +1 -0
- package/vitest.config.ts +20 -0
- package/dist/TrackSelect/Folders/biosamples/shared/AssayToggle.d.ts +0 -18
- package/dist/TrackSelect/Folders/biosamples/shared/treeBuilder.d.ts +0 -28
- package/dist/TrackSelect/Folders/genes/shared/treeBuilder.d.ts +0 -13
- package/dist/TrackSelect/Folders/other-tracks/shared/treeBuilder.d.ts +0 -4
- package/dist/TrackSelect/store.d.ts +0 -4
- package/src/TrackSelect/Folders/NEW.md +0 -929
- package/src/TrackSelect/Folders/biosamples/shared/AssayToggle.tsx +0 -78
- package/src/TrackSelect/Folders/biosamples/shared/treeBuilder.ts +0 -224
- package/src/TrackSelect/Folders/genes/shared/treeBuilder.ts +0 -45
- package/src/TrackSelect/Folders/other-tracks/shared/treeBuilder.ts +0 -34
- package/src/TrackSelect/store.ts +0 -117
|
@@ -30,19 +30,19 @@ const TreeItemRoot = styled("li")(({ theme }) => ({
|
|
|
30
30
|
}),
|
|
31
31
|
}));
|
|
32
32
|
|
|
33
|
-
const TreeItemLabelText = styled(Typography)({
|
|
34
|
-
color:
|
|
33
|
+
const TreeItemLabelText = styled(Typography)(({ theme }) => ({
|
|
34
|
+
color: theme.palette.text.primary,
|
|
35
35
|
fontFamily: "inherit",
|
|
36
36
|
overflow: "hidden",
|
|
37
37
|
textOverflow: "ellipsis",
|
|
38
38
|
whiteSpace: "nowrap",
|
|
39
|
-
});
|
|
39
|
+
}));
|
|
40
40
|
|
|
41
41
|
function CustomLabel({
|
|
42
42
|
icon: Icon,
|
|
43
43
|
children,
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
isHighlightedItem,
|
|
45
|
+
highlightName,
|
|
46
46
|
renderIcon,
|
|
47
47
|
...other
|
|
48
48
|
}: CustomLabelProps) {
|
|
@@ -78,8 +78,8 @@ function CustomLabel({
|
|
|
78
78
|
alignItems="center"
|
|
79
79
|
sx={{ minWidth: 0, overflow: "hidden", flex: 1 }}
|
|
80
80
|
>
|
|
81
|
-
{
|
|
82
|
-
<Box sx={{ flexShrink: 0 }}>{renderIcon(
|
|
81
|
+
{isHighlightedItem && highlightName && renderIcon && (
|
|
82
|
+
<Box sx={{ flexShrink: 0 }}>{renderIcon(highlightName)}</Box>
|
|
83
83
|
)}
|
|
84
84
|
<Tooltip title={labelText} enterDelay={500} placement="top">
|
|
85
85
|
<TreeItemLabelText fontWeight={fontWeight} variant={variant}>
|
|
@@ -199,8 +199,8 @@ export const CustomTreeItem = React.forwardRef(function CustomTreeItem(
|
|
|
199
199
|
icon
|
|
200
200
|
),
|
|
201
201
|
expandable: (status.expandable && status.expanded).toString(),
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
isHighlightedItem: item.isHighlightedItem,
|
|
203
|
+
highlightName: item.highlightName,
|
|
204
204
|
id: item.id,
|
|
205
205
|
renderIcon,
|
|
206
206
|
})}
|
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
import { Avatar, Box, Paper, Typography } from "@mui/material";
|
|
2
2
|
import { RichTreeView, TreeViewBaseItem } from "@mui/x-tree-view";
|
|
3
|
-
import {
|
|
3
|
+
import { useTreeItemModel } from "@mui/x-tree-view/hooks";
|
|
4
|
+
import React, { useEffect, useMemo, useState } from "react";
|
|
5
|
+
import { buildSelectedTree } from "../buildSelectedTree";
|
|
4
6
|
import {
|
|
5
7
|
CustomTreeItemProps,
|
|
6
8
|
ExtendedTreeItemProps,
|
|
7
|
-
FolderTreeConfig,
|
|
8
9
|
TreeViewWrapperProps,
|
|
9
10
|
} from "../types";
|
|
11
|
+
import { resolveFolderView } from "../resolveFolderView";
|
|
10
12
|
import { CustomTreeItem } from "./CustomTreeItem";
|
|
11
13
|
|
|
14
|
+
const attachFolderId = (
|
|
15
|
+
items: TreeViewBaseItem<ExtendedTreeItemProps>[],
|
|
16
|
+
folderId: string,
|
|
17
|
+
): TreeViewBaseItem<ExtendedTreeItemProps>[] => {
|
|
18
|
+
return items.map((item) => ({
|
|
19
|
+
...item,
|
|
20
|
+
folderId,
|
|
21
|
+
children: item.children
|
|
22
|
+
? attachFolderId(item.children, folderId)
|
|
23
|
+
: undefined,
|
|
24
|
+
}));
|
|
25
|
+
};
|
|
26
|
+
|
|
12
27
|
function getAllExpandableItemIds(
|
|
13
28
|
items: TreeViewBaseItem<ExtendedTreeItemProps>[],
|
|
14
29
|
): string[] {
|
|
@@ -24,11 +39,17 @@ function getAllExpandableItemIds(
|
|
|
24
39
|
|
|
25
40
|
function FolderTree({
|
|
26
41
|
items,
|
|
42
|
+
LeafTreeItemComponent,
|
|
27
43
|
TreeItemComponent,
|
|
28
44
|
onRemove,
|
|
29
45
|
}: {
|
|
30
|
-
items:
|
|
31
|
-
|
|
46
|
+
items: TreeViewBaseItem<ExtendedTreeItemProps>[];
|
|
47
|
+
LeafTreeItemComponent?: React.ForwardRefExoticComponent<
|
|
48
|
+
CustomTreeItemProps & React.RefAttributes<HTMLLIElement>
|
|
49
|
+
>;
|
|
50
|
+
TreeItemComponent?: React.ForwardRefExoticComponent<
|
|
51
|
+
CustomTreeItemProps & React.RefAttributes<HTMLLIElement>
|
|
52
|
+
>;
|
|
32
53
|
onRemove: (item: TreeViewBaseItem<ExtendedTreeItemProps>) => void;
|
|
33
54
|
}) {
|
|
34
55
|
const allExpandableIds = useMemo(
|
|
@@ -54,7 +75,27 @@ function FolderTree({
|
|
|
54
75
|
onRemove(item);
|
|
55
76
|
};
|
|
56
77
|
|
|
57
|
-
const TreeItem =
|
|
78
|
+
const TreeItem = useMemo(
|
|
79
|
+
() =>
|
|
80
|
+
TreeItemComponent || LeafTreeItemComponent
|
|
81
|
+
? React.forwardRef<HTMLLIElement, CustomTreeItemProps>(
|
|
82
|
+
function TreeItem(props, ref) {
|
|
83
|
+
const item = useTreeItemModel<ExtendedTreeItemProps>(
|
|
84
|
+
props.itemId,
|
|
85
|
+
);
|
|
86
|
+
const Component =
|
|
87
|
+
item?.kind === "leaf"
|
|
88
|
+
? (LeafTreeItemComponent ??
|
|
89
|
+
TreeItemComponent ??
|
|
90
|
+
CustomTreeItem)
|
|
91
|
+
: (TreeItemComponent ?? CustomTreeItem);
|
|
92
|
+
|
|
93
|
+
return <Component {...props} ref={ref} />;
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
: CustomTreeItem,
|
|
97
|
+
[LeafTreeItemComponent, TreeItemComponent],
|
|
98
|
+
);
|
|
58
99
|
|
|
59
100
|
return (
|
|
60
101
|
<RichTreeView
|
|
@@ -80,10 +121,46 @@ function FolderTree({
|
|
|
80
121
|
}
|
|
81
122
|
|
|
82
123
|
export function TreeViewWrapper({
|
|
83
|
-
|
|
124
|
+
folders,
|
|
125
|
+
selectedByFolder,
|
|
126
|
+
activeViewIdByFolder,
|
|
84
127
|
selectedCount,
|
|
85
128
|
onRemove,
|
|
86
129
|
}: TreeViewWrapperProps) {
|
|
130
|
+
const folderTrees = useMemo(
|
|
131
|
+
() =>
|
|
132
|
+
folders.flatMap((folder) => {
|
|
133
|
+
const folderSelectedIds = selectedByFolder.get(folder.id);
|
|
134
|
+
if (!folderSelectedIds || folderSelectedIds.size === 0) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const activeView = resolveFolderView(folder, activeViewIdByFolder);
|
|
139
|
+
const selectedRows = folder.rows.filter((row) =>
|
|
140
|
+
folderSelectedIds.has(row.id),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return [
|
|
144
|
+
{
|
|
145
|
+
folderId: folder.id,
|
|
146
|
+
items: attachFolderId(
|
|
147
|
+
buildSelectedTree({
|
|
148
|
+
folderId: folder.id,
|
|
149
|
+
rootLabel: folder.label,
|
|
150
|
+
selectedRows,
|
|
151
|
+
groupingModel: activeView.groupingModel,
|
|
152
|
+
leafField: activeView.leafField,
|
|
153
|
+
}),
|
|
154
|
+
folder.id,
|
|
155
|
+
),
|
|
156
|
+
TreeItemComponent: folder.TreeItemComponent,
|
|
157
|
+
LeafTreeItemComponent: folder.LeafTreeItemComponent,
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
}),
|
|
161
|
+
[activeViewIdByFolder, folders, selectedByFolder],
|
|
162
|
+
);
|
|
163
|
+
|
|
87
164
|
return (
|
|
88
165
|
<Paper
|
|
89
166
|
sx={{
|
|
@@ -131,6 +208,7 @@ export function TreeViewWrapper({
|
|
|
131
208
|
<FolderTree
|
|
132
209
|
key={folderTree.folderId}
|
|
133
210
|
items={folderTree.items}
|
|
211
|
+
LeafTreeItemComponent={folderTree.LeafTreeItemComponent}
|
|
134
212
|
TreeItemComponent={folderTree.TreeItemComponent}
|
|
135
213
|
onRemove={onRemove}
|
|
136
214
|
/>
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { TreeViewBaseItem } from "@mui/x-tree-view";
|
|
2
|
+
import { ExtendedTreeItemProps } from "./types";
|
|
3
|
+
|
|
4
|
+
type TreeRow = {
|
|
5
|
+
id: string;
|
|
6
|
+
[key: string]: unknown;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type BuildSelectedTreeOptions = {
|
|
10
|
+
folderId: string;
|
|
11
|
+
rootLabel: string;
|
|
12
|
+
selectedRows: TreeRow[];
|
|
13
|
+
groupingModel: string[];
|
|
14
|
+
leafField: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const toLabel = (value: unknown, fallback: string) => {
|
|
18
|
+
if (value === null || value === undefined || value === "") {
|
|
19
|
+
return fallback;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return String(value);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const isHighlightedField = (field: string) => {
|
|
26
|
+
return field === "assay" || field === "ome";
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const createRootNode = (
|
|
30
|
+
folderId: string,
|
|
31
|
+
rootLabel: string,
|
|
32
|
+
): TreeViewBaseItem<ExtendedTreeItemProps> => ({
|
|
33
|
+
id: `${folderId}::root`,
|
|
34
|
+
kind: "root",
|
|
35
|
+
label: rootLabel,
|
|
36
|
+
icon: "folder",
|
|
37
|
+
children: [],
|
|
38
|
+
allExpAccessions: [],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const createLeafNode = (
|
|
42
|
+
row: TreeRow,
|
|
43
|
+
leafField: string,
|
|
44
|
+
): TreeViewBaseItem<ExtendedTreeItemProps> => {
|
|
45
|
+
const value = row[leafField];
|
|
46
|
+
const label = toLabel(value, row.id);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
id: row.id,
|
|
50
|
+
kind: "leaf",
|
|
51
|
+
field: leafField,
|
|
52
|
+
value: label,
|
|
53
|
+
rowId: row.id,
|
|
54
|
+
label,
|
|
55
|
+
icon: "removeable",
|
|
56
|
+
highlightName: isHighlightedField(leafField) ? label : undefined,
|
|
57
|
+
isHighlightedItem: isHighlightedField(leafField),
|
|
58
|
+
children: [],
|
|
59
|
+
allExpAccessions: [row.id],
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const appendGroupedRows = (
|
|
64
|
+
parent: TreeViewBaseItem<ExtendedTreeItemProps>,
|
|
65
|
+
rows: TreeRow[],
|
|
66
|
+
groupingModel: string[],
|
|
67
|
+
depth: number,
|
|
68
|
+
folderId: string,
|
|
69
|
+
leafField: string,
|
|
70
|
+
path: string[],
|
|
71
|
+
) => {
|
|
72
|
+
if (depth >= groupingModel.length) {
|
|
73
|
+
rows.forEach((row) => {
|
|
74
|
+
const leafNode = createLeafNode(row, leafField);
|
|
75
|
+
parent.children!.push(leafNode);
|
|
76
|
+
parent.allExpAccessions!.push(row.id);
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const field = groupingModel[depth];
|
|
82
|
+
const groupedRows = new Map<string, TreeRow[]>();
|
|
83
|
+
|
|
84
|
+
rows.forEach((row) => {
|
|
85
|
+
const value = toLabel(row[field], row.id);
|
|
86
|
+
const groupRows = groupedRows.get(value);
|
|
87
|
+
if (groupRows) {
|
|
88
|
+
groupRows.push(row);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
groupedRows.set(value, [row]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
groupedRows.forEach((groupRows, value) => {
|
|
96
|
+
const nodeId = `${folderId}::${[...path, `${field}=${encodeURIComponent(value)}`].join("::")}`;
|
|
97
|
+
const groupNode: TreeViewBaseItem<ExtendedTreeItemProps> = {
|
|
98
|
+
id: nodeId,
|
|
99
|
+
kind: "group",
|
|
100
|
+
field,
|
|
101
|
+
value,
|
|
102
|
+
label: value,
|
|
103
|
+
icon: "removeable",
|
|
104
|
+
highlightName: isHighlightedField(field) ? value : undefined,
|
|
105
|
+
isHighlightedItem: isHighlightedField(field),
|
|
106
|
+
children: [],
|
|
107
|
+
allExpAccessions: [],
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
appendGroupedRows(
|
|
111
|
+
groupNode,
|
|
112
|
+
groupRows,
|
|
113
|
+
groupingModel,
|
|
114
|
+
depth + 1,
|
|
115
|
+
folderId,
|
|
116
|
+
leafField,
|
|
117
|
+
[...path, `${field}=${encodeURIComponent(value)}`],
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
parent.children!.push(groupNode);
|
|
121
|
+
parent.allExpAccessions!.push(...groupNode.allExpAccessions!);
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const buildSelectedTree = ({
|
|
126
|
+
folderId,
|
|
127
|
+
rootLabel,
|
|
128
|
+
selectedRows,
|
|
129
|
+
groupingModel,
|
|
130
|
+
leafField,
|
|
131
|
+
}: BuildSelectedTreeOptions): TreeViewBaseItem<ExtendedTreeItemProps>[] => {
|
|
132
|
+
const root = createRootNode(folderId, rootLabel);
|
|
133
|
+
|
|
134
|
+
appendGroupedRows(
|
|
135
|
+
root,
|
|
136
|
+
selectedRows,
|
|
137
|
+
groupingModel,
|
|
138
|
+
0,
|
|
139
|
+
folderId,
|
|
140
|
+
leafField,
|
|
141
|
+
[],
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
return [root];
|
|
145
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Track } from "@weng-lab/genomebrowser";
|
|
2
|
+
import { Assembly, FolderDefinition } from "./Folders/types";
|
|
3
|
+
import type { TrackSelectTrackContext } from "./trackContext";
|
|
4
|
+
|
|
5
|
+
const buildManagedTrack = ({
|
|
6
|
+
assembly,
|
|
7
|
+
folder,
|
|
8
|
+
id,
|
|
9
|
+
trackContext,
|
|
10
|
+
}: {
|
|
11
|
+
assembly: Assembly;
|
|
12
|
+
folder: FolderDefinition;
|
|
13
|
+
id: string;
|
|
14
|
+
trackContext?: TrackSelectTrackContext;
|
|
15
|
+
}) => {
|
|
16
|
+
const row = folder.rows.find((candidate) => candidate.id === id);
|
|
17
|
+
if (!row) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const track = folder.createTrack(row, { assembly, trackContext });
|
|
22
|
+
if (!track) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return track;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const diffManagedTracks = ({
|
|
30
|
+
assembly,
|
|
31
|
+
currentTracks,
|
|
32
|
+
folders,
|
|
33
|
+
selectedByFolder,
|
|
34
|
+
trackContext,
|
|
35
|
+
}: {
|
|
36
|
+
assembly: Assembly;
|
|
37
|
+
currentTracks: Track[];
|
|
38
|
+
folders: FolderDefinition[];
|
|
39
|
+
selectedByFolder: Map<string, Set<string>>;
|
|
40
|
+
trackContext?: TrackSelectTrackContext;
|
|
41
|
+
}) => {
|
|
42
|
+
const nextManagedIds = new Set<string>();
|
|
43
|
+
const currentManagedIds = new Set<string>();
|
|
44
|
+
const idsToRemove: string[] = [];
|
|
45
|
+
const tracksToAdd: Track[] = [];
|
|
46
|
+
|
|
47
|
+
currentTracks.forEach((track) => {
|
|
48
|
+
if (folders.some((folder) => track.id.startsWith(`${folder.id}/`))) {
|
|
49
|
+
currentManagedIds.add(track.id);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
folders.forEach((folder) => {
|
|
54
|
+
const selectedIds = selectedByFolder.get(folder.id) ?? new Set<string>();
|
|
55
|
+
|
|
56
|
+
selectedIds.forEach((id) => {
|
|
57
|
+
nextManagedIds.add(id);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
currentManagedIds.forEach((id) => {
|
|
62
|
+
if (!nextManagedIds.has(id)) {
|
|
63
|
+
idsToRemove.push(id);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
folders.forEach((folder) => {
|
|
68
|
+
const selectedIds = selectedByFolder.get(folder.id) ?? new Set<string>();
|
|
69
|
+
|
|
70
|
+
selectedIds.forEach((id) => {
|
|
71
|
+
if (currentManagedIds.has(id)) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const track = buildManagedTrack({
|
|
76
|
+
assembly,
|
|
77
|
+
folder,
|
|
78
|
+
id,
|
|
79
|
+
trackContext,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (track) {
|
|
83
|
+
tracksToAdd.push(track);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
idsToRemove,
|
|
90
|
+
tracksToAdd,
|
|
91
|
+
};
|
|
92
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FolderDefinition, FolderView } from "./Folders/types";
|
|
2
|
+
|
|
3
|
+
export const resolveFolderView = (
|
|
4
|
+
folder: FolderDefinition,
|
|
5
|
+
activeViewIdByFolder: Map<string, string>,
|
|
6
|
+
): FolderView => {
|
|
7
|
+
const activeViewId = activeViewIdByFolder.get(folder.id);
|
|
8
|
+
const activeView = folder.views?.find((view) => view.id === activeViewId);
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
activeView ??
|
|
12
|
+
folder.views?.[0] ?? {
|
|
13
|
+
id: "default",
|
|
14
|
+
label: folder.label,
|
|
15
|
+
columns: folder.columns,
|
|
16
|
+
groupingModel: folder.groupingModel,
|
|
17
|
+
leafField: folder.leafField,
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BiosampleTrackContext } from "./Folders/biosamples/shared/toTrack";
|
|
2
|
+
import type { GeneTrackContext } from "./Folders/genes/shared/toTrack";
|
|
3
|
+
import type { MohdTrackContext } from "./Folders/mohd/shared/toTrack";
|
|
4
|
+
import type { OtherTracksTrackContext } from "./Folders/other-tracks/shared/toTrack";
|
|
5
|
+
|
|
6
|
+
export type TrackSelectTrackContext = GeneTrackContext &
|
|
7
|
+
BiosampleTrackContext &
|
|
8
|
+
MohdTrackContext &
|
|
9
|
+
OtherTracksTrackContext;
|
package/src/TrackSelect/types.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "@mui/x-data-grid-premium";
|
|
8
8
|
import { ReactElement, ReactNode } from "react";
|
|
9
9
|
import { SvgIconOwnProps } from "@mui/material";
|
|
10
|
+
import { FolderDefinition } from "./Folders/types";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Custom Tree Props for RichTreeView Panel
|
|
@@ -15,12 +16,13 @@ export type ExtendedTreeItemProps = {
|
|
|
15
16
|
id: string;
|
|
16
17
|
label: string;
|
|
17
18
|
icon: string;
|
|
19
|
+
kind?: "root" | "group" | "leaf";
|
|
20
|
+
field?: string;
|
|
21
|
+
value?: string;
|
|
22
|
+
rowId?: string;
|
|
18
23
|
folderId?: string;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* The assay name for leaf nodes (experiment accession items)
|
|
22
|
-
*/
|
|
23
|
-
assayName?: string;
|
|
24
|
+
isHighlightedItem?: boolean;
|
|
25
|
+
highlightName?: string;
|
|
24
26
|
/**
|
|
25
27
|
* list of all the experimentAccession values in the children/grandchildren of the item, or the accession of the item itself
|
|
26
28
|
* this is used in updating the rowSelectionModel when removing items from the Tree View panel
|
|
@@ -28,22 +30,10 @@ export type ExtendedTreeItemProps = {
|
|
|
28
30
|
allExpAccessions?: string[];
|
|
29
31
|
};
|
|
30
32
|
|
|
31
|
-
/**
|
|
32
|
-
* Configuration for a single folder's tree in the TreeViewWrapper.
|
|
33
|
-
* Each folder gets its own tree with its own TreeItemComponent.
|
|
34
|
-
*/
|
|
35
|
-
export type FolderTreeConfig = {
|
|
36
|
-
folderId: string;
|
|
37
|
-
items: TreeViewBaseItem<ExtendedTreeItemProps>[];
|
|
38
|
-
/** Optional custom TreeItem component for this folder */
|
|
39
|
-
TreeItemComponent?: React.ForwardRefExoticComponent<
|
|
40
|
-
CustomTreeItemProps & React.RefAttributes<HTMLLIElement>
|
|
41
|
-
>;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
33
|
export type TreeViewWrapperProps = {
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
folders: FolderDefinition[];
|
|
35
|
+
selectedByFolder: Map<string, Set<string>>;
|
|
36
|
+
activeViewIdByFolder: Map<string, string>;
|
|
47
37
|
selectedCount: number;
|
|
48
38
|
onRemove: (item: TreeViewBaseItem<ExtendedTreeItemProps>) => void;
|
|
49
39
|
};
|
|
@@ -51,35 +41,20 @@ export type TreeViewWrapperProps = {
|
|
|
51
41
|
export interface CustomLabelProps {
|
|
52
42
|
id: string;
|
|
53
43
|
children: React.ReactNode;
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
isHighlightedItem?: boolean;
|
|
45
|
+
highlightName?: string;
|
|
56
46
|
icon?: React.ElementType | React.ReactElement | ReactNode;
|
|
57
|
-
/** Optional function to render custom icons for assay items */
|
|
58
47
|
renderIcon?: (name: string) => ReactNode;
|
|
59
48
|
}
|
|
60
49
|
|
|
61
50
|
export interface CustomTreeItemProps
|
|
62
|
-
extends
|
|
51
|
+
extends
|
|
52
|
+
Omit<UseTreeItemParameters, "rootRef">,
|
|
63
53
|
Omit<React.HTMLAttributes<HTMLLIElement>, "onFocus"> {
|
|
64
54
|
onRemove?: (item: TreeViewBaseItem<ExtendedTreeItemProps>) => void;
|
|
65
|
-
/** Optional function to render custom icons for assay items */
|
|
66
55
|
renderIcon?: (name: string) => ReactNode;
|
|
67
56
|
}
|
|
68
57
|
|
|
69
|
-
/**
|
|
70
|
-
* Types for useSelectionStore to keep track of selected DataGrid rows/tracks
|
|
71
|
-
*/
|
|
72
|
-
export type SelectionState = {
|
|
73
|
-
selectedByFolder: Map<string, Set<string>>;
|
|
74
|
-
activeFolderId: string;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export type SelectionAction = {
|
|
78
|
-
clear: (folderId?: string) => void;
|
|
79
|
-
setActiveFolder: (folderId: string) => void;
|
|
80
|
-
setSelection: (folderId: string, ids: Set<string>) => void;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
58
|
/**
|
|
84
59
|
* DataGrid Props
|
|
85
60
|
*/
|
package/src/lib.ts
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
import
|
|
2
|
-
export { TrackSelect, TrackSelectProps };
|
|
1
|
+
import "./muiLicense";
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
type
|
|
7
|
-
} from "./TrackSelect/
|
|
8
|
-
export {
|
|
3
|
+
import TrackSelect, {
|
|
4
|
+
type InitialSelectedIdsByAssembly,
|
|
5
|
+
type TrackSelectProps,
|
|
6
|
+
} from "./TrackSelect/TrackSelect";
|
|
7
|
+
export { TrackSelect, TrackSelectProps };
|
|
8
|
+
export type { TrackSelectTrackContext } from "./TrackSelect/trackContext";
|
|
9
|
+
export type { InitialSelectedIdsByAssembly };
|
|
9
10
|
|
|
10
11
|
import { foldersByAssembly } from "./TrackSelect/Folders/index.ts";
|
|
11
12
|
export { foldersByAssembly };
|
|
12
13
|
|
|
13
14
|
export type {
|
|
14
15
|
BiosampleRowInfo,
|
|
16
|
+
BiosampleTrackContext,
|
|
15
17
|
GeneRowInfo,
|
|
18
|
+
GeneTrackContext,
|
|
19
|
+
MohdRowInfo,
|
|
20
|
+
MohdTrackContext,
|
|
16
21
|
OtherTrackInfo,
|
|
22
|
+
OtherTracksTrackContext,
|
|
17
23
|
} from "./TrackSelect/Folders";
|
|
18
24
|
|
|
19
25
|
import { tfPeaksTrack } from "./TrackSelect/Custom/TfPeaks.tsx";
|
package/src/vite-env.d.ts
CHANGED