@weng-lab/genomebrowser-ui 0.1.5 → 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.5",
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,63 +19,58 @@ 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) => <CustomToolbar {...props} {...customToolbarProps} />;
47
- }, [label, labelTooltip, toolbarSlot]);
22
+ const { sortedAssay, handleSelection, rows, selectedIds } = props;
48
23
 
49
24
  const apiRef = useGridApiRef();
50
- const groupingModel = sortedAssay ? ["assay", "ontology"] : ["ontology", "assay"];
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
+
33
+ const groupingModel = sortedAssay
34
+ ? ["assay", "ontology"]
35
+ : ["ontology", "displayname"];
51
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
52
43
 
53
- // functions to customize the column and filter panel in the toolbar
44
+ const [columnVisibilityModel, setColumnVisibilityModel] =
45
+ useState<GridColumnVisibilityModel>(baseVisibility);
46
+
47
+ // Update visibility when sort mode changes
48
+ useEffect(() => {
49
+ setColumnVisibilityModel(baseVisibility);
50
+ }, [sortedAssay]);
51
+
52
+ // functions to customize the column and filter panel in the toolbar
54
53
  const filterColumns = ({ columns }: FilterColumnsArgs) => {
55
- return columns.filter((column) => column.type !== 'custom').map((column) => column.field);
54
+ return columns
55
+ .filter((column) => column.type !== "custom")
56
+ .map((column) => column.field);
56
57
  };
57
58
 
58
59
  const getTogglableColumns = (columns: GridColDef[]) => {
59
- return columns.filter((column) => column.type !== 'custom').map((column) => column.field);
60
+ return columns
61
+ .filter((column) => column.type !== "custom")
62
+ .map((column) => column.field);
60
63
  };
61
64
 
62
- const handleResizeCols = () => {
63
- // need to check .autosizeColumns since the current was being set with an empty object
64
- if (!apiRef.current?.autosizeColumns) return;
65
- apiRef.current.autosizeColumns(autosizeOptions);
66
- };
67
-
68
- // trigger resize when rows or columns change so that rows/columns don't need to be memoized outisde of this component
69
- // otherwise sometimes would snap back to default widths when rows/columns change
70
- useEffect(() => {
71
- handleResizeCols();
72
- }, [rows, defaultColumns, sortedByAssayColumns, handleResizeCols]);
73
-
74
65
  return (
75
66
  <Paper sx={{ width: "100%" }}>
76
- <Box sx={{
77
- height: 500,
78
- width: "100%",
79
- overflow: "auto",
80
- }}>
67
+ <Box
68
+ sx={{
69
+ height: 500,
70
+ width: "100%",
71
+ overflow: "auto",
72
+ }}
73
+ >
81
74
  <DataGridPremium
82
75
  apiRef={apiRef}
83
76
  rows={rows}
@@ -85,13 +78,15 @@ export function DataGridWrapper(props: DataGridProps) {
85
78
  getRowId={(row) => row.experimentAccession}
86
79
  autosizeOptions={autosizeOptions}
87
80
  rowGroupingModel={groupingModel}
88
- groupingColDef={{ leafField: "displayname", display: "flex" }}
89
- columnVisibilityModel={{ displayname: false }} // so you don't see a second name column
81
+ groupingColDef={{ leafField, display: "flex" }}
82
+ columnVisibilityModel={columnVisibilityModel}
83
+ onColumnVisibilityModelChange={setColumnVisibilityModel}
90
84
  onRowSelectionModelChange={handleSelection}
91
- rowSelectionPropagation={{ descendants: true }}
92
- rowSelectionModel={{ type: "include", ids: new Set(selectedTracks.keys()) }}
93
- slots={{
94
- toolbar: CustomToolbarWrapper,
85
+ rowSelectionPropagation={{ descendants: true, parents: true }}
86
+ disableRowGrouping={false}
87
+ rowSelectionModel={{
88
+ type: "include",
89
+ ids: selectedIds,
95
90
  }}
96
91
  slotProps={{
97
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
+ ];