@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
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 { RowInfo } from './TrackSelect/types.ts';
3
+ import { foldersByAssembly } from './TrackSelect/Folders/index.ts';
4
4
  export { TrackSelect, TrackSelectProps };
5
5
  export { createSelectionStore, SelectionStoreInstance };
6
- export { RowInfo };
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.1.11",
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.3"
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 { defaultColumns, sortedByAssayColumns } from "./columns";
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 { sortedAssay, handleSelection, rows, selectedIds } = props;
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
- }, [sortedAssay]);
39
+ }, [columns, groupingModel, leafField]);
40
+
41
+ const baseVisibility = useMemo(() => {
42
+ const visibility: GridColumnVisibilityModel = {
43
+ id: false,
44
+ };
32
45
 
33
- const groupingModel = sortedAssay
34
- ? ["assay", "ontology"]
35
- : ["ontology", "displayname"];
36
- const columnModel = sortedAssay ? sortedByAssayColumns : defaultColumns;
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
- // Hide columns that are used in grouping or as leaf field, plus ID column
40
- const baseVisibility: GridColumnVisibilityModel = sortedAssay
41
- ? { assay: false, ontology: false, displayname: false, id: false } // sort by assay: assay & ontology are grouping, displayname is leaf
42
- : { ontology: false, displayname: false, assay: false, id: false }; // default: ontology & displayname are grouping, assay is leaf
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
- }, [sortedAssay]);
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={columnModel}
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={handleSelection}
92
- rowSelectionPropagation={{ descendants: true, parents: true }}
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
+ }