mirror of
https://github.com/grafana/grafana.git
synced 2024-11-22 08:56:43 -06:00
DashboardGrid: Refactorings and performance improvements (#35942)
* add back only re-render dashboard first time on layout change * remove panel refs * Updates * Improved is in view logic * Updates * Remove unnessary auto sizer * Found another approach that works with resize as well * Updates * fixing test * Fixing ref issues * Fixed mobile size handling, and view panel handling, removed now unnessary logic * Updated snapshot
This commit is contained in:
parent
fcb4e5a211
commit
f0cac149da
@ -275,7 +275,7 @@
|
||||
"react-beautiful-dnd": "13.0.0",
|
||||
"react-diff-viewer": "^3.1.1",
|
||||
"react-dom": "17.0.1",
|
||||
"react-grid-layout": "1.2.0",
|
||||
"react-grid-layout": "1.2.5",
|
||||
"react-highlight-words": "0.17.0",
|
||||
"react-loadable": "5.5.0",
|
||||
"react-popper": "2.2.4",
|
||||
@ -283,7 +283,6 @@
|
||||
"react-reverse-portal": "^2.0.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-select": "4.3.0",
|
||||
"react-sizeme": "2.6.12",
|
||||
"react-split-pane": "0.1.89",
|
||||
"react-transition-group": "4.4.1",
|
||||
"react-use": "13.27.0",
|
||||
|
@ -245,6 +245,8 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
isEditing={true}
|
||||
isViewing={false}
|
||||
isInView={true}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -113,6 +113,10 @@ describe('SoloPanelPage', () => {
|
||||
|
||||
soloPanelPageScenario('Dashboard init completed ', (ctx) => {
|
||||
ctx.setup(() => {
|
||||
// Needed for AutoSizer to work in test
|
||||
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, value: 500 });
|
||||
Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { configurable: true, value: 500 });
|
||||
|
||||
ctx.mount();
|
||||
ctx.setDashboard();
|
||||
expect(ctx.dashboard).not.toBeNull();
|
||||
|
@ -1,12 +1,9 @@
|
||||
// Libraries
|
||||
import React, { Component } from 'react';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { connect } from 'react-redux';
|
||||
// Components
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { DashboardPanel } from '../dashgrid/DashboardPanel';
|
||||
// Redux
|
||||
import { initDashboard } from '../state/initDashboard';
|
||||
// Types
|
||||
import { StoreState } from 'app/types';
|
||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||
@ -87,9 +84,24 @@ export class SoloPanelPage extends Component<Props, State> {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="panel-solo">
|
||||
<DashboardPanel dashboard={dashboard} panel={panel} isEditing={false} isViewing={false} isInView={true} />
|
||||
</div>
|
||||
<AutoSizer className="panel-solo">
|
||||
{({ width, height }) => {
|
||||
if (width === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<DashboardPanel
|
||||
width={width}
|
||||
height={height}
|
||||
dashboard={dashboard}
|
||||
panel={panel}
|
||||
isEditing={false}
|
||||
isViewing={false}
|
||||
isInView={true}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</AutoSizer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
// Libraries
|
||||
import React, { PureComponent } from 'react';
|
||||
import React, { PureComponent, CSSProperties } from 'react';
|
||||
import ReactGridLayout, { ItemCallback } from 'react-grid-layout';
|
||||
import classNames from 'classnames';
|
||||
// @ts-ignore
|
||||
import sizeMe from 'react-sizeme';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
|
||||
// Components
|
||||
import { AddPanelWidget } from '../components/AddPanelWidget';
|
||||
@ -15,79 +14,8 @@ import { DashboardPanel } from './DashboardPanel';
|
||||
import { DashboardModel, PanelModel } from '../state';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { DashboardPanelsChangedEvent } from 'app/types/events';
|
||||
|
||||
let lastGridWidth = 1200;
|
||||
let ignoreNextWidthChange = false;
|
||||
|
||||
interface GridWrapperProps {
|
||||
size: { width: number };
|
||||
layout: ReactGridLayout.Layout[];
|
||||
onLayoutChange: (layout: ReactGridLayout.Layout[]) => void;
|
||||
children: JSX.Element | JSX.Element[];
|
||||
onDragStop: ItemCallback;
|
||||
onResize: ItemCallback;
|
||||
onResizeStop: ItemCallback;
|
||||
className: string;
|
||||
isResizable?: boolean;
|
||||
isDraggable?: boolean;
|
||||
viewPanel: PanelModel | null;
|
||||
}
|
||||
|
||||
function GridWrapper({
|
||||
size,
|
||||
layout,
|
||||
onLayoutChange,
|
||||
children,
|
||||
onDragStop,
|
||||
onResize,
|
||||
onResizeStop,
|
||||
className,
|
||||
isResizable,
|
||||
isDraggable,
|
||||
viewPanel,
|
||||
}: GridWrapperProps) {
|
||||
const width = size.width > 0 ? size.width : lastGridWidth;
|
||||
|
||||
// logic to ignore width changes (optimization)
|
||||
if (width !== lastGridWidth) {
|
||||
if (ignoreNextWidthChange) {
|
||||
ignoreNextWidthChange = false;
|
||||
} else if (!viewPanel && Math.abs(width - lastGridWidth) > 8) {
|
||||
lastGridWidth = width;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Disable draggable if mobile device, solving an issue with unintentionally
|
||||
moving panels. https://github.com/grafana/grafana/issues/18497
|
||||
theme.breakpoints.md = 769
|
||||
*/
|
||||
const draggable = width <= 769 ? false : isDraggable;
|
||||
|
||||
return (
|
||||
<ReactGridLayout
|
||||
width={lastGridWidth}
|
||||
className={className}
|
||||
isDraggable={draggable}
|
||||
isResizable={isResizable}
|
||||
containerPadding={[0, 0]}
|
||||
useCSSTransforms={false}
|
||||
margin={[GRID_CELL_VMARGIN, GRID_CELL_VMARGIN]}
|
||||
cols={GRID_COLUMN_COUNT}
|
||||
rowHeight={GRID_CELL_HEIGHT}
|
||||
draggableHandle=".grid-drag-handle"
|
||||
layout={layout}
|
||||
onResize={onResize}
|
||||
onResizeStop={onResizeStop}
|
||||
onDragStop={onDragStop}
|
||||
onLayoutChange={onLayoutChange}
|
||||
>
|
||||
{children}
|
||||
</ReactGridLayout>
|
||||
);
|
||||
}
|
||||
|
||||
const SizedReactLayoutGrid = sizeMe({ monitorWidth: true })(GridWrapper);
|
||||
import { GridPos } from '../state/PanelModel';
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
export interface Props {
|
||||
dashboard: DashboardModel;
|
||||
@ -100,10 +28,12 @@ export interface Props {
|
||||
export interface State {
|
||||
isLayoutInitialized: boolean;
|
||||
}
|
||||
|
||||
export class DashboardGrid extends PureComponent<Props, State> {
|
||||
private panelMap: { [id: string]: PanelModel } = {};
|
||||
private panelRef: { [id: string]: HTMLElement } = {};
|
||||
private eventSubs = new Subscription();
|
||||
private windowHeight = 1200;
|
||||
private gridWidth = 0;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@ -162,7 +92,11 @@ export class DashboardGrid extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
this.props.dashboard.sortPanelsByGridPos();
|
||||
this.forceUpdate();
|
||||
|
||||
// This is called on grid mount as it can correct invalid initial grid positions
|
||||
if (!this.state.isLayoutInitialized) {
|
||||
this.setState({ isLayoutInitialized: true });
|
||||
}
|
||||
};
|
||||
|
||||
triggerForceUpdate = () => {
|
||||
@ -185,98 +119,184 @@ export class DashboardGrid extends PureComponent<Props, State> {
|
||||
this.updateGridPos(newItem, layout);
|
||||
};
|
||||
|
||||
isInView = (panel: PanelModel): boolean => {
|
||||
isInView(panel: PanelModel) {
|
||||
if (panel.isViewing || panel.isEditing) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// elem is set *after* the first render
|
||||
const elem = this.panelRef[panel.id.toString()];
|
||||
if (!elem) {
|
||||
// NOTE the gridPos is also not valid until after the first render
|
||||
// since it is passed to the layout engine and made to be valid
|
||||
// for example, you can have Y=0 for everything and it will stack them
|
||||
// down vertically in the second call
|
||||
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;
|
||||
|
||||
// Show things that are almost in the view
|
||||
const buffer = 100;
|
||||
|
||||
// The panel is above the viewport
|
||||
if (scrollTop > panelBottom + buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const top = elem.offsetTop;
|
||||
const height = panel.gridPos.h * GRID_CELL_HEIGHT + 40;
|
||||
const bottom = top + height;
|
||||
const scrollViewBottom = scrollTop + this.windowHeight;
|
||||
|
||||
// Show things that are almost in the view
|
||||
const buffer = 250;
|
||||
|
||||
const viewTop = this.props.scrollTop;
|
||||
if (viewTop > bottom + buffer) {
|
||||
return false; // The panel is above the viewport
|
||||
}
|
||||
|
||||
// Use the whole browser height (larger than real value)
|
||||
// TODO? is there a better way
|
||||
const viewHeight = isNaN(window.innerHeight) ? (window as any).clientHeight : window.innerHeight;
|
||||
const viewBot = viewTop + viewHeight;
|
||||
if (top > viewBot + buffer) {
|
||||
// Panel is below view
|
||||
if (panelTop > scrollViewBottom + buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !this.props.dashboard.otherPanelInFullscreen(panel);
|
||||
};
|
||||
}
|
||||
|
||||
renderPanels() {
|
||||
renderPanels(gridWidth: number) {
|
||||
const panelElements = [];
|
||||
|
||||
// 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) {
|
||||
this.windowHeight = window.innerHeight ?? 1000;
|
||||
this.gridWidth = gridWidth;
|
||||
}
|
||||
|
||||
for (const panel of this.props.dashboard.panels) {
|
||||
const panelClasses = classNames({ 'react-grid-item--fullscreen': panel.isViewing });
|
||||
const id = panel.id.toString();
|
||||
const itemKey = panel.id.toString();
|
||||
|
||||
// Update is in view state
|
||||
panel.isInView = this.isInView(panel);
|
||||
|
||||
panelElements.push(
|
||||
<div key={id} className={panelClasses} data-panelid={id} ref={(elem) => elem && (this.panelRef[id] = elem)}>
|
||||
{this.renderPanel(panel)}
|
||||
</div>
|
||||
<GrafanaGridItem
|
||||
key={itemKey}
|
||||
className={panelClasses}
|
||||
data-panelid={itemKey}
|
||||
gridPos={panel.gridPos}
|
||||
gridWidth={gridWidth}
|
||||
windowHeight={this.windowHeight}
|
||||
isViewing={panel.isViewing}
|
||||
>
|
||||
{(width: number, height: number) => {
|
||||
return this.renderPanel(panel, width, height, itemKey);
|
||||
}}
|
||||
</GrafanaGridItem>
|
||||
);
|
||||
}
|
||||
|
||||
return panelElements;
|
||||
}
|
||||
|
||||
renderPanel(panel: PanelModel) {
|
||||
renderPanel(panel: PanelModel, width: any, height: any, itemKey: string) {
|
||||
if (panel.type === 'row') {
|
||||
return <DashboardRow panel={panel} dashboard={this.props.dashboard} />;
|
||||
return <DashboardRow key={itemKey} panel={panel} dashboard={this.props.dashboard} />;
|
||||
}
|
||||
|
||||
if (panel.type === 'add-panel') {
|
||||
return <AddPanelWidget panel={panel} dashboard={this.props.dashboard} />;
|
||||
return <AddPanelWidget key={itemKey} panel={panel} dashboard={this.props.dashboard} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<DashboardPanel
|
||||
key={itemKey}
|
||||
panel={panel}
|
||||
dashboard={this.props.dashboard}
|
||||
isEditing={panel.isEditing}
|
||||
isViewing={panel.isViewing}
|
||||
isInView={panel.isInView}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dashboard, viewPanel } = this.props;
|
||||
const { dashboard } = this.props;
|
||||
|
||||
const autoSizerStyle: CSSProperties = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
};
|
||||
|
||||
return (
|
||||
<SizedReactLayoutGrid
|
||||
className={classNames({ layout: true })}
|
||||
layout={this.buildLayout()}
|
||||
isResizable={dashboard.meta.canEdit}
|
||||
isDraggable={dashboard.meta.canEdit}
|
||||
onLayoutChange={this.onLayoutChange}
|
||||
onDragStop={this.onDragStop}
|
||||
onResize={this.onResize}
|
||||
onResizeStop={this.onResizeStop}
|
||||
viewPanel={viewPanel}
|
||||
>
|
||||
{this.renderPanels()}
|
||||
</SizedReactLayoutGrid>
|
||||
<AutoSizer style={autoSizerStyle} disableHeight>
|
||||
{({ width }) => {
|
||||
if (width === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const draggable = width <= 769 ? false : dashboard.meta.canEdit;
|
||||
|
||||
/*
|
||||
Disable draggable if mobile device, solving an issue with unintentionally
|
||||
moving panels. https://github.com/grafana/grafana/issues/18497
|
||||
theme.breakpoints.md = 769
|
||||
*/
|
||||
|
||||
return (
|
||||
<ReactGridLayout
|
||||
width={width}
|
||||
isDraggable={draggable}
|
||||
isResizable={dashboard.meta.canEdit}
|
||||
containerPadding={[0, 0]}
|
||||
useCSSTransforms={false}
|
||||
margin={[GRID_CELL_VMARGIN, GRID_CELL_VMARGIN]}
|
||||
cols={GRID_COLUMN_COUNT}
|
||||
rowHeight={GRID_CELL_HEIGHT}
|
||||
draggableHandle=".grid-drag-handle"
|
||||
layout={this.buildLayout()}
|
||||
onDragStop={this.onDragStop}
|
||||
onResize={this.onResize}
|
||||
onResizeStop={this.onResizeStop}
|
||||
onLayoutChange={this.onLayoutChange}
|
||||
>
|
||||
{this.renderPanels(width)}
|
||||
</ReactGridLayout>
|
||||
);
|
||||
}}
|
||||
</AutoSizer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface GrafanaGridItemProps extends Record<string, any> {
|
||||
gridWidth?: number;
|
||||
gridPos?: GridPos;
|
||||
isViewing: string;
|
||||
windowHeight: number;
|
||||
children: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* A hacky way to intercept the react-layout-grid item dimensions and pass them to DashboardPanel
|
||||
*/
|
||||
const GrafanaGridItem = React.forwardRef<HTMLDivElement, GrafanaGridItemProps>((props, ref) => {
|
||||
const theme = config.theme2;
|
||||
let width = 100;
|
||||
let height = 100;
|
||||
|
||||
const { gridWidth, gridPos, isViewing, windowHeight, ...divProps } = props;
|
||||
const style: CSSProperties = props.style ?? {};
|
||||
|
||||
if (isViewing) {
|
||||
width = props.gridWidth!;
|
||||
height = windowHeight * 0.85;
|
||||
style.height = height;
|
||||
style.width = '100%';
|
||||
} else if (props.gridWidth! < theme.breakpoints.values.md) {
|
||||
width = props.gridWidth!;
|
||||
height = props.gridPos!.h * (GRID_CELL_HEIGHT + GRID_CELL_VMARGIN) - GRID_CELL_VMARGIN;
|
||||
style.height = height;
|
||||
style.width = '100%';
|
||||
} else {
|
||||
// RGL passes width and height directly to children as style props.
|
||||
width = parseFloat(props.style.width);
|
||||
height = parseFloat(props.style.height);
|
||||
}
|
||||
|
||||
// props.children[0] is our main children. RGL adds the drag handle at props.children[1]
|
||||
return (
|
||||
<div {...divProps} ref={ref}>
|
||||
{/* Pass width and height to children as render props */}
|
||||
{[props.children[0](width, height), props.children.slice(1)]}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
GrafanaGridItem.displayName = 'GridItemWithDimensions';
|
||||
|
@ -1,7 +1,5 @@
|
||||
// Libraries
|
||||
import React, { PureComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
|
||||
// Components
|
||||
@ -15,8 +13,6 @@ import { initDashboardPanel } from '../state/actions';
|
||||
import { DashboardModel, PanelModel } from '../state';
|
||||
import { StoreState } from 'app/types';
|
||||
import { PanelPlugin } from '@grafana/data';
|
||||
import { stylesFactory } from '@grafana/ui';
|
||||
import { css } from 'emotion';
|
||||
|
||||
export interface OwnProps {
|
||||
panel: PanelModel;
|
||||
@ -24,6 +20,8 @@ export interface OwnProps {
|
||||
isEditing: boolean;
|
||||
isViewing: boolean;
|
||||
isInView: boolean;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
@ -69,51 +67,40 @@ export class DashboardPanelUnconnected extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
renderPanel(plugin: PanelPlugin) {
|
||||
const { dashboard, panel, isViewing, isInView, isEditing } = this.props;
|
||||
const { dashboard, panel, isViewing, isInView, isEditing, width, height } = this.props;
|
||||
|
||||
if (plugin.angularPanelCtrl) {
|
||||
return (
|
||||
<PanelChromeAngular
|
||||
plugin={plugin}
|
||||
panel={panel}
|
||||
dashboard={dashboard}
|
||||
isViewing={isViewing}
|
||||
isEditing={isEditing}
|
||||
isInView={isInView}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AutoSizer>
|
||||
{({ width, height }) => {
|
||||
if (width === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (plugin.angularPanelCtrl) {
|
||||
return (
|
||||
<PanelChromeAngular
|
||||
plugin={plugin}
|
||||
panel={panel}
|
||||
dashboard={dashboard}
|
||||
isViewing={isViewing}
|
||||
isEditing={isEditing}
|
||||
isInView={isInView}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PanelChrome
|
||||
plugin={plugin}
|
||||
panel={panel}
|
||||
dashboard={dashboard}
|
||||
isViewing={isViewing}
|
||||
isEditing={isEditing}
|
||||
isInView={isInView}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</AutoSizer>
|
||||
<PanelChrome
|
||||
plugin={plugin}
|
||||
panel={panel}
|
||||
dashboard={dashboard}
|
||||
isViewing={isViewing}
|
||||
isEditing={isEditing}
|
||||
isInView={isInView}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isViewing, plugin } = this.props;
|
||||
const { plugin } = this.props;
|
||||
const { isLazy } = this.state;
|
||||
const styles = getStyles();
|
||||
|
||||
// If we have not loaded plugin exports yet, wait
|
||||
if (!plugin) {
|
||||
@ -125,27 +112,8 @@ export class DashboardPanelUnconnected extends PureComponent<Props, State> {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={isViewing === true ? classNames(styles.panelWrapper, styles.panelWrapperView) : styles.panelWrapper}
|
||||
>
|
||||
{this.renderPanel(plugin)}
|
||||
</div>
|
||||
);
|
||||
return this.renderPanel(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
export const getStyles = stylesFactory(() => {
|
||||
return {
|
||||
panelWrapper: css`
|
||||
height: 100%;
|
||||
position: relative;
|
||||
`,
|
||||
panelWrapperView: css`
|
||||
flex: 1 1 0;
|
||||
height: 90%;
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
export const DashboardPanel = connector(DashboardPanelUnconnected);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -38,6 +38,7 @@ import {
|
||||
} from './getPanelOptionsWithDefaults';
|
||||
import { QueryGroupOptions } from 'app/types';
|
||||
import { PanelModelLibraryPanel } from '../../library-panels/types';
|
||||
|
||||
export interface GridPos {
|
||||
x: number;
|
||||
y: number;
|
||||
|
@ -17,10 +17,6 @@
|
||||
}
|
||||
|
||||
.panel-in-fullscreen {
|
||||
.react-grid-layout {
|
||||
height: 90% !important;
|
||||
}
|
||||
|
||||
.react-grid-item {
|
||||
display: none !important;
|
||||
transition-property: none !important;
|
||||
@ -28,8 +24,6 @@
|
||||
&--fullscreen {
|
||||
display: block !important;
|
||||
position: unset !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: translate(0px, 0px) !important;
|
||||
}
|
||||
}
|
||||
@ -54,15 +48,10 @@
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
.react-grid-layout {
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.react-grid-item {
|
||||
display: block !important;
|
||||
transition-property: none !important;
|
||||
position: unset !important;
|
||||
width: 100% !important;
|
||||
transform: translate(0px, 0px) !important;
|
||||
margin-bottom: $space-md;
|
||||
}
|
||||
|
@ -75,6 +75,8 @@ div.flot-text {
|
||||
margin: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: '100%';
|
||||
height: '100%';
|
||||
|
||||
.panel-container {
|
||||
border: none;
|
||||
|
37
yarn.lock
37
yarn.lock
@ -7367,6 +7367,11 @@ classnames@2.2.6, classnames@2.x, classnames@^2.2.1, classnames@^2.2.5, classnam
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
|
||||
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
|
||||
|
||||
classnames@2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
|
||||
integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
|
||||
|
||||
clean-css@4.2.x, clean-css@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
|
||||
@ -9537,7 +9542,7 @@ elegant-spinner@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
|
||||
integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=
|
||||
|
||||
element-resize-detector@^1.2.1, element-resize-detector@^1.2.2:
|
||||
element-resize-detector@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/element-resize-detector/-/element-resize-detector-1.2.2.tgz#bf7c3ff915957e4e62e86241ed2f9c86b078892b"
|
||||
integrity sha512-+LOXRkCJc4I5WhEJxIDjhmE3raF8jtOMBDqSCgZTMz2TX3oXAX5pE2+MDeopJlGdXzP7KzPbBJaUGfNaP9HG4A==
|
||||
@ -18480,16 +18485,16 @@ react-from-dom@^0.6.0:
|
||||
resolved "https://registry.yarnpkg.com/react-from-dom/-/react-from-dom-0.6.0.tgz#8b9710ba7fbd36cde9e13e9eb26f5f67ab933678"
|
||||
integrity sha512-W5m1pYV7qlc9bmpA7p2K/wspYNlAh3aqJ9Tc5KRXe6vt/JlX6/84ol+RQlCMK69z+5e38sOpoVW5i4Qpqgs+EA==
|
||||
|
||||
react-grid-layout@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-grid-layout/-/react-grid-layout-1.2.0.tgz#87124d549c86c8df8841666618c8c3e3cb205c26"
|
||||
integrity sha512-fJMGQFguphkAs0NsLNf8hz9cUv9B642JYei2yddiPby/X/kJ4HFIaMUhhqg1ArVfn/vHet1+h+LE4n85cFPh+Q==
|
||||
react-grid-layout@1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/react-grid-layout/-/react-grid-layout-1.2.5.tgz#fa40288d5a1fa783484c44ce78b1e10eb5313d26"
|
||||
integrity sha512-P/NNWAExTX/zEq+RUh6hrIG67UBicDNCOOg9LZe8BAtSdYtCnCGgVmWBS+sIbM0C8RJIiyGsFHh5dIfCddhS/w==
|
||||
dependencies:
|
||||
classnames "2.x"
|
||||
classnames "2.3.1"
|
||||
lodash.isequal "^4.0.0"
|
||||
prop-types "^15.0.0"
|
||||
react-draggable "^4.0.0"
|
||||
react-resizable "^1.10.0"
|
||||
react-resizable "^3.0.1"
|
||||
|
||||
react-helmet-async@^1.0.7:
|
||||
version "1.0.9"
|
||||
@ -18649,10 +18654,10 @@ react-refresh@^0.8.3:
|
||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
|
||||
integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==
|
||||
|
||||
react-resizable@^1.10.0:
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react-resizable/-/react-resizable-1.11.0.tgz#0b237c4aff16937b7663de1045861749683227ad"
|
||||
integrity sha512-VoGz2ddxUFvildS8r8/29UZJeyiM3QJnlmRZSuXm+FpTqq/eIrMPc796Y9XQLg291n2hFZJtIoP1xC3hSTw/jg==
|
||||
react-resizable@^3.0.1:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-resizable/-/react-resizable-3.0.4.tgz#aa20108eff28c52c6fddaa49abfbef8abf5e581b"
|
||||
integrity sha512-StnwmiESiamNzdRHbSSvA65b0ZQJ7eVQpPusrSmcpyGKzC0gojhtO62xxH6YOBmepk9dQTBi9yxidL3W4s3EBA==
|
||||
dependencies:
|
||||
prop-types "15.x"
|
||||
react-draggable "^4.0.3"
|
||||
@ -18733,16 +18738,6 @@ react-shallow-renderer@^16.13.1:
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.12.0 || ^17.0.0"
|
||||
|
||||
react-sizeme@2.6.12:
|
||||
version "2.6.12"
|
||||
resolved "https://registry.yarnpkg.com/react-sizeme/-/react-sizeme-2.6.12.tgz#ed207be5476f4a85bf364e92042520499455453e"
|
||||
integrity sha512-tL4sCgfmvapYRZ1FO2VmBmjPVzzqgHA7kI8lSJ6JS6L78jXFNRdOZFpXyK6P1NBZvKPPCZxReNgzZNUajAerZw==
|
||||
dependencies:
|
||||
element-resize-detector "^1.2.1"
|
||||
invariant "^2.2.4"
|
||||
shallowequal "^1.1.0"
|
||||
throttle-debounce "^2.1.0"
|
||||
|
||||
react-sizeme@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-sizeme/-/react-sizeme-3.0.1.tgz#4d12f4244e0e6a0fb97253e7af0314dc7c83a5a0"
|
||||
|
Loading…
Reference in New Issue
Block a user