Skip to content

Commit a8d794f

Browse files
committed
selectable channel types
1 parent 51ba44d commit a8d794f

13 files changed

Lines changed: 415 additions & 181 deletions

File tree

modules/electrophysiology_browser/jsx/electrophysiologySessionView.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,10 @@ class ElectrophysiologySessionView extends Component {
358358
eegMontage,
359359
} = this.state.database[i];
360360
const file = this.state.database[i].file;
361+
const channelsURL = `${loris.BaseURL}/api/v0.0.4-dev/candidates`
362+
+ `/${this.state.patient.info.pscid}`
363+
+ `/${this.state.patient.info.visit_label}/recordings/${file.name}`
364+
+ `/channels`;
361365
const splitPagination = [];
362366
for (const j of Array(file.splitData?.splitCount).keys()) {
363367
splitPagination.push(
@@ -403,6 +407,7 @@ class ElectrophysiologySessionView extends Component {
403407
{EEG_VIS_ENABLED &&
404408
<div className="react-series-data-viewer-scoped col-xs-12">
405409
<EEGLabSeriesProvider
410+
channelsURL={channelsURL}
406411
chunksURL={
407412
chunksURLs?.[file.splitData?.splitIndex] || chunksURLs
408413
}

modules/electrophysiology_browser/jsx/react-series-data-viewer/src/eeglab/EEGLabSeriesProvider.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ import {setDomain, setInterval} from '../series/store/state/bounds';
2222
import {
2323
setCoordinateSystem, setElectrodes,
2424
} from '../series/store/state/montage';
25-
import {EventMetadata, HEDSchemaElement} from '../series/store/types';
25+
import {
26+
ChannelInfos, EventMetadata, HEDSchemaElement,
27+
} from '../series/store/types';
2628
import TriggerableModal from 'jsx/TriggerableModal';
2729
import DatasetTagger from '../series/components/DatasetTagger';
2830
import {InfoIcon} from '../series/components/components';
@@ -35,6 +37,7 @@ declare global {
3537

3638

3739
type CProps = {
40+
channelsURL: string,
3841
chunksURL: string,
3942
epochsURL: string,
4043
electrodesURL: string,
@@ -178,6 +181,12 @@ class EEGLabSeriesProvider extends Component<CProps, any> {
178181
}
179182
};
180183

184+
fetchJSON(props.channelsURL).then((json: ChannelInfos) => {
185+
this.store.dispatch(setDatasetMetadata({
186+
bidsChannels: json.Channels,
187+
}));
188+
});
189+
181190
Promise.race(racers(fetchJSON, chunksURL, '/index.json')).then(
182191
({json, url}) => {
183192
if (json) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import {useTranslation} from "node_modules/react-i18next";
2+
import {ChannelTypeState} from "./SeriesRenderer";
3+
4+
/**
5+
* Component that displays the list of channel types present in the acquisition and
6+
* allows to configure which ones should be displayed or not.
7+
*/
8+
const ChannelTypesSelector = ({channelTypes, setChannelTypes}: {
9+
channelTypes: Record<string, ChannelTypeState>,
10+
setChannelTypes: React.Dispatch<React.SetStateAction<Record<string, ChannelTypeState>>>,
11+
}) => {
12+
const {t} = useTranslation();
13+
return (
14+
<div style={{position: 'relative'}}>
15+
<button className="btn btn-primary dropdown" data-toggle="dropdown">
16+
{t('Channel Types')}
17+
</button>
18+
<ul className="dropdown-menu">
19+
{Object.entries(channelTypes).map(([name, {visible, channelsCount}]) => (
20+
<li key={name} style={{
21+
display: 'flex',
22+
justifyContent: 'space-between',
23+
padding: '0.75rem 1.5rem',
24+
}}>
25+
<span>{name} ({channelsCount})</span>
26+
<input
27+
type="checkbox"
28+
checked={visible || false}
29+
onClick={
30+
// Do not collapse the dropdown on click.
31+
(e) => e.stopPropagation()
32+
}
33+
onChange={(e) => {
34+
setChannelTypes((channelTypes) => ({
35+
...channelTypes,
36+
[name]: {
37+
...channelTypes[name],
38+
visible: e.target.checked,
39+
},
40+
}));
41+
}}
42+
/>
43+
</li>
44+
))}
45+
</ul>
46+
</div>
47+
);
48+
}
49+
50+
export default ChannelTypesSelector;
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import React from 'react';
2+
import {useTranslation} from "react-i18next";
3+
import {CHANNEL_DISPLAY_OPTIONS} from "../../vector";
4+
import {RightPanel} from "../store/types";
5+
6+
/**
7+
* Pagination component that provides controls for selecting how many channels
8+
* should be displayed at once, and navigating through the paginated channels.
9+
*/
10+
function Pagination({
11+
limit,
12+
selectedChannelsCount,
13+
visibleChannelsCount,
14+
offsetIndex,
15+
setOffsetIndex,
16+
displayedChannelsLimit,
17+
setDisplayedChannelsLimit,
18+
rightPanel,
19+
}: {
20+
limit: number,
21+
selectedChannelsCount: number,
22+
visibleChannelsCount: number,
23+
offsetIndex: number,
24+
setOffsetIndex: (_: number) => void,
25+
displayedChannelsLimit: number,
26+
setDisplayedChannelsLimit: (_: number) => void,
27+
rightPanel: RightPanel,
28+
}) {
29+
const {t} = useTranslation();
30+
31+
const hardLimit = Math.min(offsetIndex + limit - 1, visibleChannelsCount);
32+
33+
return (
34+
<div
35+
className={
36+
(rightPanel ? '' : 'pull-right-lg col-lg-5 ') + 'pagination-nav'
37+
}
38+
style={{padding: '5px 15px'}}
39+
>
40+
<small style={{marginRight: '3px'}}>
41+
{t('Displaying: ', {ns: 'electrophysiology_browser'})}
42+
<select
43+
value={displayedChannelsLimit}
44+
onChange={(e) => {
45+
const displayedChannelsLimit = parseInt(e.target.value, 10);
46+
setDisplayedChannelsLimit(displayedChannelsLimit);
47+
}}
48+
>
49+
{CHANNEL_DISPLAY_OPTIONS.map((numChannels) => (
50+
<option
51+
key={numChannels}
52+
value={numChannels}
53+
>
54+
{t('{{numChannels}} channels', {
55+
ns: 'electrophysiology_browser',
56+
numChannels: numChannels,
57+
})}
58+
</option>
59+
))};
60+
</select>
61+
&nbsp;
62+
{t('Showing:', {ns: 'electrophysiology_browser'})}
63+
&nbsp;
64+
<input
65+
type='number'
66+
style={{width: '45px'}}
67+
value={offsetIndex}
68+
onChange={(e) => {
69+
const value = parseInt(e.target.value);
70+
!isNaN(value) && setOffsetIndex(value);
71+
}}
72+
/>
73+
&nbsp;
74+
{t('to {{channelsInView}} of {{totalChannels}}', {
75+
ns: 'electrophysiology_browser',
76+
channelsInView: hardLimit,
77+
totalChannels: selectedChannelsCount
78+
})}
79+
</small>
80+
<div
81+
className='btn-group'
82+
style={{marginRight: 0}}
83+
>
84+
<input
85+
type='button'
86+
className='btn btn-primary btn-xs'
87+
onClick={() => setOffsetIndex(offsetIndex - limit)}
88+
value='<<'
89+
/>
90+
<input
91+
type='button'
92+
className='btn btn-primary btn-xs'
93+
onClick={() => setOffsetIndex(offsetIndex - 1)}
94+
value='<'
95+
/>
96+
<input
97+
type='button'
98+
className='btn btn-primary btn-xs'
99+
onClick={() => setOffsetIndex(offsetIndex + 1)}
100+
value='>'
101+
/>
102+
<input
103+
type='button'
104+
className='btn btn-primary btn-xs'
105+
onClick={() => setOffsetIndex(offsetIndex + limit)}
106+
value='>>'
107+
/>
108+
</div>
109+
</div>
110+
);
111+
}
112+
113+
export default Pagination;

0 commit comments

Comments
 (0)