@weng-lab/genomebrowser-ui 0.1.6 → 0.1.7

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@weng-lab/genomebrowser-ui",
3
3
  "private": false,
4
- "version": "0.1.6",
4
+ "version": "0.1.7",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "publishConfig": {
@@ -22,7 +22,7 @@
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.2-beta.2"
25
+ "@weng-lab/genomebrowser": "1.7.2-beta.4"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@eslint/js": "^9.34.0",
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "mcp__Ref__ref_read_url"
5
+ ]
6
+ }
7
+ }
@@ -2,7 +2,6 @@ import * as React from "react";
2
2
  import {
3
3
  Toolbar,
4
4
  ToolbarButton,
5
- ColumnsPanelTrigger,
6
5
  FilterPanelTrigger,
7
6
  ExportCsv,
8
7
  ExportPrint,
@@ -13,7 +12,6 @@ import {
13
12
  import Tooltip from "@mui/material/Tooltip";
14
13
  import Menu from "@mui/material/Menu";
15
14
  import Badge from "@mui/material/Badge";
16
- import ViewColumnIcon from "@mui/icons-material/ViewColumn";
17
15
  import FilterListIcon from "@mui/icons-material/FilterList";
18
16
  import FileDownloadIcon from "@mui/icons-material/FileDownload";
19
17
  import MenuItem from "@mui/material/MenuItem";
@@ -77,12 +75,6 @@ export function CustomToolbar({
77
75
  </>
78
76
  )}
79
77
 
80
- <Tooltip title="Columns">
81
- <ColumnsPanelTrigger render={<ToolbarButton />}>
82
- <ViewColumnIcon fontSize="small" htmlColor={iconColor} />
83
- </ColumnsPanelTrigger>
84
- </Tooltip>
85
-
86
78
  <Tooltip title="Filters">
87
79
  <FilterPanelTrigger
88
80
  render={(props, state) => (
@@ -1,17 +1,15 @@
1
1
  import { Box, Paper } from "@mui/material";
2
2
  import {
3
3
  DataGridPremium,
4
- GridToolbarProps,
5
- ToolbarPropsOverrides,
4
+ FilterColumnsArgs,
6
5
  GridAutosizeOptions,
7
- useGridApiRef,
8
6
  GridColDef,
9
- FilterColumnsArgs,
7
+ GridColumnVisibilityModel,
8
+ useGridApiRef,
10
9
  } from "@mui/x-data-grid-premium";
10
+ import { useEffect, useState } from "react";
11
11
  import { DataGridProps } from "../types";
12
- import { CustomToolbar } from "./CustomToolbar";
13
- import { useEffect, useMemo } from "react";
14
- import { sortedByAssayColumns, defaultColumns } from "./columns";
12
+ import { defaultColumns, sortedByAssayColumns } from "./columns";
15
13
 
16
14
  const autosizeOptions: GridAutosizeOptions = {
17
15
  expand: true,
@@ -21,38 +19,35 @@ const autosizeOptions: GridAutosizeOptions = {
21
19
 
22
20
  // TODO: figure out where mui stores the number of rows in a row grouping so that can be bolded too
23
21
  export function DataGridWrapper(props: DataGridProps) {
24
- const {
25
- label,
26
- labelTooltip,
27
- downloadFileName,
28
- toolbarSlot,
29
- toolbarStyle,
30
- toolbarIconColor,
31
- sortedAssay,
32
- handleSelection,
33
- rows,
34
- selectedTracks,
35
- } = props;
36
-
37
- const CustomToolbarWrapper = useMemo(() => {
38
- const customToolbarProps = {
39
- label,
40
- downloadFileName,
41
- labelTooltip,
42
- toolbarSlot,
43
- toolbarStyle,
44
- toolbarIconColor,
45
- };
46
- return (props: GridToolbarProps & ToolbarPropsOverrides) => (
47
- <CustomToolbar {...props} {...customToolbarProps} />
48
- );
49
- }, [label, labelTooltip, toolbarSlot]);
22
+ const { sortedAssay, handleSelection, rows, selectedIds } = props;
50
23
 
51
24
  const apiRef = useGridApiRef();
25
+
26
+ // Resize columns when toggling between sort modes
27
+ useEffect(() => {
28
+ if (apiRef.current && apiRef.current.autosizeColumns) {
29
+ apiRef.current.autosizeColumns(autosizeOptions);
30
+ }
31
+ }, [sortedAssay]);
32
+
52
33
  const groupingModel = sortedAssay
53
34
  ? ["assay", "ontology"]
54
- : ["ontology", "assay"];
35
+ : ["ontology", "displayname"];
55
36
  const columnModel = sortedAssay ? sortedByAssayColumns : defaultColumns;
37
+ const leafField = sortedAssay ? "displayname" : "assay";
38
+
39
+ // Hide columns that are used in grouping or as leaf field
40
+ const baseVisibility: GridColumnVisibilityModel = sortedAssay
41
+ ? { assay: false, ontology: false, displayname: false } // sort by assay: assay & ontology are grouping, displayname is leaf
42
+ : { ontology: false, displayname: false, assay: false }; // default: ontology & displayname are grouping, assay is leaf
43
+
44
+ const [columnVisibilityModel, setColumnVisibilityModel] =
45
+ useState<GridColumnVisibilityModel>(baseVisibility);
46
+
47
+ // Update visibility when sort mode changes
48
+ useEffect(() => {
49
+ setColumnVisibilityModel(baseVisibility);
50
+ }, [sortedAssay]);
56
51
 
57
52
  // functions to customize the column and filter panel in the toolbar
58
53
  const filterColumns = ({ columns }: FilterColumnsArgs) => {
@@ -67,18 +62,6 @@ export function DataGridWrapper(props: DataGridProps) {
67
62
  .map((column) => column.field);
68
63
  };
69
64
 
70
- const handleResizeCols = () => {
71
- // need to check .autosizeColumns since the current was being set with an empty object
72
- if (!apiRef.current?.autosizeColumns) return;
73
- apiRef.current.autosizeColumns(autosizeOptions);
74
- };
75
-
76
- // trigger resize when rows or columns change so that rows/columns don't need to be memoized outisde of this component
77
- // otherwise sometimes would snap back to default widths when rows/columns change
78
- useEffect(() => {
79
- handleResizeCols();
80
- }, [rows, defaultColumns, sortedByAssayColumns, handleResizeCols]);
81
-
82
65
  return (
83
66
  <Paper sx={{ width: "100%" }}>
84
67
  <Box
@@ -95,17 +78,15 @@ export function DataGridWrapper(props: DataGridProps) {
95
78
  getRowId={(row) => row.experimentAccession}
96
79
  autosizeOptions={autosizeOptions}
97
80
  rowGroupingModel={groupingModel}
98
- groupingColDef={{ leafField: "displayname", display: "flex" }}
99
- columnVisibilityModel={{ displayname: false }} // so you don't see a second name column
81
+ groupingColDef={{ leafField, display: "flex" }}
82
+ columnVisibilityModel={columnVisibilityModel}
83
+ onColumnVisibilityModelChange={setColumnVisibilityModel}
100
84
  onRowSelectionModelChange={handleSelection}
101
- rowSelectionPropagation={{ descendants: true }}
85
+ rowSelectionPropagation={{ descendants: true, parents: true }}
102
86
  disableRowGrouping={false}
103
87
  rowSelectionModel={{
104
88
  type: "include",
105
- ids: new Set(selectedTracks.keys()),
106
- }}
107
- slots={{
108
- toolbar: CustomToolbarWrapper,
89
+ ids: selectedIds,
109
90
  }}
110
91
  slotProps={{
111
92
  filterPanel: {
@@ -12,123 +12,133 @@ const displayNameCol: GridColDef<RowInfo> = {
12
12
  };
13
13
 
14
14
  const sortedByAssayOntologyCol: GridColDef<RowInfo> = {
15
- field: "ontology",
16
- headerName: "Ontology",
17
- type: "singleSelect",
18
- valueOptions: ontologyTypes,
19
- renderCell: (params) => {
20
- if (params.rowNode.type === "group") {
21
- if (params.value === undefined) {
22
- return null;
23
- }
24
- const val = params.value;
25
- return (
26
- <div><b>{val}</b></div>
27
- )
15
+ field: "ontology",
16
+ headerName: "Ontology",
17
+ type: "singleSelect",
18
+ valueOptions: ontologyTypes,
19
+ renderCell: (params) => {
20
+ if (params.rowNode.type === "group") {
21
+ if (params.value === undefined) {
22
+ return null;
28
23
  }
29
- },
24
+ const val = params.value;
25
+ return (
26
+ <div>
27
+ <b>{val}</b>
28
+ </div>
29
+ );
30
+ }
31
+ },
30
32
  };
31
33
 
32
- const sortedByAssayAssayCol : GridColDef<RowInfo> = {
33
- field: "assay",
34
- headerName: "Assay",
35
- valueOptions: assayTypes,
36
- renderCell: (params) => {
37
- if (params.rowNode.type === "group") {
38
- if (params.value === undefined) {
39
- return null;
40
- }
41
- const val = params.value;
42
- return (
43
- <Stack direction="row" spacing={2} alignItems="center">
44
- {AssayIcon(val)}
45
- <div><b>{val}</b></div>
46
- </Stack>
47
- )
48
- }
34
+ const sortedByAssayAssayCol: GridColDef<RowInfo> = {
35
+ field: "assay",
36
+ headerName: "Assay",
37
+ valueOptions: assayTypes,
38
+ renderCell: (params) => {
39
+ if (params.rowNode.type === "group") {
40
+ if (params.value === undefined) {
41
+ return null;
42
+ }
43
+ const val = params.value;
44
+ return (
45
+ <Stack direction="row" spacing={2} alignItems="center">
46
+ {AssayIcon(val)}
47
+ <div>
48
+ <b>{val}</b>
49
+ </div>
50
+ </Stack>
51
+ );
49
52
  }
50
- }
53
+ },
54
+ };
51
55
 
52
56
  const defaultOntologyCol: GridColDef<RowInfo> = {
53
- field: "ontology",
54
- headerName: "Ontology",
55
- type: "singleSelect",
56
- valueOptions: ontologyTypes,
57
- renderCell: (params) => {
58
- if (params.rowNode.type === "group") {
59
- if (params.value === undefined) {
60
- return null;
61
- }
62
- const val = params.value;
63
- return (
64
- <div><b>{val}</b></div>
65
- )
66
- }
57
+ field: "ontology",
58
+ headerName: "Ontology",
59
+ type: "singleSelect",
60
+ valueOptions: ontologyTypes,
61
+ renderCell: (params) => {
62
+ if (params.rowNode.type === "group") {
63
+ if (params.value === undefined) {
64
+ return null;
65
+ }
66
+ const val = params.value;
67
+ return (
68
+ <div>
69
+ <b>{val}</b>
70
+ </div>
71
+ );
67
72
  }
73
+ },
68
74
  };
69
75
 
70
- const defaultAssayCol: GridColDef<RowInfo> = {
71
- field: "assay",
72
- headerName: "Assay",
73
- valueOptions: assayTypes,
74
- renderCell: (params) => {
75
- if (params.rowNode.type === "group") {
76
- if (params.value === undefined) {
77
- return null;
78
- }
79
- const val = params.value;
80
- return (
81
- <Stack direction="row" spacing={2} alignItems="center">
82
- {AssayIcon(val)}
83
- <div>{val}</div>
84
- </Stack>
85
- )
86
- }
76
+ const defaultAssayCol: GridColDef<RowInfo> = {
77
+ field: "assay",
78
+ headerName: "Assay",
79
+ valueOptions: assayTypes,
80
+ renderCell: (params) => {
81
+ if (params.value === undefined) {
82
+ return null;
87
83
  }
88
- }
84
+ const val = params.value;
85
+ return (
86
+ <Stack direction="row" spacing={2} alignItems="center" sx={{ ml: 6 }}>
87
+ {AssayIcon(val)}
88
+ <div>{val}</div>
89
+ </Stack>
90
+ );
91
+ },
92
+ };
89
93
 
90
94
  const sampleTypeCol: GridColDef<RowInfo> = {
91
- field: "sampleType",
92
- headerName: "Sample Type",
93
- type: "singleSelect",
94
- valueOptions: ["tissue", "primary cell", "cell line", "in vitro differentiated cells", "organoid"],
95
- valueFormatter: (value) => value && capitalize(value),
95
+ field: "sampleType",
96
+ headerName: "Sample Type",
97
+ type: "singleSelect",
98
+ valueOptions: [
99
+ "tissue",
100
+ "primary cell",
101
+ "cell line",
102
+ "in vitro differentiated cells",
103
+ "organoid",
104
+ ],
105
+ valueFormatter: (value) => value && capitalize(value),
96
106
  };
97
107
 
98
108
  const lifeStageCol: GridColDef<RowInfo> = {
99
- field: "lifeStage",
100
- headerName: "Life Stage",
101
- type: "singleSelect",
102
- valueOptions: ["adult", "embryonic"],
103
- valueFormatter: (value) => value && capitalize(value),
109
+ field: "lifeStage",
110
+ headerName: "Life Stage",
111
+ type: "singleSelect",
112
+ valueOptions: ["adult", "embryonic"],
113
+ valueFormatter: (value) => value && capitalize(value),
104
114
  };
105
115
 
106
- const experimentCol: GridColDef<RowInfo> = {
107
- field: "experimentAccession",
108
- headerName: "Experiment Accession"
109
- }
116
+ const experimentCol: GridColDef<RowInfo> = {
117
+ field: "experimentAccession",
118
+ headerName: "Experiment Accession",
119
+ };
110
120
 
111
121
  const fileCol: GridColDef<RowInfo> = {
112
- field: "fileAccession",
113
- headerName: "File Accession"
114
- }
122
+ field: "fileAccession",
123
+ headerName: "File Accession",
124
+ };
115
125
 
116
126
  export const sortedByAssayColumns: GridColDef<RowInfo>[] = [
117
- displayNameCol,
118
- sortedByAssayOntologyCol,
119
- sampleTypeCol,
120
- lifeStageCol,
121
- sortedByAssayAssayCol,
122
- experimentCol,
123
- fileCol
124
- ]
127
+ displayNameCol,
128
+ sortedByAssayOntologyCol,
129
+ sampleTypeCol,
130
+ lifeStageCol,
131
+ sortedByAssayAssayCol,
132
+ experimentCol,
133
+ fileCol,
134
+ ];
125
135
 
126
136
  export const defaultColumns: GridColDef<RowInfo>[] = [
127
- displayNameCol,
128
- sampleTypeCol,
129
- lifeStageCol,
130
- defaultOntologyCol,
131
- defaultAssayCol,
132
- experimentCol,
133
- fileCol
134
- ]
137
+ defaultAssayCol,
138
+ sampleTypeCol,
139
+ lifeStageCol,
140
+ defaultOntologyCol,
141
+ displayNameCol,
142
+ experimentCol,
143
+ fileCol,
144
+ ];