Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] [Dashboard] [Collapsable Panels] Reduce re-renders (#197343) #198219

Merged
merged 1 commit into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions packages/kbn-grid-layout/grid/drag_preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import React, { useEffect, useRef } from 'react';
import { combineLatest, skip } from 'rxjs';

import { transparentize } from '@elastic/eui';
import { css } from '@emotion/react';
import { euiThemeVars } from '@kbn/ui-theme';

import { GridLayoutStateManager } from './types';

export const DragPreview = ({
rowIndex,
gridLayoutStateManager,
}: {
rowIndex: number;
gridLayoutStateManager: GridLayoutStateManager;
}) => {
const dragPreviewRef = useRef<HTMLDivElement | null>(null);

useEffect(
() => {
/** Update the styles of the drag preview via a subscription to prevent re-renders */
const styleSubscription = combineLatest([
gridLayoutStateManager.activePanel$,
gridLayoutStateManager.gridLayout$,
])
.pipe(skip(1)) // skip the first emit because the drag preview is only rendered after a user action
.subscribe(([activePanel, gridLayout]) => {
if (!dragPreviewRef.current) return;

if (!activePanel || !gridLayout[rowIndex].panels[activePanel.id]) {
dragPreviewRef.current.style.display = 'none';
} else {
const panel = gridLayout[rowIndex].panels[activePanel.id];
dragPreviewRef.current.style.display = 'block';
dragPreviewRef.current.style.gridColumnStart = `${panel.column + 1}`;
dragPreviewRef.current.style.gridColumnEnd = `${panel.column + 1 + panel.width}`;
dragPreviewRef.current.style.gridRowStart = `${panel.row + 1}`;
dragPreviewRef.current.style.gridRowEnd = `${panel.row + 1 + panel.height}`;
}
});

return () => {
styleSubscription.unsubscribe();
};
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);

return (
<div
ref={dragPreviewRef}
css={css`
display: none;
pointer-events: none;
border-radius: ${euiThemeVars.euiBorderRadius};
background-color: ${transparentize(euiThemeVars.euiColorSuccess, 0.2)};
transition: opacity 100ms linear;
`}
/>
);
};
37 changes: 25 additions & 12 deletions packages/kbn-grid-layout/grid/grid_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import React from 'react';

import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import React, { useEffect, useState } from 'react';
import { distinctUntilChanged, map, skip } from 'rxjs';
import { v4 as uuidv4 } from 'uuid';

import { GridHeightSmoother } from './grid_height_smoother';
import { GridRow } from './grid_row';
Expand All @@ -29,12 +29,28 @@ export const GridLayout = ({
});
useGridLayoutEvents({ gridLayoutStateManager });

const [gridLayout, runtimeSettings, interactionEvent] = useBatchedPublishingSubjects(
gridLayoutStateManager.gridLayout$,
gridLayoutStateManager.runtimeSettings$,
gridLayoutStateManager.interactionEvent$
const [rowCount, setRowCount] = useState<number>(
gridLayoutStateManager.gridLayout$.getValue().length
);

useEffect(() => {
/**
* The only thing that should cause the entire layout to re-render is adding a new row;
* this subscription ensures this by updating the `rowCount` state when it changes.
*/
const rowCountSubscription = gridLayoutStateManager.gridLayout$
.pipe(
skip(1), // we initialized `rowCount` above, so skip the initial emit
map((newLayout) => newLayout.length),
distinctUntilChanged()
)
.subscribe((newRowCount) => {
setRowCount(newRowCount);
});
return () => rowCountSubscription.unsubscribe();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<>
<GridHeightSmoother gridLayoutStateManager={gridLayoutStateManager}>
Expand All @@ -43,15 +59,12 @@ export const GridLayout = ({
setDimensionsRef(divElement);
}}
>
{gridLayout.map((rowData, rowIndex) => {
{Array.from({ length: rowCount }, (_, rowIndex) => {
return (
<GridRow
rowData={rowData}
key={rowData.title}
key={uuidv4()}
rowIndex={rowIndex}
runtimeSettings={runtimeSettings}
renderPanelContents={renderPanelContents}
targetRowIndex={interactionEvent?.targetRowIndex}
gridLayoutStateManager={gridLayoutStateManager}
toggleIsCollapsed={() => {
const currentLayout = gridLayoutStateManager.gridLayout$.value;
Expand Down
Loading
Loading