From 4c52ada7a94842911c1d08c27ebead8dcefda44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 28 Oct 2021 13:02:01 +0200 Subject: [PATCH] Dashboard: Fixes lazy loading of panels not in view for mobile layout (#41009) * Dashboard: Fixes lazy loading of panels not in view for mobile layout * Refactoring and adding more comments * Removed console.log --- .../dashboard/dashgrid/DashboardGrid.tsx | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index acae4b650f2..f37f9c32fce 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -34,6 +34,8 @@ export class DashboardGrid extends PureComponent { private windowHeight = 1200; private windowWidth = 1920; private gridWidth = 0; + /** Used to keep track of mobile panel layout position */ + private lastPanelBottom = 0; constructor(props: Props) { super(props); @@ -123,36 +125,55 @@ export class DashboardGrid extends PureComponent { this.updateGridPos(newItem, layout); }; - isInView(panel: PanelModel) { + isInView(panel: PanelModel, gridWidth: number) { if (panel.isViewing || panel.isEditing) { return true; } const scrollTop = this.props.scrollTop; - const panelTop = panel.gridPos.y * (GRID_CELL_HEIGHT + GRID_CELL_VMARGIN); - const panelBottom = panelTop + panel.gridPos.h * (GRID_CELL_HEIGHT + GRID_CELL_VMARGIN) - GRID_CELL_VMARGIN; + const screenPos = this.getPanelScreenPos(panel, gridWidth); // Show things that are almost in the view const buffer = 100; // The panel is above the viewport - if (scrollTop > panelBottom + buffer) { + if (scrollTop > screenPos.bottom + buffer) { return false; } const scrollViewBottom = scrollTop + this.windowHeight; // Panel is below view - if (panelTop > scrollViewBottom + buffer) { + if (screenPos.top > scrollViewBottom + buffer) { return false; } return !this.props.dashboard.otherPanelInFullscreen(panel); } + getPanelScreenPos(panel: PanelModel, gridWidth: number): { top: number; bottom: number } { + let top = 0; + + // mobile layout + if (gridWidth < config.theme2.breakpoints.values.md) { + // In mobile layout panels are stacked so we just add the panel vertical margin to the last panel bottom position + top = this.lastPanelBottom + GRID_CELL_VMARGIN; + } else { + // For top position we need to add back the vertical margin removed by translateGridHeightToScreenHeight + top = translateGridHeightToScreenHeight(panel.gridPos.y) + GRID_CELL_VMARGIN; + } + + this.lastPanelBottom = top + translateGridHeightToScreenHeight(panel.gridPos.h); + + return { top, bottom: this.lastPanelBottom }; + } + renderPanels(gridWidth: number) { const panelElements = []; + // Reset last panel bottom + this.lastPanelBottom = 0; + // This is to avoid layout re-flows, accessing window.innerHeight can trigger re-flow // We assume here that if width change height might have changed as well if (this.gridWidth !== gridWidth) { @@ -165,7 +186,7 @@ export class DashboardGrid extends PureComponent { const panelClasses = classNames({ 'react-grid-item--fullscreen': panel.isViewing }); // Update is in view state - panel.isInView = this.isInView(panel); + panel.isInView = this.isInView(panel, gridWidth); panelElements.push( (( const style: CSSProperties = props.style ?? {}; if (isViewing) { + // In fullscreen view mode a single panel take up full width & 85% height width = gridWidth!; height = windowHeight * 0.85; style.height = height; style.width = '100%'; } else if (windowWidth < theme.breakpoints.values.md) { + // Mobile layout is a bit different, every panel take up full width width = props.gridWidth!; - height = props.gridPos!.h * (GRID_CELL_HEIGHT + GRID_CELL_VMARGIN) - GRID_CELL_VMARGIN; + height = translateGridHeightToScreenHeight(gridPos!.h); style.height = height; style.width = '100%'; } else { - // RGL passes width and height directly to children as style props. + // Normal grid layout. The grid framework passes width and height directly to children as style props. width = parseFloat(props.style.width); height = parseFloat(props.style.height); } @@ -314,4 +337,11 @@ const GrafanaGridItem = React.forwardRef(( ); }); +/** + * This translates grid height dimensions to real pixels + */ +function translateGridHeightToScreenHeight(gridHeight: number): number { + return gridHeight * (GRID_CELL_HEIGHT + GRID_CELL_VMARGIN) - GRID_CELL_VMARGIN; +} + GrafanaGridItem.displayName = 'GridItemWithDimensions';