1
1
import classNames from 'classnames' ;
2
- import { PureComponent , CSSProperties } from 'react' ;
3
- import * as React from 'react' ;
2
+ import React , { PureComponent , CSSProperties } from 'react' ;
4
3
import ReactGridLayout , { ItemCallback } from 'react-grid-layout' ;
4
+ import { connect } from 'react-redux' ;
5
+ import AutoSizer from 'react-virtualized-auto-sizer' ;
5
6
import { Subscription } from 'rxjs' ;
6
7
7
8
import { config } from '@grafana/runtime' ;
8
- import appEvents from 'app/core/app_events' ;
9
9
import { GRID_CELL_HEIGHT , GRID_CELL_VMARGIN , GRID_COLUMN_COUNT } from 'app/core/constants' ;
10
10
import { contextSrv } from 'app/core/services/context_srv' ;
11
- import { VariablesChanged } from 'app/features/variables /types' ;
11
+ import { StoreState } from 'app/types' ;
12
12
import { DashboardPanelsChangedEvent } from 'app/types/events' ;
13
13
14
14
import { AddLibraryPanelWidget } from '../components/AddLibraryPanelWidget' ;
15
+ import { AddPanelWidget } from '../components/AddPanelWidget' ;
15
16
import { DashboardRow } from '../components/DashboardRow' ;
16
17
import { DashboardModel , PanelModel } from '../state' ;
17
18
import { GridPos } from '../state/PanelModel' ;
18
19
19
20
import DashboardEmpty from './DashboardEmpty' ;
20
21
import { DashboardPanel } from './DashboardPanel' ;
21
22
22
- export const PANEL_FILTER_VARIABLE = 'systemPanelFilterVar' ;
23
-
24
23
export interface Props {
25
24
dashboard : DashboardModel ;
26
25
isEditable : boolean ;
27
26
editPanel : PanelModel | null ;
28
27
viewPanel : PanelModel | null ;
29
28
hidePanelMenus ?: boolean ;
29
+ isFnDashboard ?: boolean ;
30
30
}
31
31
32
- interface State {
33
- panelFilter ?: RegExp ;
34
- width : number ;
35
- }
36
-
37
- export class DashboardGrid extends PureComponent < Props , State > {
32
+ export class Component extends PureComponent < Props > {
38
33
private panelMap : { [ key : string ] : PanelModel } = { } ;
39
34
private eventSubs = new Subscription ( ) ;
40
35
private windowHeight = 1200 ;
@@ -46,67 +41,21 @@ export class DashboardGrid extends PureComponent<Props, State> {
46
41
47
42
constructor ( props : Props ) {
48
43
super ( props ) ;
49
- this . state = {
50
- panelFilter : undefined ,
51
- width : document . body . clientWidth , // initial very rough estimate
52
- } ;
53
44
}
54
45
55
46
componentDidMount ( ) {
56
47
const { dashboard } = this . props ;
57
-
58
- if ( config . featureToggles . panelFilterVariable ) {
59
- // If panel filter variable is set on load then
60
- // update state to filter panels
61
- for ( const variable of dashboard . getVariables ( ) ) {
62
- if ( variable . id === PANEL_FILTER_VARIABLE ) {
63
- if ( 'query' in variable ) {
64
- this . setPanelFilter ( variable . query ) ;
65
- }
66
- break ;
67
- }
68
- }
69
-
70
- this . eventSubs . add (
71
- appEvents . subscribe ( VariablesChanged , ( e ) => {
72
- if ( e . payload . variable ?. id === PANEL_FILTER_VARIABLE ) {
73
- if ( 'current' in e . payload . variable ) {
74
- let variable = e . payload . variable . current ;
75
- if ( 'value' in variable && typeof variable . value === 'string' ) {
76
- this . setPanelFilter ( variable . value ) ;
77
- }
78
- }
79
- }
80
- } )
81
- ) ;
82
- }
83
-
84
48
this . eventSubs . add ( dashboard . events . subscribe ( DashboardPanelsChangedEvent , this . triggerForceUpdate ) ) ;
85
49
}
86
50
87
51
componentWillUnmount ( ) {
88
52
this . eventSubs . unsubscribe ( ) ;
89
53
}
90
54
91
- setPanelFilter ( regex : string ) {
92
- // Only set the panels filter if the systemPanelFilterVar variable
93
- // is a non-empty string
94
- let panelFilter = undefined ;
95
- if ( regex . length > 0 ) {
96
- panelFilter = new RegExp ( regex , 'i' ) ;
97
- }
98
-
99
- this . setState ( {
100
- panelFilter : panelFilter ,
101
- } ) ;
102
- }
103
-
104
55
buildLayout ( ) {
105
56
const layout : ReactGridLayout . Layout [ ] = [ ] ;
106
57
this . panelMap = { } ;
107
- const { panelFilter } = this . state ;
108
58
109
- let count = 0 ;
110
59
for ( const panel of this . props . dashboard . panels ) {
111
60
if ( ! panel . key ) {
112
61
panel . key = `panel-${ panel . id } -${ Date . now ( ) } ` ;
@@ -133,27 +82,13 @@ export class DashboardGrid extends PureComponent<Props, State> {
133
82
panelPos . isDraggable = panel . collapsed ;
134
83
}
135
84
136
- if ( ! panelFilter ) {
137
- layout . push ( panelPos ) ;
138
- } else {
139
- if ( panelFilter . test ( panel . title ) ) {
140
- panelPos . isResizable = false ;
141
- panelPos . isDraggable = false ;
142
- panelPos . x = ( count % 2 ) * GRID_COLUMN_COUNT ;
143
- panelPos . y = Math . floor ( count / 2 ) ;
144
- layout . push ( panelPos ) ;
145
- count ++ ;
146
- }
147
- }
85
+ layout . push ( panelPos ) ;
148
86
}
149
87
150
88
return layout ;
151
89
}
152
90
153
91
onLayoutChange = ( newLayout : ReactGridLayout . Layout [ ] ) => {
154
- if ( this . state . panelFilter ) {
155
- return ;
156
- }
157
92
for ( const newPos of newLayout ) {
158
93
this . panelMap [ newPos . i ! ] . updateGridPos ( newPos , this . isLayoutInitialized ) ;
159
94
}
@@ -205,7 +140,6 @@ export class DashboardGrid extends PureComponent<Props, State> {
205
140
}
206
141
207
142
renderPanels ( gridWidth : number , isDashboardDraggable : boolean ) {
208
- const { panelFilter } = this . state ;
209
143
const panelElements = [ ] ;
210
144
211
145
// Reset last panel bottom
@@ -222,7 +156,7 @@ export class DashboardGrid extends PureComponent<Props, State> {
222
156
for ( const panel of this . props . dashboard . panels ) {
223
157
const panelClasses = classNames ( { 'react-grid-item--fullscreen' : panel . isViewing } ) ;
224
158
225
- const p = (
159
+ panelElements . push (
226
160
< GrafanaGridItem
227
161
key = { panel . key }
228
162
className = { panelClasses }
@@ -238,14 +172,6 @@ export class DashboardGrid extends PureComponent<Props, State> {
238
172
} }
239
173
</ GrafanaGridItem >
240
174
) ;
241
-
242
- if ( ! panelFilter ) {
243
- panelElements . push ( p ) ;
244
- } else {
245
- if ( panelFilter . test ( panel . title ) ) {
246
- panelElements . push ( p ) ;
247
- }
248
- }
249
175
}
250
176
251
177
return panelElements ;
@@ -256,6 +182,11 @@ export class DashboardGrid extends PureComponent<Props, State> {
256
182
return < DashboardRow key = { panel . key } panel = { panel } dashboard = { this . props . dashboard } /> ;
257
183
}
258
184
185
+ // Todo: Remove this when we remove the emptyDashboardPage toggle
186
+ if ( panel . type === 'add-panel' ) {
187
+ return < AddPanelWidget key = { panel . key } panel = { panel } dashboard = { this . props . dashboard } /> ;
188
+ }
189
+
259
190
if ( panel . type === 'add-library-panel' ) {
260
191
return < AddLibraryPanelWidget key = { panel . key } panel = { panel } dashboard = { this . props . dashboard } /> ;
261
192
}
@@ -288,69 +219,61 @@ export class DashboardGrid extends PureComponent<Props, State> {
288
219
}
289
220
} ;
290
221
291
- private resizeObserver ?: ResizeObserver ;
292
- private rootEl : HTMLDivElement | null = null ;
293
- onMeasureRef = ( rootEl : HTMLDivElement | null ) => {
294
- if ( ! rootEl ) {
295
- if ( this . rootEl && this . resizeObserver ) {
296
- this . resizeObserver . unobserve ( this . rootEl ) ;
297
- }
298
- return ;
299
- }
300
-
301
- this . rootEl = rootEl ;
302
- this . resizeObserver = new ResizeObserver ( ( entries ) => {
303
- entries . forEach ( ( entry ) => {
304
- this . setState ( { width : entry . contentRect . width } ) ;
305
- } ) ;
306
- } ) ;
307
-
308
- this . resizeObserver . observe ( rootEl ) ;
309
- } ;
310
-
311
222
render ( ) {
312
- const { isEditable, dashboard } = this . props ;
313
- const { width } = this . state ;
223
+ const { isEditable, dashboard, isFnDashboard } = this . props ;
314
224
315
- if ( dashboard . panels . length === 0 ) {
225
+ if ( config . featureToggles . emptyDashboardPage && dashboard . panels . length === 0 ) {
316
226
return < DashboardEmpty dashboard = { dashboard } canCreate = { isEditable } /> ;
317
227
}
318
228
319
- const draggable = width <= config . theme2 . breakpoints . values . md ? false : isEditable ;
320
-
321
- // pos: rel + z-index is required to create a new stacking context to contain
322
- // the escalating z-indexes of the panels
229
+ /**
230
+ * We have a parent with "flex: 1 1 0" we need to reset it to "flex: 1 1 auto" to have the AutoSizer
231
+ * properly working. For more information go here:
232
+ * https://github.com/bvaughn/react-virtualized/blob/master/docs/usingAutoSizer.md#can-i-use-autosizer-within-a-flex-container
233
+ */
323
234
return (
324
- < div
325
- ref = { this . onMeasureRef }
326
- style = { {
327
- flex : '1 1 auto' ,
328
- position : 'relative' ,
329
- zIndex : 1 ,
330
- display : this . props . editPanel ? 'none' : undefined ,
331
- } }
332
- >
333
- < div style = { { width : width , height : '100%' } } ref = { this . onGetWrapperDivRef } >
334
- < ReactGridLayout
335
- width = { width }
336
- isDraggable = { draggable }
337
- isResizable = { isEditable }
338
- containerPadding = { [ 0 , 0 ] }
339
- useCSSTransforms = { true }
340
- margin = { [ GRID_CELL_VMARGIN , GRID_CELL_VMARGIN ] }
341
- cols = { GRID_COLUMN_COUNT }
342
- rowHeight = { GRID_CELL_HEIGHT }
343
- draggableHandle = ".grid-drag-handle"
344
- draggableCancel = ".grid-drag-cancel"
345
- layout = { this . buildLayout ( ) }
346
- onDragStop = { this . onDragStop }
347
- onResize = { this . onResize }
348
- onResizeStop = { this . onResizeStop }
349
- onLayoutChange = { this . onLayoutChange }
350
- >
351
- { this . renderPanels ( width , draggable ) }
352
- </ ReactGridLayout >
353
- </ div >
235
+ < div style = { { flex : '1 1 auto' , display : this . props . editPanel ? 'none' : undefined } } >
236
+ < AutoSizer disableHeight >
237
+ { ( { width } ) => {
238
+ if ( width === 0 ) {
239
+ return null ;
240
+ }
241
+
242
+ // Disable draggable if mobile device, solving an issue with unintentionally
243
+ // moving panels. https://github.com/grafana/grafana/issues/18497
244
+ const isLg = width <= config . theme2 . breakpoints . values . md ;
245
+ const draggable = isLg ? false : isEditable ;
246
+
247
+ return (
248
+ /**
249
+ * The children is using a width of 100% so we need to guarantee that it is wrapped
250
+ * in an element that has the calculated size given by the AutoSizer. The AutoSizer
251
+ * has a width of 0 and will let its content overflow its div.
252
+ */
253
+ < div style = { { width : width , height : '100%' } } ref = { this . onGetWrapperDivRef } >
254
+ < ReactGridLayout
255
+ width = { width }
256
+ isDraggable = { isFnDashboard ? false : draggable }
257
+ isResizable = { isFnDashboard ? false : isEditable }
258
+ containerPadding = { [ 0 , 0 ] }
259
+ useCSSTransforms = { true }
260
+ margin = { [ GRID_CELL_VMARGIN , GRID_CELL_VMARGIN ] }
261
+ cols = { GRID_COLUMN_COUNT }
262
+ rowHeight = { GRID_CELL_HEIGHT }
263
+ draggableHandle = ".grid-drag-handle"
264
+ draggableCancel = ".grid-drag-cancel"
265
+ layout = { this . buildLayout ( ) }
266
+ onDragStop = { this . onDragStop }
267
+ onResize = { this . onResize }
268
+ onResizeStop = { this . onResizeStop }
269
+ onLayoutChange = { this . onLayoutChange }
270
+ >
271
+ { this . renderPanels ( width , draggable ) }
272
+ </ ReactGridLayout >
273
+ </ div >
274
+ ) ;
275
+ } }
276
+ </ AutoSizer >
354
277
</ div >
355
278
) ;
356
279
}
@@ -362,7 +285,7 @@ interface GrafanaGridItemProps extends React.HTMLAttributes<HTMLDivElement> {
362
285
isViewing : boolean ;
363
286
windowHeight : number ;
364
287
windowWidth : number ;
365
- children : any ; // eslint-disable-line @typescript-eslint/no-explicit-any
288
+ children : any ;
366
289
}
367
290
368
291
/**
@@ -403,7 +326,7 @@ const GrafanaGridItem = React.forwardRef<HTMLDivElement, GrafanaGridItemProps>((
403
326
404
327
// props.children[0] is our main children. RGL adds the drag handle at props.children[1]
405
328
return (
406
- < div { ...divProps } style = { { ... divProps . style } } ref = { ref } >
329
+ < div { ...divProps } ref = { ref } >
407
330
{ /* Pass width and height to children as render props */ }
408
331
{ [ props . children [ 0 ] ( width , height ) , props . children . slice ( 1 ) ] }
409
332
</ div >
@@ -418,3 +341,11 @@ function translateGridHeightToScreenHeight(gridHeight: number): number {
418
341
}
419
342
420
343
GrafanaGridItem . displayName = 'GridItemWithDimensions' ;
344
+
345
+ function mapStateToProps ( ) {
346
+ return ( state : StoreState ) => ( {
347
+ isFnDashboard : state . fnGlobalState . FNDashboard ,
348
+ } ) ;
349
+ }
350
+
351
+ export const DashboardGrid = connect ( mapStateToProps ) ( Component ) ;
0 commit comments