@@ -6,36 +6,51 @@ import React, {
6
6
forwardRef ,
7
7
useImperativeHandle ,
8
8
useRef ,
9
+ useState ,
9
10
} from 'react' ;
10
11
11
12
import { memoize } from 'lodash' ;
12
13
13
14
import { FacetedData , FacetedPlotRef , PlotRef } from '../types/plots' ;
14
15
import { PlotProps } from './PlotlyPlot' ;
15
16
17
+ import { FullScreenModal } from '@veupathdb/core-components' ;
18
+
16
19
type ComponentWithPlotRef < P > = ComponentType <
17
20
PropsWithoutRef < P > & RefAttributes < PlotRef >
18
21
> ;
19
22
20
23
export interface FacetedPlotProps < D , P extends PlotProps < D > > {
21
24
data ?: FacetedData < D > ;
22
25
component : ComponentWithPlotRef < P > ;
23
- props : P ;
26
+ componentProps : P ;
27
+ /** Provide modalComponentProps to activate click-to-expand
28
+ * These are the props the expanded plot inside the modal will receive
29
+ */
30
+ modalComponentProps ?: P ;
24
31
// custom legend prop
25
32
checkedLegendItems ?: string [ ] ;
26
33
}
27
34
35
+ export interface FacetedPlotPropsWithRef < D , P extends PlotProps < D > >
36
+ extends FacetedPlotProps < D , P > {
37
+ facetedPlotRef ?: Ref < FacetedPlotRef > ;
38
+ }
39
+
28
40
function renderFacetedPlot < D , P extends PlotProps < D > > (
29
41
props : FacetedPlotProps < D , P > ,
30
42
ref : Ref < FacetedPlotRef >
31
43
) {
32
44
const {
33
45
data,
34
46
component : Component ,
35
- props : componentProps ,
36
- checkedLegendItems : checkedLegendItems ,
47
+ componentProps,
48
+ modalComponentProps,
49
+ checkedLegendItems,
37
50
} = props ;
38
51
const plotRefs = useRef < FacetedPlotRef > ( [ ] ) ;
52
+ const [ modalIsOpen , setModalIsOpen ] = useState ( false ) ;
53
+ const [ modalPlot , setModalPlot ] = useState < React . ReactNode | null > ( null ) ;
39
54
40
55
useImperativeHandle < FacetedPlotRef , FacetedPlotRef > (
41
56
ref ,
@@ -60,27 +75,77 @@ function renderFacetedPlot<D, P extends PlotProps<D>>(
60
75
overflow : 'auto' ,
61
76
} }
62
77
>
63
- { data ?. facets . map ( ( { data, label } , index ) => (
64
- < Component
65
- { ...componentProps }
66
- ref = { ( plotInstance ) => {
67
- if ( plotInstance == null ) {
68
- delete plotRefs . current [ index ] ;
69
- } else {
70
- plotRefs . current [ index ] = plotInstance ;
71
- }
72
- } }
73
- key = { index }
74
- data = { data }
75
- title = { label }
76
- displayLegend = { false }
77
- interactive = { false }
78
+ { data ?. facets . map ( ( { data, label } , index ) => {
79
+ const sharedProps = {
80
+ data : data ,
78
81
// pass checkedLegendItems to PlotlyPlot
79
- checkedLegendItems = { checkedLegendItems }
80
- showNoDataOverlay = { data == null }
81
- />
82
- ) ) }
82
+ checkedLegendItems : checkedLegendItems ,
83
+ showNoDataOverlay : data == null ,
84
+ } ;
85
+
86
+ const divModalProps = modalComponentProps && {
87
+ onClick : ( ) => {
88
+ setModalPlot (
89
+ < Component
90
+ { ...sharedProps }
91
+ displayLegend = { true }
92
+ interactive = { true }
93
+ { ...modalComponentProps }
94
+ title = { label }
95
+ />
96
+ ) ;
97
+ setModalIsOpen ( true ) ;
98
+ } ,
99
+ title : 'Click to expand' ,
100
+ } ;
101
+
102
+ return (
103
+ < div
104
+ { ...divModalProps }
105
+ key = { index }
106
+ style = { {
107
+ marginRight : 15 ,
108
+ cursor : modalComponentProps && 'pointer' ,
109
+ } }
110
+ >
111
+ < Component
112
+ { ...sharedProps }
113
+ ref = { ( plotInstance ) => {
114
+ if ( plotInstance == null ) {
115
+ delete plotRefs . current [ index ] ;
116
+ } else {
117
+ plotRefs . current [ index ] = plotInstance ;
118
+ }
119
+ } }
120
+ displayLegend = { false }
121
+ interactive = { false }
122
+ { ...componentProps }
123
+ title = { label }
124
+ />
125
+ </ div >
126
+ ) ;
127
+ } ) }
83
128
</ div >
129
+ { modalComponentProps && (
130
+ < FullScreenModal visible = { modalIsOpen } >
131
+ < button
132
+ onClick = { ( ) => setModalIsOpen ( false ) }
133
+ style = { {
134
+ position : 'absolute' ,
135
+ top : 30 ,
136
+ right : 30 ,
137
+ backgroundColor : 'white' ,
138
+ cursor : 'pointer' ,
139
+ border : 'none' ,
140
+ zIndex : 2000 ,
141
+ } }
142
+ title = "Close expanded plot"
143
+ >
144
+ < i className = "fas fa-times fa-lg" > </ i >
145
+ </ button >
146
+ { modalPlot }
147
+ </ FullScreenModal >
148
+ ) }
84
149
</ >
85
150
) ;
86
151
}
@@ -101,7 +166,7 @@ const makeFacetedPlotComponent = memoize(function <D, P extends PlotProps<D>>(
101
166
} ) ;
102
167
103
168
export default function FacetedPlot < D , P extends PlotProps < D > > (
104
- props : FacetedPlotProps < D , P > & { facetedPlotRef ?: Ref < FacetedPlotRef > }
169
+ props : FacetedPlotPropsWithRef < D , P >
105
170
) {
106
171
const FacetedPlotComponent = makeFacetedPlotComponent < D , P > ( props . component ) ;
107
172
0 commit comments