mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
grafana/ui: Add basic horizontal and vertical layout components (#22303)
This commit is contained in:
parent
55277ca732
commit
e6c59d0729
38
packages/grafana-ui/src/components/Layout/Layout.story.tsx
Normal file
38
packages/grafana-ui/src/components/Layout/Layout.story.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { VerticalGroup, HorizontalGroup, Layout } from './Layout';
|
||||
import { Button } from '../Forms/Button';
|
||||
import { withStoryContainer } from '../../utils/storybook/withStoryContainer';
|
||||
import { select } from '@storybook/addon-knobs';
|
||||
|
||||
export default {
|
||||
title: 'Layout/Groups',
|
||||
component: Layout,
|
||||
decorators: [withStoryContainer, withCenteredStory, withHorizontallyCenteredStory],
|
||||
};
|
||||
|
||||
const justifyVariants = ['flex-start', 'flex-end', 'space-between'];
|
||||
|
||||
const spacingVariants = ['xs', 'sm', 'md', 'lg'];
|
||||
|
||||
export const horizontal = () => {
|
||||
const justify = select('Justify elements', justifyVariants, 'flex-start');
|
||||
const spacing = select('Elements spacing', spacingVariants, 'sm');
|
||||
return (
|
||||
<HorizontalGroup justify={justify as any} spacing={spacing as any}>
|
||||
<Button>Save</Button>
|
||||
<Button>Cancel</Button>
|
||||
</HorizontalGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export const vertical = () => {
|
||||
const justify = select('Justify elements', justifyVariants, 'flex-start');
|
||||
const spacing = select('Elements spacing', spacingVariants, 'sm');
|
||||
return (
|
||||
<VerticalGroup justify={justify as any} spacing={spacing as any}>
|
||||
<Button>Save</Button>
|
||||
<Button>Cancel</Button>
|
||||
</VerticalGroup>
|
||||
);
|
||||
};
|
66
packages/grafana-ui/src/components/Layout/Layout.tsx
Normal file
66
packages/grafana-ui/src/components/Layout/Layout.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import { css } from 'emotion';
|
||||
import { stylesFactory, useTheme } from '../../themes';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
|
||||
enum Orientation {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
type Spacing = 'xs' | 'sm' | 'md' | 'lg';
|
||||
type Justify = 'flex-start' | 'flex-end' | 'space-between';
|
||||
|
||||
export interface LayoutProps {
|
||||
children: React.ReactNode[];
|
||||
orientation?: Orientation;
|
||||
spacing?: Spacing;
|
||||
justify?: Justify;
|
||||
}
|
||||
|
||||
export const Layout: React.FC<LayoutProps> = ({
|
||||
children,
|
||||
orientation = Orientation.Horizontal,
|
||||
spacing = 'sm',
|
||||
justify = 'flex-start',
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyles(theme, orientation, spacing, justify);
|
||||
return (
|
||||
<div className={styles.layout}>
|
||||
{React.Children.map(children, (child, index) => {
|
||||
return <div className={styles.buttonWrapper}>{child}</div>;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const HorizontalGroup: React.FC<Omit<LayoutProps, 'orientation'>> = ({ children, spacing, justify }) => (
|
||||
<Layout spacing={spacing} justify={justify} orientation={Orientation.Horizontal}>
|
||||
{children}
|
||||
</Layout>
|
||||
);
|
||||
export const VerticalGroup: React.FC<Omit<LayoutProps, 'orientation'>> = ({ children, spacing, justify }) => (
|
||||
<Layout spacing={spacing} justify={justify} orientation={Orientation.Vertical}>
|
||||
{children}
|
||||
</Layout>
|
||||
);
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme, orientation: Orientation, spacing: Spacing, justify: Justify) => {
|
||||
return {
|
||||
layout: css`
|
||||
display: flex;
|
||||
flex-direction: ${orientation === Orientation.Vertical ? 'column' : 'row'};
|
||||
justify-content: ${justify};
|
||||
height: 100%;
|
||||
`,
|
||||
buttonWrapper: css`
|
||||
margin-bottom: ${orientation === Orientation.Horizontal ? 0 : theme.spacing[spacing]};
|
||||
margin-right: ${orientation === Orientation.Horizontal ? theme.spacing[spacing] : 0};
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
@ -25,7 +25,6 @@ export { SecretFormField } from './SecretFormFied/SecretFormField';
|
||||
export { LoadingPlaceholder } from './LoadingPlaceholder/LoadingPlaceholder';
|
||||
export { ColorPicker, SeriesColorPicker } from './ColorPicker/ColorPicker';
|
||||
export { SeriesColorPickerPopover, SeriesColorPickerPopoverWithTheme } from './ColorPicker/SeriesColorPickerPopover';
|
||||
|
||||
export { PanelOptionsGroup } from './PanelOptionsGroup/PanelOptionsGroup';
|
||||
export { PanelOptionsGrid } from './PanelOptionsGrid/PanelOptionsGrid';
|
||||
export { ValueMappingsEditor } from './ValueMappingsEditor/ValueMappingsEditor';
|
||||
@ -142,3 +141,4 @@ export { default as Forms } from './Forms';
|
||||
export { ValuePicker } from './ValuePicker/ValuePicker';
|
||||
export { fieldMatchersUI } from './MatchersUI/fieldMatchersUI';
|
||||
export { getStandardFieldConfigs } from './FieldConfigs/standardFieldConfigEditors';
|
||||
export { HorizontalGroup, VerticalGroup } from './Layout/Layout';
|
||||
|
@ -3,9 +3,15 @@ import { boolean, number } from '@storybook/addon-knobs';
|
||||
import { css, cx } from 'emotion';
|
||||
import { RenderFunction } from '../../types';
|
||||
|
||||
const StoryContainer: React.FC<{ width?: number; showBoundaries: boolean }> = ({ children, width, showBoundaries }) => {
|
||||
const StoryContainer: React.FC<{ width?: number; height?: number; showBoundaries: boolean }> = ({
|
||||
children,
|
||||
width,
|
||||
height,
|
||||
showBoundaries,
|
||||
}) => {
|
||||
const checkColor = '#f0f0f0';
|
||||
const finalWidth = width ? `${width}px` : '100%';
|
||||
const finalHeight = height !== 0 ? `${height}px` : 'auto';
|
||||
const bgStyles =
|
||||
showBoundaries &&
|
||||
css`
|
||||
@ -27,6 +33,7 @@ const StoryContainer: React.FC<{ width?: number; showBoundaries: boolean }> = ({
|
||||
className={cx(
|
||||
css`
|
||||
width: ${finalWidth};
|
||||
height: ${finalHeight};
|
||||
`,
|
||||
bgStyles
|
||||
)}
|
||||
@ -52,8 +59,23 @@ export const withStoryContainer = (story: RenderFunction) => {
|
||||
},
|
||||
CONTAINER_GROUP
|
||||
);
|
||||
const containerHeight = number(
|
||||
'Container height',
|
||||
0,
|
||||
{
|
||||
range: true,
|
||||
min: 100,
|
||||
max: 500,
|
||||
step: 10,
|
||||
},
|
||||
CONTAINER_GROUP
|
||||
);
|
||||
return (
|
||||
<StoryContainer width={fullWidthContainter ? undefined : containerWidth} showBoundaries={containerBoundary}>
|
||||
<StoryContainer
|
||||
width={fullWidthContainter ? undefined : containerWidth}
|
||||
height={containerHeight}
|
||||
showBoundaries={containerBoundary}
|
||||
>
|
||||
{story()}
|
||||
</StoryContainer>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user