mirror of https://github.com/grafana/grafana
Revert: Query editor components from grafana-ui (#57436)
* Revert: QueryEditor components from grafana-ui * Use local version of experimental * Use experimental for query editor components * Fix type issues in MSSQL * point to actual version of experimental package * point to latest version of experimental Co-authored-by: Erik Sundell <erik.sundell87@gmail.com>pull/57555/head
parent
3cbbf706cf
commit
c0b778134e
@ -1,23 +0,0 @@ |
|||||||
import { css, cx } from '@emotion/css'; |
|
||||||
import React from 'react'; |
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data'; |
|
||||||
|
|
||||||
import { useTheme2, stylesFactory } from '../../themes'; |
|
||||||
import { Button, ButtonProps } from '../Button'; |
|
||||||
|
|
||||||
interface AccessoryButtonProps extends ButtonProps {} |
|
||||||
|
|
||||||
export const AccessoryButton: React.FC<AccessoryButtonProps> = ({ className, ...props }) => { |
|
||||||
const theme = useTheme2(); |
|
||||||
const styles = getButtonStyles(theme); |
|
||||||
|
|
||||||
return <Button {...props} className={cx(className, styles.button)} />; |
|
||||||
}; |
|
||||||
|
|
||||||
const getButtonStyles = stylesFactory((theme: GrafanaTheme2) => ({ |
|
||||||
button: css({ |
|
||||||
paddingLeft: theme.spacing(3 / 2), |
|
||||||
paddingRight: theme.spacing(3 / 2), |
|
||||||
}), |
|
||||||
})); |
|
||||||
@ -1,80 +0,0 @@ |
|||||||
import { css } from '@emotion/css'; |
|
||||||
import React, { ComponentProps } from 'react'; |
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data'; |
|
||||||
|
|
||||||
import { stylesFactory, useTheme2 } from '../../themes'; |
|
||||||
import { ReactUtils } from '../../utils'; |
|
||||||
import { Field } from '../Forms/Field'; |
|
||||||
import { Icon } from '../Icon/Icon'; |
|
||||||
import { PopoverContent, Tooltip } from '../Tooltip'; |
|
||||||
|
|
||||||
import { Space } from './Space'; |
|
||||||
|
|
||||||
interface EditorFieldProps extends ComponentProps<typeof Field> { |
|
||||||
label: string; |
|
||||||
children: React.ReactElement; |
|
||||||
width?: number | string; |
|
||||||
optional?: boolean; |
|
||||||
tooltip?: PopoverContent; |
|
||||||
tooltipInteractive?: boolean; |
|
||||||
} |
|
||||||
|
|
||||||
export const EditorField: React.FC<EditorFieldProps> = (props) => { |
|
||||||
const { label, optional, tooltip, tooltipInteractive, children, width, ...fieldProps } = props; |
|
||||||
|
|
||||||
const theme = useTheme2(); |
|
||||||
const styles = getStyles(theme, width); |
|
||||||
|
|
||||||
// Null check for backward compatibility
|
|
||||||
const childInputId = fieldProps?.htmlFor || ReactUtils?.getChildId(children); |
|
||||||
|
|
||||||
const labelEl = ( |
|
||||||
<> |
|
||||||
<label className={styles.label} htmlFor={childInputId}> |
|
||||||
{label} |
|
||||||
{optional && <span className={styles.optional}> - optional</span>} |
|
||||||
{tooltip && ( |
|
||||||
<Tooltip placement="top" content={tooltip} theme="info" interactive={tooltipInteractive}> |
|
||||||
<Icon name="info-circle" size="sm" className={styles.icon} /> |
|
||||||
</Tooltip> |
|
||||||
)} |
|
||||||
</label> |
|
||||||
<Space v={0.5} /> |
|
||||||
</> |
|
||||||
); |
|
||||||
|
|
||||||
return ( |
|
||||||
<div className={styles.root}> |
|
||||||
<Field className={styles.field} label={labelEl} {...fieldProps}> |
|
||||||
{children} |
|
||||||
</Field> |
|
||||||
</div> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme2, width?: number | string) => { |
|
||||||
return { |
|
||||||
root: css({ |
|
||||||
minWidth: theme.spacing(width ?? 0), |
|
||||||
}), |
|
||||||
label: css({ |
|
||||||
fontSize: 12, |
|
||||||
fontWeight: theme.typography.fontWeightMedium, |
|
||||||
}), |
|
||||||
optional: css({ |
|
||||||
fontStyle: 'italic', |
|
||||||
color: theme.colors.text.secondary, |
|
||||||
}), |
|
||||||
field: css({ |
|
||||||
marginBottom: 0, // GrafanaUI/Field has a bottom margin which we must remove
|
|
||||||
}), |
|
||||||
icon: css({ |
|
||||||
color: theme.colors.text.secondary, |
|
||||||
marginLeft: theme.spacing(1), |
|
||||||
':hover': { |
|
||||||
color: theme.colors.text.primary, |
|
||||||
}, |
|
||||||
}), |
|
||||||
}; |
|
||||||
}); |
|
||||||
@ -1,9 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
|
|
||||||
import { Stack } from './Stack'; |
|
||||||
|
|
||||||
interface EditorFieldGroupProps {} |
|
||||||
|
|
||||||
export const EditorFieldGroup = ({ children }: React.PropsWithChildren<EditorFieldGroupProps>) => { |
|
||||||
return <Stack gap={1}>{children}</Stack>; |
|
||||||
}; |
|
||||||
@ -1,25 +0,0 @@ |
|||||||
import { css } from '@emotion/css'; |
|
||||||
import React from 'react'; |
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data'; |
|
||||||
|
|
||||||
import { stylesFactory, useTheme2 } from '../../themes'; |
|
||||||
|
|
||||||
interface EditorHeaderProps {} |
|
||||||
|
|
||||||
export const EditorHeader = ({ children }: React.PropsWithChildren<EditorHeaderProps>) => { |
|
||||||
const theme = useTheme2(); |
|
||||||
const styles = getStyles(theme); |
|
||||||
|
|
||||||
return <div className={styles.root}>{children}</div>; |
|
||||||
}; |
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme2) => ({ |
|
||||||
root: css({ |
|
||||||
display: 'flex', |
|
||||||
flexWrap: 'wrap', |
|
||||||
alignItems: 'center', |
|
||||||
gap: theme.spacing(3), |
|
||||||
minHeight: theme.spacing(4), |
|
||||||
}), |
|
||||||
})); |
|
||||||
@ -1,49 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
|
|
||||||
import { Button } from '../Button'; |
|
||||||
|
|
||||||
import { Stack } from './Stack'; |
|
||||||
|
|
||||||
interface EditorListProps<T> { |
|
||||||
items: Array<Partial<T>>; |
|
||||||
renderItem: ( |
|
||||||
item: Partial<T>, |
|
||||||
onChangeItem: (item: Partial<T>) => void, |
|
||||||
onDeleteItem: () => void |
|
||||||
) => React.ReactElement; |
|
||||||
onChange: (items: Array<Partial<T>>) => void; |
|
||||||
} |
|
||||||
|
|
||||||
export function EditorList<T>({ items, renderItem, onChange }: EditorListProps<T>) { |
|
||||||
const onAddItem = () => { |
|
||||||
const newItems = [...items, {}]; |
|
||||||
|
|
||||||
onChange(newItems); |
|
||||||
}; |
|
||||||
|
|
||||||
const onChangeItem = (itemIndex: number, newItem: Partial<T>) => { |
|
||||||
const newItems = [...items]; |
|
||||||
newItems[itemIndex] = newItem; |
|
||||||
onChange(newItems); |
|
||||||
}; |
|
||||||
|
|
||||||
const onDeleteItem = (itemIndex: number) => { |
|
||||||
const newItems = [...items]; |
|
||||||
newItems.splice(itemIndex, 1); |
|
||||||
onChange(newItems); |
|
||||||
}; |
|
||||||
return ( |
|
||||||
<Stack> |
|
||||||
{items.map((item, index) => ( |
|
||||||
<div key={index}> |
|
||||||
{renderItem( |
|
||||||
item, |
|
||||||
(newItem) => onChangeItem(index, newItem), |
|
||||||
() => onDeleteItem(index) |
|
||||||
)} |
|
||||||
</div> |
|
||||||
))} |
|
||||||
<Button onClick={onAddItem} variant="secondary" size="md" icon="plus" aria-label="Add" type="button" /> |
|
||||||
</Stack> |
|
||||||
); |
|
||||||
} |
|
||||||
@ -1,30 +0,0 @@ |
|||||||
import { css } from '@emotion/css'; |
|
||||||
import React from 'react'; |
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data'; |
|
||||||
|
|
||||||
import { useStyles2 } from '../../themes'; |
|
||||||
|
|
||||||
import { Stack } from './Stack'; |
|
||||||
|
|
||||||
interface EditorRowProps {} |
|
||||||
|
|
||||||
export const EditorRow = ({ children }: React.PropsWithChildren<EditorRowProps>) => { |
|
||||||
const styles = useStyles2(getStyles); |
|
||||||
|
|
||||||
return ( |
|
||||||
<div className={styles.root}> |
|
||||||
<Stack gap={2}>{children}</Stack> |
|
||||||
</div> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => { |
|
||||||
return { |
|
||||||
root: css({ |
|
||||||
padding: theme.spacing(1), |
|
||||||
backgroundColor: theme.colors.background.secondary, |
|
||||||
borderRadius: theme.shape.borderRadius(1), |
|
||||||
}), |
|
||||||
}; |
|
||||||
}; |
|
||||||
@ -1,13 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
|
|
||||||
import { Stack } from './Stack'; |
|
||||||
|
|
||||||
interface EditorRowsProps {} |
|
||||||
|
|
||||||
export const EditorRows = ({ children }: React.PropsWithChildren<EditorRowsProps>) => { |
|
||||||
return ( |
|
||||||
<Stack gap={0.5} direction="column"> |
|
||||||
{children} |
|
||||||
</Stack> |
|
||||||
); |
|
||||||
}; |
|
||||||
@ -1,25 +0,0 @@ |
|||||||
import { css } from '@emotion/css'; |
|
||||||
import React, { ComponentProps } from 'react'; |
|
||||||
|
|
||||||
import { Switch } from '../Switch/Switch'; |
|
||||||
|
|
||||||
// Wrapper component around <Switch /> that properly aligns it in <EditorField />
|
|
||||||
export const EditorSwitch: React.FC<ComponentProps<typeof Switch>> = (props) => { |
|
||||||
const styles = getStyles(); |
|
||||||
|
|
||||||
return ( |
|
||||||
<div className={styles.switch}> |
|
||||||
<Switch {...props} /> |
|
||||||
</div> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
const getStyles = () => { |
|
||||||
return { |
|
||||||
switch: css({ |
|
||||||
display: 'flex', |
|
||||||
alignItems: 'center', |
|
||||||
minHeight: 30, |
|
||||||
}), |
|
||||||
}; |
|
||||||
}; |
|
||||||
@ -1,10 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
|
|
||||||
interface FlexItemProps { |
|
||||||
grow?: number; |
|
||||||
shrink?: number; |
|
||||||
} |
|
||||||
|
|
||||||
export const FlexItem: React.FC<FlexItemProps> = ({ grow, shrink }) => { |
|
||||||
return <div style={{ display: 'block', flexGrow: grow, flexShrink: shrink }} />; |
|
||||||
}; |
|
||||||
@ -1,89 +0,0 @@ |
|||||||
import { css, cx } from '@emotion/css'; |
|
||||||
import React, { useState } from 'react'; |
|
||||||
import { GroupBase } from 'react-select'; |
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data'; |
|
||||||
|
|
||||||
import { stylesFactory, useTheme2 } from '../../themes'; |
|
||||||
import { Select } from '../Select/Select'; |
|
||||||
import { SelectContainerProps, SelectContainer as BaseSelectContainer } from '../Select/SelectContainer'; |
|
||||||
import { SelectCommonProps } from '../Select/types'; |
|
||||||
|
|
||||||
interface InlineSelectProps<T> extends SelectCommonProps<T> { |
|
||||||
label?: string; |
|
||||||
} |
|
||||||
|
|
||||||
export function InlineSelect<T>({ label: labelProp, ...props }: InlineSelectProps<T>) { |
|
||||||
const theme = useTheme2(); |
|
||||||
const [id] = useState(() => Math.random().toString(16).slice(2)); |
|
||||||
const styles = getSelectStyles(theme); |
|
||||||
const components = { |
|
||||||
SelectContainer, |
|
||||||
ValueContainer, |
|
||||||
SingleValue: ValueContainer, |
|
||||||
}; |
|
||||||
|
|
||||||
return ( |
|
||||||
<div className={styles.root}> |
|
||||||
{labelProp && ( |
|
||||||
<label className={styles.label} htmlFor={id}> |
|
||||||
{labelProp} |
|
||||||
{':'} |
|
||||||
</label> |
|
||||||
)} |
|
||||||
{/* @ts-ignore */} |
|
||||||
<Select openMenuOnFocus inputId={id} {...props} components={components} /> |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
const SelectContainer = <Option, isMulti extends boolean, Group extends GroupBase<Option>>( |
|
||||||
props: SelectContainerProps<Option, isMulti, Group> |
|
||||||
) => { |
|
||||||
const { children } = props; |
|
||||||
|
|
||||||
const theme = useTheme2(); |
|
||||||
const styles = getSelectStyles(theme); |
|
||||||
|
|
||||||
return ( |
|
||||||
<BaseSelectContainer {...props} className={cx(props.className, styles.container)}> |
|
||||||
{children} |
|
||||||
</BaseSelectContainer> |
|
||||||
); |
|
||||||
}; |
|
||||||
|
|
||||||
const ValueContainer = <Option, isMulti extends boolean, Group extends GroupBase<Option>>( |
|
||||||
props: SelectContainerProps<Option, isMulti, Group> |
|
||||||
) => { |
|
||||||
const { className, children } = props; |
|
||||||
const theme = useTheme2(); |
|
||||||
const styles = getSelectStyles(theme); |
|
||||||
|
|
||||||
return <div className={cx(className, styles.valueContainer)}>{children}</div>; |
|
||||||
}; |
|
||||||
|
|
||||||
const getSelectStyles = stylesFactory((theme: GrafanaTheme2) => ({ |
|
||||||
root: css({ |
|
||||||
display: 'flex', |
|
||||||
fontSize: 12, |
|
||||||
alignItems: 'center', |
|
||||||
}), |
|
||||||
|
|
||||||
label: css({ |
|
||||||
color: theme.colors.text.secondary, |
|
||||||
whiteSpace: 'nowrap', |
|
||||||
}), |
|
||||||
|
|
||||||
container: css({ |
|
||||||
background: 'none', |
|
||||||
borderColor: 'transparent', |
|
||||||
}), |
|
||||||
|
|
||||||
valueContainer: css({ |
|
||||||
display: 'flex', |
|
||||||
alignItems: 'center', |
|
||||||
flex: 'initial', |
|
||||||
color: theme.colors.text.secondary, |
|
||||||
fontSize: 12, |
|
||||||
}), |
|
||||||
})); |
|
||||||
@ -1,65 +0,0 @@ |
|||||||
import { ComponentMeta } from '@storybook/react'; |
|
||||||
import React from 'react'; |
|
||||||
|
|
||||||
import { Stack } from '..'; |
|
||||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; |
|
||||||
import { Input } from '../Input/Input'; |
|
||||||
import { Select } from '../Select/Select'; |
|
||||||
|
|
||||||
import { AccessoryButton } from './AccessoryButton'; |
|
||||||
import { InputGroup } from './InputGroup'; |
|
||||||
|
|
||||||
const meta: ComponentMeta<typeof InputGroup> = { |
|
||||||
title: 'Experimental/InputGroup', |
|
||||||
component: InputGroup, |
|
||||||
decorators: [withCenteredStory], |
|
||||||
}; |
|
||||||
|
|
||||||
export function WithTextInputs() { |
|
||||||
return ( |
|
||||||
<InputGroup> |
|
||||||
<Input placeholder="One" /> |
|
||||||
<Input placeholder="Two" /> |
|
||||||
</InputGroup> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
export function WithAccessoryButton() { |
|
||||||
return ( |
|
||||||
<InputGroup> |
|
||||||
<Select value={selectOptions[0]} options={selectOptions} onChange={() => {}} /> |
|
||||||
<AccessoryButton aria-label="Remove group by column" icon="times" variant="secondary" /> |
|
||||||
</InputGroup> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
export function WithSelectsAndInput() { |
|
||||||
return ( |
|
||||||
<Stack direction="column"> |
|
||||||
<InputGroup> |
|
||||||
<Input invalid placeholder="LHS" /> |
|
||||||
<Select value={comparitorOptions[0]} options={comparitorOptions} onChange={() => {}} /> |
|
||||||
<Input placeholder="RHS" /> |
|
||||||
</InputGroup> |
|
||||||
<InputGroup> |
|
||||||
<Input placeholder="LHS" /> |
|
||||||
<Select invalid value={comparitorOptions[0]} options={comparitorOptions} onChange={() => {}} /> |
|
||||||
<Input placeholder="RHS" /> |
|
||||||
</InputGroup> |
|
||||||
<InputGroup> |
|
||||||
<Input placeholder="LHS" /> |
|
||||||
<Select value={comparitorOptions[0]} options={comparitorOptions} onChange={() => {}} /> |
|
||||||
<Input invalid placeholder="RHS" /> |
|
||||||
</InputGroup> |
|
||||||
</Stack> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
const selectOptions = [{ label: 'Prometheus', value: 1 }]; |
|
||||||
const comparitorOptions = [ |
|
||||||
{ label: '=', value: 1 }, |
|
||||||
{ label: '<', value: 2 }, |
|
||||||
{ label: '>', value: 3 }, |
|
||||||
]; |
|
||||||
|
|
||||||
export default meta; |
|
||||||
@ -1,80 +0,0 @@ |
|||||||
import { css, cx } from '@emotion/css'; |
|
||||||
import React, { Children, cloneElement, isValidElement, ReactElement } from 'react'; |
|
||||||
|
|
||||||
import { useStyles2 } from '../../themes'; |
|
||||||
|
|
||||||
type Child = string | undefined | ReactElement<{ className?: string; invalid?: unknown }>; |
|
||||||
interface InputGroupProps { |
|
||||||
// we type the children props so we can test them later on
|
|
||||||
children: Child | Child[]; |
|
||||||
} |
|
||||||
|
|
||||||
export const InputGroup = ({ children }: InputGroupProps) => { |
|
||||||
const styles = useStyles2(getStyles); |
|
||||||
|
|
||||||
// Find children with an invalid prop, and set a class name to raise their z-index so all
|
|
||||||
// of the invalid border is visible
|
|
||||||
const modifiedChildren = Children.map(children, (child) => { |
|
||||||
if (isValidElement(child) && child.props.invalid) { |
|
||||||
return cloneElement(child, { className: cx(child.props.className, styles.invalidChild) }); |
|
||||||
} |
|
||||||
|
|
||||||
return child; |
|
||||||
}); |
|
||||||
|
|
||||||
return <div className={styles.root}>{modifiedChildren}</div>; |
|
||||||
}; |
|
||||||
|
|
||||||
// The later in the array the higher the priority for showing that element's border
|
|
||||||
const borderPriority = [ |
|
||||||
'' as const, // lowest priority
|
|
||||||
'base' as const, |
|
||||||
'hovered' as const, |
|
||||||
'invalid' as const, |
|
||||||
'focused' as const, // highest priority
|
|
||||||
]; |
|
||||||
|
|
||||||
const getStyles = () => ({ |
|
||||||
root: css({ |
|
||||||
display: 'flex', |
|
||||||
|
|
||||||
// Style the direct children of the component
|
|
||||||
'> *': { |
|
||||||
'&:not(:first-child)': { |
|
||||||
// Negative margin hides the double-border on adjacent selects
|
|
||||||
marginLeft: -1, |
|
||||||
}, |
|
||||||
|
|
||||||
'&:first-child': { |
|
||||||
borderTopRightRadius: 0, |
|
||||||
borderBottomRightRadius: 0, |
|
||||||
}, |
|
||||||
|
|
||||||
'&:last-child': { |
|
||||||
borderTopLeftRadius: 0, |
|
||||||
borderBottomLeftRadius: 0, |
|
||||||
}, |
|
||||||
|
|
||||||
'&:not(:first-child):not(:last-child)': { |
|
||||||
borderRadius: 0, |
|
||||||
}, |
|
||||||
|
|
||||||
//
|
|
||||||
position: 'relative', |
|
||||||
zIndex: borderPriority.indexOf('base'), |
|
||||||
|
|
||||||
// Adjacent borders are overlapping, so raise children up when hovering etc
|
|
||||||
// so all that child's borders are visible.
|
|
||||||
'&:hover': { |
|
||||||
zIndex: borderPriority.indexOf('hovered'), |
|
||||||
}, |
|
||||||
'&:focus-within': { |
|
||||||
zIndex: borderPriority.indexOf('focused'), |
|
||||||
}, |
|
||||||
}, |
|
||||||
}), |
|
||||||
|
|
||||||
invalidChild: css({ |
|
||||||
zIndex: borderPriority.indexOf('invalid'), |
|
||||||
}), |
|
||||||
}); |
|
||||||
@ -1,40 +0,0 @@ |
|||||||
import { css, cx } from '@emotion/css'; |
|
||||||
import React from 'react'; |
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data'; |
|
||||||
|
|
||||||
import { stylesFactory, useTheme2 } from '../../themes'; |
|
||||||
|
|
||||||
export interface SpaceProps { |
|
||||||
v?: number; |
|
||||||
h?: number; |
|
||||||
layout?: 'block' | 'inline'; |
|
||||||
} |
|
||||||
|
|
||||||
export const Space = (props: SpaceProps) => { |
|
||||||
const theme = useTheme2(); |
|
||||||
const styles = getStyles(theme, props); |
|
||||||
|
|
||||||
return <span className={cx(styles.wrapper)} />; |
|
||||||
}; |
|
||||||
|
|
||||||
Space.defaultProps = { |
|
||||||
v: 0, |
|
||||||
h: 0, |
|
||||||
layout: 'block', |
|
||||||
}; |
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme2, props: SpaceProps) => ({ |
|
||||||
wrapper: css([ |
|
||||||
{ |
|
||||||
paddingRight: theme.spacing(props.h ?? 0), |
|
||||||
paddingBottom: theme.spacing(props.v ?? 0), |
|
||||||
}, |
|
||||||
props.layout === 'inline' && { |
|
||||||
display: 'inline-block', |
|
||||||
}, |
|
||||||
props.layout === 'block' && { |
|
||||||
display: 'block', |
|
||||||
}, |
|
||||||
]), |
|
||||||
})); |
|
||||||
@ -1,31 +0,0 @@ |
|||||||
import { css } from '@emotion/css'; |
|
||||||
import React, { CSSProperties, useCallback } from 'react'; |
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data'; |
|
||||||
|
|
||||||
import { useStyles2 } from '../../themes'; |
|
||||||
|
|
||||||
interface StackProps { |
|
||||||
direction?: CSSProperties['flexDirection']; |
|
||||||
alignItems?: CSSProperties['alignItems']; |
|
||||||
wrap?: boolean; |
|
||||||
gap?: number; |
|
||||||
flexGrow?: CSSProperties['flexGrow']; |
|
||||||
} |
|
||||||
|
|
||||||
export const Stack = ({ children, ...props }: React.PropsWithChildren<StackProps>) => { |
|
||||||
const styles = useStyles2(useCallback((theme) => getStyles(theme, props), [props])); |
|
||||||
|
|
||||||
return <div className={styles.root}>{children}</div>; |
|
||||||
}; |
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2, props: StackProps) => ({ |
|
||||||
root: css({ |
|
||||||
display: 'flex', |
|
||||||
flexDirection: props.direction ?? 'row', |
|
||||||
flexWrap: props.wrap ?? true ? 'wrap' : undefined, |
|
||||||
alignItems: props.alignItems, |
|
||||||
gap: theme.spacing(props.gap ?? 2), |
|
||||||
flexGrow: props.flexGrow, |
|
||||||
}), |
|
||||||
}); |
|
||||||
@ -1,13 +0,0 @@ |
|||||||
export { AccessoryButton } from './AccessoryButton'; |
|
||||||
export { EditorFieldGroup } from './EditorFieldGroup'; |
|
||||||
export { EditorHeader } from './EditorHeader'; |
|
||||||
export { EditorField } from './EditorField'; |
|
||||||
export { EditorRow } from './EditorRow'; |
|
||||||
export { EditorList } from './EditorList'; |
|
||||||
export { EditorRows } from './EditorRows'; |
|
||||||
export { EditorSwitch } from './EditorSwitch'; |
|
||||||
export { FlexItem } from './FlexItem'; |
|
||||||
export { Stack } from './Stack'; |
|
||||||
export { InlineSelect } from './InlineSelect'; |
|
||||||
export { InputGroup } from './InputGroup'; |
|
||||||
export { Space } from './Space'; |
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue