@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.
- package/.env.local +1 -0
- package/dist/TrackSelect/DataGrid/DefaultGroupingCell.d.ts +6 -0
- package/dist/TrackSelect/FolderList/Breadcrumb.d.ts +6 -0
- package/dist/TrackSelect/FolderList/FolderCard.d.ts +6 -0
- package/dist/TrackSelect/FolderList/FolderList.d.ts +6 -0
- package/dist/TrackSelect/{Data/humanBiosamples.json.d.ts → Folders/biosamples/data/human.json.d.ts} +1940 -1919
- package/dist/TrackSelect/{Data/mouseBiosamples.json.d.ts → Folders/biosamples/data/mouse.json.d.ts} +408 -357
- package/dist/TrackSelect/Folders/biosamples/human.d.ts +7 -0
- package/dist/TrackSelect/Folders/biosamples/mouse.d.ts +7 -0
- package/dist/TrackSelect/Folders/biosamples/shared/AssayToggle.d.ts +14 -0
- package/dist/TrackSelect/Folders/biosamples/shared/BiosampleGroupingCell.d.ts +6 -0
- package/dist/TrackSelect/Folders/biosamples/shared/BiosampleTreeItem.d.ts +7 -0
- package/dist/TrackSelect/Folders/biosamples/shared/columns.d.ts +14 -0
- package/dist/TrackSelect/Folders/biosamples/shared/constants.d.ts +19 -0
- package/dist/TrackSelect/Folders/biosamples/shared/createFolder.d.ts +24 -0
- package/dist/TrackSelect/Folders/biosamples/shared/treeBuilder.d.ts +25 -0
- package/dist/TrackSelect/Folders/biosamples/shared/types.d.ts +44 -0
- package/dist/TrackSelect/Folders/genes/data/human.json.d.ts +10 -0
- package/dist/TrackSelect/Folders/genes/data/mouse.json.d.ts +10 -0
- package/dist/TrackSelect/Folders/genes/human.d.ts +7 -0
- package/dist/TrackSelect/Folders/genes/mouse.d.ts +7 -0
- package/dist/TrackSelect/Folders/genes/shared/columns.d.ts +14 -0
- package/dist/TrackSelect/Folders/genes/shared/createFolder.d.ts +12 -0
- package/dist/TrackSelect/Folders/genes/shared/treeBuilder.d.ts +13 -0
- package/dist/TrackSelect/Folders/genes/shared/types.d.ts +26 -0
- package/dist/TrackSelect/Folders/index.d.ts +14 -0
- package/dist/TrackSelect/Folders/types.d.ts +76 -0
- package/dist/TrackSelect/TrackSelect.d.ts +12 -5
- package/dist/TrackSelect/TreeView/CustomTreeItem.d.ts +3 -0
- package/dist/TrackSelect/TreeView/TreeViewWrapper.d.ts +1 -1
- package/dist/TrackSelect/store.d.ts +1 -2
- package/dist/TrackSelect/types.d.ts +24 -62
- package/dist/genomebrowser-ui.es.js +1373 -2117
- package/dist/genomebrowser-ui.es.js.map +1 -1
- package/dist/lib.d.ts +2 -2
- package/package.json +3 -3
- package/src/TrackSelect/DataGrid/DataGridWrapper.tsx +36 -20
- package/src/TrackSelect/DataGrid/DefaultGroupingCell.tsx +64 -0
- package/src/TrackSelect/FolderList/Breadcrumb.tsx +38 -0
- package/src/TrackSelect/FolderList/FolderCard.tsx +51 -0
- package/src/TrackSelect/FolderList/FolderList.tsx +47 -0
- package/src/TrackSelect/Folders/NEW.md +929 -0
- package/src/TrackSelect/{Data → Folders/biosamples/data}/formatBiosamples.go +2 -2
- package/src/TrackSelect/{Data/humanBiosamples.json → Folders/biosamples/data/human.json} +1940 -1919
- package/src/TrackSelect/{Data/mouseBiosamples.json → Folders/biosamples/data/mouse.json} +408 -357
- package/src/TrackSelect/Folders/biosamples/human.ts +17 -0
- package/src/TrackSelect/Folders/biosamples/mouse.ts +17 -0
- package/src/TrackSelect/Folders/biosamples/shared/AssayToggle.tsx +65 -0
- package/src/TrackSelect/{DataGrid/GroupingCell.tsx → Folders/biosamples/shared/BiosampleGroupingCell.tsx} +7 -5
- package/src/TrackSelect/Folders/biosamples/shared/BiosampleTreeItem.tsx +15 -0
- package/src/TrackSelect/{DataGrid → Folders/biosamples/shared}/columns.tsx +31 -17
- package/src/TrackSelect/Folders/biosamples/shared/constants.tsx +116 -0
- package/src/TrackSelect/Folders/biosamples/shared/createFolder.ts +116 -0
- package/src/TrackSelect/Folders/biosamples/shared/treeBuilder.ts +227 -0
- package/src/TrackSelect/Folders/biosamples/shared/types.ts +48 -0
- package/src/TrackSelect/Folders/genes/data/human.json +7 -0
- package/src/TrackSelect/Folders/genes/data/mouse.json +7 -0
- package/src/TrackSelect/Folders/genes/human.ts +16 -0
- package/src/TrackSelect/Folders/genes/mouse.ts +16 -0
- package/src/TrackSelect/Folders/genes/shared/columns.tsx +42 -0
- package/src/TrackSelect/Folders/genes/shared/createFolder.ts +68 -0
- package/src/TrackSelect/Folders/genes/shared/treeBuilder.ts +45 -0
- package/src/TrackSelect/Folders/genes/shared/types.ts +29 -0
- package/src/TrackSelect/Folders/index.ts +27 -0
- package/src/TrackSelect/Folders/types.ts +95 -0
- package/src/TrackSelect/TrackSelect.tsx +409 -311
- package/src/TrackSelect/TreeView/CustomTreeItem.tsx +217 -0
- package/src/TrackSelect/TreeView/TreeViewWrapper.tsx +47 -42
- package/src/TrackSelect/store.ts +103 -46
- package/src/TrackSelect/types.ts +28 -74
- package/src/lib.ts +2 -2
- package/test/main.tsx +113 -169
- package/.claude/settings.local.json +0 -7
- package/dist/TrackSelect/DataGrid/CustomToolbar.d.ts +0 -12
- package/dist/TrackSelect/DataGrid/GroupingCell.d.ts +0 -2
- package/dist/TrackSelect/DataGrid/columns.d.ts +0 -4
- package/dist/TrackSelect/DataGrid/dataGridHelpers.d.ts +0 -49
- package/dist/TrackSelect/TreeView/treeViewHelpers.d.ts +0 -49
- package/dist/TrackSelect/consts.d.ts +0 -11
- package/src/TrackSelect/DataGrid/CustomToolbar.tsx +0 -152
- package/src/TrackSelect/DataGrid/dataGridHelpers.tsx +0 -155
- package/src/TrackSelect/TreeView/treeViewHelpers.tsx +0 -475
- package/src/TrackSelect/consts.ts +0 -92
- package/src/TrackSelect/issues.md +0 -404
package/dist/lib.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { default as TrackSelect, TrackSelectProps } from './TrackSelect/TrackSelect';
|
|
2
2
|
import { createSelectionStore, SelectionStoreInstance } from './TrackSelect/store.ts';
|
|
3
|
-
import {
|
|
3
|
+
import { foldersByAssembly } from './TrackSelect/Folders/index.ts';
|
|
4
4
|
export { TrackSelect, TrackSelectProps };
|
|
5
5
|
export { createSelectionStore, SelectionStoreInstance };
|
|
6
|
-
export {
|
|
6
|
+
export { foldersByAssembly };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weng-lab/genomebrowser-ui",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0-beta.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"publishConfig": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"module": "dist/genomebrowser-ui.es.js",
|
|
11
11
|
"types": "dist/lib.d.ts",
|
|
12
12
|
"dependencies": {
|
|
13
|
+
"@mui/x-license": "^8.25.0",
|
|
13
14
|
"fuse.js": "^7.1.0",
|
|
14
15
|
"zustand": "^5.0.8"
|
|
15
16
|
},
|
|
@@ -18,11 +19,10 @@
|
|
|
18
19
|
"@emotion/styled": "^11.0.0",
|
|
19
20
|
"@mui/icons-material": "^7.3.6",
|
|
20
21
|
"@mui/material": "^7.0.0",
|
|
21
|
-
"@mui/x-data-grid": "^8.19.0",
|
|
22
22
|
"@mui/x-data-grid-premium": "^8.19.0",
|
|
23
23
|
"react": "^19.0.0",
|
|
24
24
|
"react-dom": "^19.0.0",
|
|
25
|
-
"@weng-lab/genomebrowser": "1.7.
|
|
25
|
+
"@weng-lab/genomebrowser": "1.7.6"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@eslint/js": "^9.34.0",
|
|
@@ -7,10 +7,9 @@ import {
|
|
|
7
7
|
GridColumnVisibilityModel,
|
|
8
8
|
useGridApiRef,
|
|
9
9
|
} from "@mui/x-data-grid-premium";
|
|
10
|
-
import { useEffect, useState } from "react";
|
|
10
|
+
import { useEffect, useMemo, useState } from "react";
|
|
11
11
|
import { DataGridProps } from "../types";
|
|
12
|
-
import {
|
|
13
|
-
import GroupingCell from "./GroupingCell";
|
|
12
|
+
import { DefaultGroupingCell } from "./DefaultGroupingCell";
|
|
14
13
|
|
|
15
14
|
const autosizeOptions: GridAutosizeOptions = {
|
|
16
15
|
expand: true,
|
|
@@ -19,35 +18,49 @@ const autosizeOptions: GridAutosizeOptions = {
|
|
|
19
18
|
};
|
|
20
19
|
|
|
21
20
|
export function DataGridWrapper(props: DataGridProps) {
|
|
22
|
-
const {
|
|
21
|
+
const {
|
|
22
|
+
columns,
|
|
23
|
+
groupingModel,
|
|
24
|
+
leafField,
|
|
25
|
+
onSelectionChange,
|
|
26
|
+
rows,
|
|
27
|
+
selectedIds,
|
|
28
|
+
GroupingCellComponent,
|
|
29
|
+
} = props;
|
|
30
|
+
|
|
31
|
+
const GroupingCell = GroupingCellComponent ?? DefaultGroupingCell;
|
|
23
32
|
|
|
24
33
|
const apiRef = useGridApiRef();
|
|
25
34
|
|
|
26
|
-
// Resize columns when toggling between sort modes
|
|
27
35
|
useEffect(() => {
|
|
28
36
|
if (apiRef.current && apiRef.current.autosizeColumns) {
|
|
29
37
|
apiRef.current.autosizeColumns(autosizeOptions);
|
|
30
38
|
}
|
|
31
|
-
}, [
|
|
39
|
+
}, [columns, groupingModel, leafField]);
|
|
40
|
+
|
|
41
|
+
const baseVisibility = useMemo(() => {
|
|
42
|
+
const visibility: GridColumnVisibilityModel = {
|
|
43
|
+
id: false,
|
|
44
|
+
};
|
|
32
45
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const leafField = sortedAssay ? "displayname" : "assay";
|
|
46
|
+
// Only hide leafField if we have grouping (it shows in grouping column)
|
|
47
|
+
if (groupingModel.length > 0) {
|
|
48
|
+
visibility[leafField] = false;
|
|
49
|
+
}
|
|
38
50
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
51
|
+
groupingModel.forEach((field) => {
|
|
52
|
+
visibility[field] = false;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return visibility;
|
|
56
|
+
}, [groupingModel, leafField]);
|
|
43
57
|
|
|
44
58
|
const [columnVisibilityModel, setColumnVisibilityModel] =
|
|
45
59
|
useState<GridColumnVisibilityModel>(baseVisibility);
|
|
46
60
|
|
|
47
|
-
// Update visibility when sort mode changes
|
|
48
61
|
useEffect(() => {
|
|
49
62
|
setColumnVisibilityModel(baseVisibility);
|
|
50
|
-
}, [
|
|
63
|
+
}, [baseVisibility]);
|
|
51
64
|
|
|
52
65
|
// functions to customize the column and filter panel in the toolbar
|
|
53
66
|
const filterColumns = ({ columns }: FilterColumnsArgs) => {
|
|
@@ -74,7 +87,7 @@ export function DataGridWrapper(props: DataGridProps) {
|
|
|
74
87
|
<DataGridPremium
|
|
75
88
|
apiRef={apiRef}
|
|
76
89
|
rows={rows}
|
|
77
|
-
columns={
|
|
90
|
+
columns={columns}
|
|
78
91
|
getRowId={(row) => row.id}
|
|
79
92
|
autosizeOptions={autosizeOptions}
|
|
80
93
|
rowGroupingModel={groupingModel}
|
|
@@ -88,8 +101,11 @@ export function DataGridWrapper(props: DataGridProps) {
|
|
|
88
101
|
}}
|
|
89
102
|
columnVisibilityModel={columnVisibilityModel}
|
|
90
103
|
onColumnVisibilityModelChange={setColumnVisibilityModel}
|
|
91
|
-
onRowSelectionModelChange={
|
|
92
|
-
|
|
104
|
+
onRowSelectionModelChange={(selection) => {
|
|
105
|
+
const ids = (selection as any)?.ids ?? new Set<string>();
|
|
106
|
+
onSelectionChange(new Set(ids));
|
|
107
|
+
}}
|
|
108
|
+
rowSelectionPropagation={{ descendants: true, parents: false }}
|
|
93
109
|
disableRowGrouping={false}
|
|
94
110
|
rowSelectionModel={{
|
|
95
111
|
type: "include",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Tooltip, Box, IconButton } from "@mui/material";
|
|
2
|
+
import {
|
|
3
|
+
GridRenderCellParams,
|
|
4
|
+
useGridApiContext,
|
|
5
|
+
GridGroupNode,
|
|
6
|
+
} from "@mui/x-data-grid-premium";
|
|
7
|
+
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
|
8
|
+
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Default grouping cell that handles expand/collapse, truncation, and tooltips.
|
|
12
|
+
* This is a generic implementation without any folder-specific logic.
|
|
13
|
+
*/
|
|
14
|
+
export function DefaultGroupingCell(params: GridRenderCellParams) {
|
|
15
|
+
const apiRef = useGridApiContext();
|
|
16
|
+
const isGroup = params.rowNode.type === "group";
|
|
17
|
+
const groupNode = params.rowNode as GridGroupNode;
|
|
18
|
+
const isExpanded = isGroup ? groupNode.childrenExpanded : false;
|
|
19
|
+
const depth = params.rowNode.depth ?? 0;
|
|
20
|
+
|
|
21
|
+
const handleExpandClick = (e: React.MouseEvent) => {
|
|
22
|
+
e.stopPropagation();
|
|
23
|
+
apiRef.current.setRowChildrenExpansion(params.id, !isExpanded);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const value = String(params.value ?? "");
|
|
27
|
+
|
|
28
|
+
// Indent based on depth (2 units per level)
|
|
29
|
+
const indentLevel = depth * 2;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Box
|
|
33
|
+
sx={{
|
|
34
|
+
display: "flex",
|
|
35
|
+
alignItems: "center",
|
|
36
|
+
width: "100%",
|
|
37
|
+
ml: indentLevel,
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
{isGroup && (
|
|
41
|
+
<IconButton size="small" onClick={handleExpandClick} sx={{ mr: 0.5 }}>
|
|
42
|
+
{isExpanded ? (
|
|
43
|
+
<ExpandMoreIcon fontSize="small" />
|
|
44
|
+
) : (
|
|
45
|
+
<ChevronRightIcon fontSize="small" />
|
|
46
|
+
)}
|
|
47
|
+
</IconButton>
|
|
48
|
+
)}
|
|
49
|
+
<Tooltip title={value} placement="top-start" enterDelay={500}>
|
|
50
|
+
<Box
|
|
51
|
+
sx={{
|
|
52
|
+
overflow: "hidden",
|
|
53
|
+
textOverflow: "ellipsis",
|
|
54
|
+
whiteSpace: "nowrap",
|
|
55
|
+
flex: 1,
|
|
56
|
+
fontWeight: isGroup ? "bold" : undefined,
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{params.formattedValue}
|
|
60
|
+
</Box>
|
|
61
|
+
</Tooltip>
|
|
62
|
+
</Box>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Breadcrumbs, Link, Typography } from "@mui/material";
|
|
2
|
+
import NavigateNextIcon from "@mui/icons-material/NavigateNext";
|
|
3
|
+
import { FolderDefinition } from "../Folders/types";
|
|
4
|
+
|
|
5
|
+
export interface BreadcrumbProps {
|
|
6
|
+
currentFolder: FolderDefinition | null;
|
|
7
|
+
onNavigateToRoot: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function Breadcrumb({
|
|
11
|
+
currentFolder,
|
|
12
|
+
onNavigateToRoot,
|
|
13
|
+
}: BreadcrumbProps) {
|
|
14
|
+
return (
|
|
15
|
+
<Breadcrumbs separator={<NavigateNextIcon fontSize="small" />}>
|
|
16
|
+
{currentFolder ? (
|
|
17
|
+
<Link
|
|
18
|
+
component="button"
|
|
19
|
+
variant="body1"
|
|
20
|
+
onClick={onNavigateToRoot}
|
|
21
|
+
underline="hover"
|
|
22
|
+
sx={{ cursor: "pointer" }}
|
|
23
|
+
>
|
|
24
|
+
All Folders
|
|
25
|
+
</Link>
|
|
26
|
+
) : (
|
|
27
|
+
<Typography variant="body1" color="text.primary">
|
|
28
|
+
All Folders
|
|
29
|
+
</Typography>
|
|
30
|
+
)}
|
|
31
|
+
{currentFolder && (
|
|
32
|
+
<Typography variant="body1" color="text.primary">
|
|
33
|
+
{currentFolder.label}
|
|
34
|
+
</Typography>
|
|
35
|
+
)}
|
|
36
|
+
</Breadcrumbs>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Paper, Typography } from "@mui/material";
|
|
2
|
+
import { FolderDefinition } from "../Folders/types";
|
|
3
|
+
|
|
4
|
+
export interface FolderCardProps {
|
|
5
|
+
folder: FolderDefinition;
|
|
6
|
+
onClick: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function FolderCard({ folder, onClick }: FolderCardProps) {
|
|
10
|
+
return (
|
|
11
|
+
<Paper
|
|
12
|
+
elevation={1}
|
|
13
|
+
onClick={onClick}
|
|
14
|
+
onKeyDown={(e) => {
|
|
15
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
16
|
+
e.preventDefault();
|
|
17
|
+
onClick();
|
|
18
|
+
}
|
|
19
|
+
}}
|
|
20
|
+
role="button"
|
|
21
|
+
tabIndex={0}
|
|
22
|
+
sx={{
|
|
23
|
+
p: 3,
|
|
24
|
+
cursor: "pointer",
|
|
25
|
+
transition: "all 0.2s ease-in-out",
|
|
26
|
+
"&:hover": {
|
|
27
|
+
elevation: 3,
|
|
28
|
+
boxShadow: 3,
|
|
29
|
+
bgcolor: "action.hover",
|
|
30
|
+
},
|
|
31
|
+
"&:focus": {
|
|
32
|
+
outline: "2px solid",
|
|
33
|
+
outlineColor: "primary.main",
|
|
34
|
+
outlineOffset: 2,
|
|
35
|
+
},
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
<Typography variant="h6" gutterBottom>
|
|
39
|
+
{folder.label}
|
|
40
|
+
</Typography>
|
|
41
|
+
{folder.description && (
|
|
42
|
+
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|
43
|
+
{folder.description}
|
|
44
|
+
</Typography>
|
|
45
|
+
)}
|
|
46
|
+
<Typography variant="caption" color="text.secondary">
|
|
47
|
+
{folder.rowById.size.toLocaleString()} tracks available
|
|
48
|
+
</Typography>
|
|
49
|
+
</Paper>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Paper, Stack, Typography } from "@mui/material";
|
|
2
|
+
import { FolderDefinition } from "../Folders/types";
|
|
3
|
+
import { FolderCard } from "./FolderCard";
|
|
4
|
+
|
|
5
|
+
export interface FolderListProps {
|
|
6
|
+
folders: FolderDefinition[];
|
|
7
|
+
onFolderSelect: (folderId: string) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function FolderList({ folders, onFolderSelect }: FolderListProps) {
|
|
11
|
+
if (folders.length === 0) {
|
|
12
|
+
return (
|
|
13
|
+
<Paper
|
|
14
|
+
variant="outlined"
|
|
15
|
+
sx={{
|
|
16
|
+
height: 500,
|
|
17
|
+
borderWidth: 2,
|
|
18
|
+
}}
|
|
19
|
+
>
|
|
20
|
+
<Typography color="text.secondary" sx={{ p: 3 }}>
|
|
21
|
+
No folders available
|
|
22
|
+
</Typography>
|
|
23
|
+
</Paper>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Paper
|
|
29
|
+
variant="outlined"
|
|
30
|
+
sx={{
|
|
31
|
+
height: 500,
|
|
32
|
+
overflow: "auto",
|
|
33
|
+
borderWidth: 2,
|
|
34
|
+
}}
|
|
35
|
+
>
|
|
36
|
+
<Stack spacing={2} sx={{ p: 2 }}>
|
|
37
|
+
{folders.map((folder) => (
|
|
38
|
+
<FolderCard
|
|
39
|
+
key={folder.id}
|
|
40
|
+
folder={folder}
|
|
41
|
+
onClick={() => onFolderSelect(folder.id)}
|
|
42
|
+
/>
|
|
43
|
+
))}
|
|
44
|
+
</Stack>
|
|
45
|
+
</Paper>
|
|
46
|
+
);
|
|
47
|
+
}
|