mirror of
https://github.com/grafana/grafana.git
synced 2025-09-17 23:04:48 +08:00
VizLayout: Simple viz layout component for legend placement and scaling (#28820)
* Updates * Updated * Rename slot to legend * Updated * Adding js doc comments * hide horizontal track * Fix docs error * Added another export * removing auto mode * I hate these doc warnings
This commit is contained in:
@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { number } from '@storybook/addon-knobs';
|
||||
import { VizLayout } from './VizLayout';
|
||||
|
||||
export default {
|
||||
title: 'Visualizations/VizLayout',
|
||||
component: VizLayout,
|
||||
decorators: [withCenteredStory],
|
||||
parameters: {
|
||||
docs: {},
|
||||
},
|
||||
};
|
||||
|
||||
const getKnobs = () => {
|
||||
return {
|
||||
legendWidth: number('legendWidth', 100),
|
||||
legendItems: number('legendItems', 2),
|
||||
};
|
||||
};
|
||||
|
||||
export const BottomLegend = () => {
|
||||
const { legendItems } = getKnobs();
|
||||
const items = Array.from({ length: legendItems }, (_, i) => i + 1);
|
||||
|
||||
const legend = (
|
||||
<VizLayout.Legend position="bottom" maxHeight="30%">
|
||||
{items.map((_, index) => (
|
||||
<div style={{ height: '30px', width: '100%', background: 'blue', marginBottom: '2px' }}>
|
||||
Legend item {index}
|
||||
</div>
|
||||
))}
|
||||
</VizLayout.Legend>
|
||||
);
|
||||
|
||||
return (
|
||||
<VizLayout width={400} height={500} legend={legend}>
|
||||
{(vizWidth: number, vizHeight: number) => {
|
||||
return <div style={{ width: vizWidth, height: vizHeight, background: 'red' }} />;
|
||||
}}
|
||||
</VizLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export const RightLegend = () => {
|
||||
const { legendItems, legendWidth } = getKnobs();
|
||||
const items = Array.from({ length: legendItems }, (_, i) => i + 1);
|
||||
|
||||
const legend = (
|
||||
<VizLayout.Legend position="right" maxWidth="50%">
|
||||
{items.map((_, index) => (
|
||||
<div style={{ height: '30px', width: `${legendWidth}px`, background: 'blue', marginBottom: '2px' }}>
|
||||
Legend item {index}
|
||||
</div>
|
||||
))}
|
||||
</VizLayout.Legend>
|
||||
);
|
||||
|
||||
return (
|
||||
<VizLayout width={400} height={500} legend={legend}>
|
||||
{(vizWidth: number, vizHeight: number) => {
|
||||
return <div style={{ width: vizWidth, height: vizHeight, background: 'red' }} />;
|
||||
}}
|
||||
</VizLayout>
|
||||
);
|
||||
};
|
99
packages/grafana-ui/src/components/VizLayout/VizLayout.tsx
Normal file
99
packages/grafana-ui/src/components/VizLayout/VizLayout.tsx
Normal file
@ -0,0 +1,99 @@
|
||||
import React, { FC, CSSProperties, ComponentType } from 'react';
|
||||
import { useMeasure } from 'react-use';
|
||||
import CustomScrollbar from '../CustomScrollbar/CustomScrollbar';
|
||||
|
||||
/**
|
||||
* @beta
|
||||
*/
|
||||
export interface VizLayoutProps {
|
||||
width: number;
|
||||
height: number;
|
||||
legend?: React.ReactElement<VizLayoutLegendProps>;
|
||||
children: (width: number, height: number) => React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta
|
||||
*/
|
||||
export interface VizLayoutComponentType extends FC<VizLayoutProps> {
|
||||
Legend: ComponentType<VizLayoutLegendProps>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta
|
||||
*/
|
||||
export const VizLayout: VizLayoutComponentType = ({ width, height, legend, children }) => {
|
||||
const containerStyle: CSSProperties = {
|
||||
display: 'flex',
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
};
|
||||
|
||||
if (!legend) {
|
||||
return <div style={containerStyle}>{children(width, height)}</div>;
|
||||
}
|
||||
|
||||
const { position, maxHeight, maxWidth } = legend.props;
|
||||
const [legendRef, legendMeasure] = useMeasure();
|
||||
let size: VizSize | null = null;
|
||||
|
||||
const vizStyle: CSSProperties = {
|
||||
flexGrow: 1,
|
||||
};
|
||||
|
||||
const legendStyle: CSSProperties = {
|
||||
flexGrow: 1,
|
||||
};
|
||||
|
||||
switch (position) {
|
||||
case 'bottom':
|
||||
containerStyle.flexDirection = 'column';
|
||||
legendStyle.maxHeight = maxHeight;
|
||||
|
||||
if (legendMeasure) {
|
||||
size = { width, height: height - legendMeasure.height };
|
||||
}
|
||||
break;
|
||||
case 'right':
|
||||
containerStyle.flexDirection = 'row';
|
||||
legendStyle.maxWidth = maxWidth;
|
||||
|
||||
if (legendMeasure) {
|
||||
size = { width: width - legendMeasure.width, height };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<div style={vizStyle}>{size && children(size.width, size.height)}</div>
|
||||
<div style={legendStyle} ref={legendRef}>
|
||||
<CustomScrollbar hideHorizontalTrack>{legend}</CustomScrollbar>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface VizSize {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta
|
||||
*/
|
||||
export interface VizLayoutLegendProps {
|
||||
position: 'bottom' | 'right';
|
||||
maxHeight?: string;
|
||||
maxWidth?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta
|
||||
*/
|
||||
export const VizLayoutLegend: FC<VizLayoutLegendProps> = ({ children }) => {
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
VizLayout.Legend = VizLayoutLegend;
|
@ -78,6 +78,7 @@ export { BarGauge, BarGaugeDisplayMode } from './BarGauge/BarGauge';
|
||||
export { GraphTooltipOptions } from './Graph/GraphTooltip/types';
|
||||
export { VizRepeater, VizRepeaterRenderValueProps } from './VizRepeater/VizRepeater';
|
||||
export { graphTimeFormat, graphTickFormatter } from './Graph/utils';
|
||||
export { VizLayout, VizLayoutComponentType, VizLayoutLegendProps, VizLayoutProps } from './VizLayout/VizLayout';
|
||||
|
||||
export {
|
||||
LegendOptions,
|
||||
|
Reference in New Issue
Block a user