GrafanaUI: Add external border radius mixin (#100266)
* Add external border radius mixin * Add getInternalRadius mixin for inverse use case * Update border radius fns with clearler types and JSDoc annotations * tidy up interface, apply correctly in radio buttons, add internal story * connect padding/offset in radiobuttongroup/radiobutton * split out offset into its own param --------- Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
This commit is contained in:
co-authored by
Ashley Harrison
parent
852243c8f0
commit
98e4d57bcd
@@ -5,10 +5,11 @@ import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { StringSelector, selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { useStyles2 } from '../../../themes';
|
||||
import { getFocusStyles, getMouseFocusStyles } from '../../../themes/mixins';
|
||||
import { getFocusStyles, getInternalRadius, getMouseFocusStyles } from '../../../themes/mixins';
|
||||
import { Tooltip } from '../../Tooltip/Tooltip';
|
||||
import { getPropertiesForButtonSize } from '../commonStyles';
|
||||
|
||||
export const RADIO_GROUP_PADDING = 2;
|
||||
export type RadioButtonSize = 'sm' | 'md';
|
||||
|
||||
export interface RadioButtonProps {
|
||||
@@ -130,7 +131,7 @@ const getRadioButtonStyles = (theme: GrafanaTheme2, size: RadioButtonSize, fullW
|
||||
lineHeight: `${labelHeight}px`,
|
||||
color: textColor,
|
||||
padding: theme.spacing(0, padding),
|
||||
borderRadius: theme.shape.radius.default,
|
||||
borderRadius: getInternalRadius(theme, RADIO_GROUP_PADDING),
|
||||
background: theme.colors.background.primary,
|
||||
cursor: 'pointer',
|
||||
userSelect: 'none',
|
||||
|
||||
@@ -7,8 +7,7 @@ import { GrafanaTheme2, SelectableValue, toIconName } from '@grafana/data';
|
||||
import { useStyles2 } from '../../../themes';
|
||||
import { Icon } from '../../Icon/Icon';
|
||||
|
||||
import { RadioButtonSize, RadioButton } from './RadioButton';
|
||||
|
||||
import { RadioButtonSize, RadioButton, RADIO_GROUP_PADDING } from './RadioButton';
|
||||
export interface RadioButtonGroupProps<T> {
|
||||
value?: T;
|
||||
id?: string;
|
||||
@@ -119,7 +118,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
flexWrap: 'nowrap',
|
||||
border: `1px solid ${theme.components.input.borderColor}`,
|
||||
borderRadius: theme.shape.radius.default,
|
||||
padding: '2px',
|
||||
padding: RADIO_GROUP_PADDING,
|
||||
'&:hover': {
|
||||
borderColor: theme.components.input.borderHover,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Meta, StoryFn } from '@storybook/react';
|
||||
|
||||
import { BorderRadiusContainer } from './BorderRadius';
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'Docs Overview/Theme',
|
||||
component: BorderRadiusContainer,
|
||||
decorators: [],
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
args: {
|
||||
referenceBorderRadius: 20,
|
||||
referenceBorderWidth: 10,
|
||||
offset: 0,
|
||||
borderWidth: 2,
|
||||
},
|
||||
argTypes: {
|
||||
offset: {
|
||||
control: {
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
referenceBorderRadius: {
|
||||
control: {
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
referenceBorderWidth: {
|
||||
control: {
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
borderWidth: {
|
||||
control: {
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const OffsetBorderRadius: StoryFn<typeof BorderRadiusContainer> = (args) => {
|
||||
return <BorderRadiusContainer {...args} />;
|
||||
};
|
||||
|
||||
export default meta;
|
||||
@@ -0,0 +1,87 @@
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { getInternalRadius, getExternalRadius } from '../../themes/mixins';
|
||||
import { Stack } from '../Layout/Stack/Stack';
|
||||
import { Text } from '../Text/Text';
|
||||
|
||||
interface DemoBoxProps {
|
||||
referenceBorderRadius: number;
|
||||
referenceBorderWidth: number;
|
||||
offset: number;
|
||||
borderWidth: number;
|
||||
}
|
||||
|
||||
export const BorderRadiusContainer = ({
|
||||
referenceBorderRadius,
|
||||
referenceBorderWidth,
|
||||
offset,
|
||||
borderWidth,
|
||||
}: DemoBoxProps) => {
|
||||
const styles = useStyles2(getStyles, referenceBorderRadius, referenceBorderWidth, offset, borderWidth);
|
||||
return (
|
||||
<Stack direction="column" alignItems="center" gap={4}>
|
||||
<Stack alignItems="center">
|
||||
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
|
||||
<Text variant="code">getInternalRadius</Text>
|
||||
<div className={styles.baseForInternal}>
|
||||
<div className={styles.internalContainer} />
|
||||
</div>
|
||||
</Stack>
|
||||
<Stack alignItems="center">
|
||||
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
|
||||
<Text variant="code">getExternalRadius</Text>
|
||||
<div className={styles.externalContainer}>
|
||||
<div className={styles.baseForExternal} />
|
||||
</div>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = (
|
||||
theme: GrafanaTheme2,
|
||||
referenceBorderRadius: number,
|
||||
referenceBorderWidth: number,
|
||||
offset: number,
|
||||
borderWidth: number
|
||||
) => ({
|
||||
baseForInternal: css({
|
||||
backgroundColor: theme.colors.action.disabledBackground,
|
||||
border: `${referenceBorderWidth}px dashed ${theme.colors.action.disabledText}`,
|
||||
borderRadius: referenceBorderRadius,
|
||||
display: 'flex',
|
||||
height: '80px',
|
||||
padding: offset,
|
||||
width: '300px',
|
||||
}),
|
||||
baseForExternal: css({
|
||||
backgroundColor: theme.colors.action.disabledBackground,
|
||||
border: `${referenceBorderWidth}px dashed ${theme.colors.action.disabledText}`,
|
||||
borderRadius: referenceBorderRadius,
|
||||
height: '80px',
|
||||
flex: 1,
|
||||
width: '300px',
|
||||
}),
|
||||
internalContainer: css({
|
||||
backgroundColor: theme.colors.background.primary,
|
||||
border: `${borderWidth}px solid ${theme.colors.primary.main}`,
|
||||
borderRadius: getInternalRadius(theme, offset, {
|
||||
parentBorderRadius: referenceBorderRadius,
|
||||
parentBorderWidth: referenceBorderWidth,
|
||||
}),
|
||||
flex: 1,
|
||||
}),
|
||||
externalContainer: css({
|
||||
border: `${borderWidth}px solid ${theme.colors.primary.main}`,
|
||||
borderRadius: getExternalRadius(theme, offset, {
|
||||
childBorderRadius: referenceBorderRadius,
|
||||
selfBorderWidth: borderWidth,
|
||||
}),
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
padding: offset,
|
||||
}),
|
||||
});
|
||||
@@ -82,3 +82,55 @@ export const getTooltipContainerStyles = (theme: GrafanaTheme2) => ({
|
||||
borderRadius: theme.shape.radius.default,
|
||||
zIndex: theme.zIndex.tooltip,
|
||||
});
|
||||
|
||||
interface ExternalRadiusAdditionalOptions {
|
||||
selfBorderWidth?: number;
|
||||
childBorderRadius?: number;
|
||||
}
|
||||
/**
|
||||
* Calculates a border radius for an element, based on border radius of its child.
|
||||
*
|
||||
* @param theme
|
||||
* @param offset - The distance to offset from the child element, should be >= 0.
|
||||
* @param additionalOptions
|
||||
* @param additionalOptions.selfBorderWidth - The border width of the element itself (default: 1)
|
||||
* @param additionalOptions.childBorderRadius - The border radius of the child element (default: theme default radius)
|
||||
* @returns A CSS calc() expression that returns the relative external radius value
|
||||
*/
|
||||
export const getExternalRadius = (
|
||||
theme: GrafanaTheme2,
|
||||
offset: number,
|
||||
additionalOptions: ExternalRadiusAdditionalOptions = {}
|
||||
) => {
|
||||
const { selfBorderWidth = 1, childBorderRadius } = additionalOptions;
|
||||
|
||||
const childBorderRadiusPx = childBorderRadius !== undefined ? `${childBorderRadius}px` : theme.shape.radius.default;
|
||||
return `calc(max(0px, ${childBorderRadiusPx} + ${offset}px + ${selfBorderWidth}px))`;
|
||||
};
|
||||
|
||||
interface InternalRadiusAdditionalOptions {
|
||||
parentBorderWidth?: number;
|
||||
parentBorderRadius?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a border radius for an element, based on border radius of its parent.
|
||||
*
|
||||
* @param theme
|
||||
* @param offset - The distance to offset from the parent element, should be >= 0.
|
||||
* @param additionalOptions
|
||||
* @param additionalOptions.parentBorderWidth - The border width of the parent element (default: 1)
|
||||
* @param additionalOptions.parentBorderRadius - The border radius of the parent element (default: theme default radius)
|
||||
* @returns A CSS calc() expression that returns the relative internal radius value
|
||||
*/
|
||||
export const getInternalRadius = (
|
||||
theme: GrafanaTheme2,
|
||||
offset: number,
|
||||
additionalOptions: InternalRadiusAdditionalOptions = {}
|
||||
) => {
|
||||
const { parentBorderWidth = 1, parentBorderRadius } = additionalOptions;
|
||||
|
||||
const parentBorderRadiusPx =
|
||||
parentBorderRadius !== undefined ? `${parentBorderRadius}px` : theme.shape.radius.default;
|
||||
return `calc(max(0px, ${parentBorderRadiusPx} - ${offset}px - ${parentBorderWidth}px))`;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user