Chore: Clean up some @grafana/ui SCSS (#87986)

* clean up drawer and slider styles

* clean up cascader styles
This commit is contained in:
Ashley Harrison 2024-05-20 12:51:34 +01:00 committed by GitHub
parent 20294b0fb6
commit 3800b97a5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 218 additions and 208 deletions

View File

@ -1,4 +1,4 @@
import { css } from '@emotion/css';
import { css, cx } from '@emotion/css';
import RCCascader, { BaseOptionType } from 'rc-cascader';
import React from 'react';
@ -9,6 +9,7 @@ import { IconName } from '../../types/icon';
import { Button, ButtonProps } from '../Button';
import { CascaderOption } from '../Cascader/Cascader';
import { onChangeCascader, onLoadDataCascader } from '../Cascader/optionMappings';
import { getCascaderStyles } from '../Cascader/styles';
import { Icon } from '../Icon/Icon';
export interface ButtonCascaderProps {
@ -30,6 +31,7 @@ export interface ButtonCascaderProps {
export const ButtonCascader = (props: ButtonCascaderProps) => {
const { onChange, className, loadData, icon, buttonProps, hideDownIcon, variant, disabled, ...rest } = props;
const styles = useStyles2(getStyles);
const cascaderStyles = useStyles2(getCascaderStyles);
// Weird way to do this bit it goes around a styling issue in Button where even null/undefined child triggers
// styling change which messes up the look if there is only single icon content.
@ -42,7 +44,7 @@ export const ButtonCascader = (props: ButtonCascaderProps) => {
<RCCascader
onChange={onChangeCascader(onChange)}
loadData={onLoadDataCascader(loadData)}
dropdownClassName={styles.popup}
dropdownClassName={cx(cascaderStyles.dropdown, styles.popup)}
{...rest}
expandIcon={null}
>

View File

@ -1,197 +0,0 @@
.rc-cascader {
font-size: 12px;
&-dropdown {
position: absolute;
// Required, otherwise the portal that the popup is shown in will render under other components
z-index: 9999;
&-hidden {
display: none;
}
}
&-menus {
//font-size: 12px;
overflow: hidden;
background: $page-bg;
border: $panel-border;
border-radius: $border-radius;
box-shadow: $typeahead-shadow;
white-space: nowrap;
&.slide-up-enter,
&.slide-up-appear {
animation-duration: 0.3s;
animation-fill-mode: both;
transform-origin: 0 0;
opacity: 0;
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
animation-play-state: paused;
}
&.slide-up-enter.slide-up-enter-active.rc-cascader-menus-placement,
&.slide-up-appear.slide-up-appear-active.rc-cascader-menus-placement {
&-bottomLeft {
animation-name: SlideUpIn;
animation-play-state: running;
}
&-topLeft {
animation-name: SlideDownIn;
animation-play-state: running;
}
}
&.slide-up-leave {
animation-duration: 0.3s;
animation-fill-mode: both;
transform-origin: 0 0;
opacity: 1;
animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
animation-play-state: paused;
&.slide-up-leave-active.rc-cascader-menus-placement {
&-bottomLeft {
animation-name: SlideUpOut;
animation-play-state: running;
}
&-topLeft {
animation-name: SlideDownOut;
animation-play-state: running;
}
}
}
}
&-menu {
display: inline-block;
/* width: 100px; */
max-width: 50vw;
height: 192px;
list-style: none;
margin: 0;
padding: 0;
border-right: $panel-border;
overflow: auto;
&:last-child {
border-right: 0;
}
&-item {
height: 32px;
line-height: 32px;
padding: 0 2.5em 0 16px;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: all 0.3s ease;
position: relative;
&:hover {
background: $colors-action-hover;
}
&-disabled {
cursor: not-allowed;
color: $text-color-weak;
&:hover {
background: transparent;
}
&:after {
position: absolute;
right: 12px;
content: 'loading';
color: $text-color-weak;
font-style: italic;
}
}
&-active {
color: $text-color-strong;
background: $colors-action-selected;
&:hover {
background: $colors-action-hover;
}
}
&-expand {
position: relative;
&:after {
background: $text-color-weak;
content: '';
height: 24px;
mask: url(../img/icons/unicons/angle-right.svg);
mask-type: luminance;
position: absolute;
right: 0px;
top: calc((32px - 24px) / 2);
width: 24px;
}
}
}
}
}
@keyframes SlideUpIn {
0% {
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0.8);
}
100% {
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
}
@keyframes SlideUpOut {
0% {
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
100% {
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0.8);
}
}
@keyframes SlideDownIn {
0% {
opacity: 0;
transform-origin: 0% 100%;
transform: scaleY(0.8);
}
100% {
opacity: 1;
transform-origin: 0% 100%;
transform: scaleY(1);
}
}
@keyframes SlideDownOut {
0% {
opacity: 1;
transform-origin: 0% 100%;
transform: scaleY(1);
}
100% {
opacity: 0;
transform-origin: 0% 100%;
transform: scaleY(0.8);
}
}

View File

@ -31,7 +31,7 @@ const options = [
},
];
const CascaderWithOptionsStateUpdate = (props: Omit<CascaderProps, 'options'>) => {
const CascaderWithOptionsStateUpdate = (props: Omit<CascaderProps, 'options' | 'theme'>) => {
const [updatedOptions, setOptions] = React.useState<CascaderOption[]>([
{
label: 'Initial state option',

View File

@ -5,6 +5,8 @@ import React, { PureComponent } from 'react';
import { SelectableValue } from '@grafana/data';
import { withTheme2 } from '../../themes';
import { Themeable2 } from '../../types';
import { Icon } from '../Icon/Icon';
import { IconButton } from '../IconButton/IconButton';
import { Input } from '../Input/Input';
@ -12,8 +14,9 @@ import { Stack } from '../Layout/Stack/Stack';
import { Select } from '../Select/Select';
import { onChangeCascader } from './optionMappings';
import { getCascaderStyles } from './styles';
export interface CascaderProps {
export interface CascaderProps extends Themeable2 {
/** The separator between levels in the search */
separator?: string;
placeholder?: string;
@ -81,7 +84,7 @@ const disableDivFocus = css({
const DEFAULT_SEPARATOR = ' / ';
export class Cascader extends PureComponent<CascaderProps, CascaderState> {
class UnthemedCascader extends PureComponent<CascaderProps, CascaderState> {
constructor(props: CascaderProps) {
super(props);
const searchableOptions = this.getSearchableOptions(props.options);
@ -231,10 +234,12 @@ export class Cascader extends PureComponent<CascaderProps, CascaderState> {
disabled,
id,
isClearable,
theme,
} = this.props;
const { focusCascade, isSearching, rcValue, activeLabel, inputValue } = this.state;
const searchableOptions = this.getSearchableOptions(options);
const styles = getCascaderStyles(theme);
return (
<div>
@ -264,6 +269,7 @@ export class Cascader extends PureComponent<CascaderProps, CascaderState> {
expandIcon={null}
open={this.props.alwaysOpen}
disabled={disabled}
dropdownClassName={styles.dropdown}
>
<div className={disableDivFocus}>
<Input
@ -301,3 +307,5 @@ export class Cascader extends PureComponent<CascaderProps, CascaderState> {
);
}
}
export const Cascader = withTheme2(UnthemedCascader);

View File

@ -0,0 +1,199 @@
import { css, keyframes } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
const slideUpIn = keyframes({
'0%': {
opacity: 0,
transformOrigin: '0% 0%',
transform: 'scaleY(0.8)',
},
'100%': {
opacity: 1,
transformOrigin: '0% 0%',
transform: 'scaleY(1)',
},
});
const slideUpOut = keyframes({
'0%': {
opacity: 1,
transformOrigin: '0% 0%',
transform: 'scaleY(1)',
},
'100%': {
opacity: 0,
transformOrigin: '0% 0%',
transform: 'scaleY(0.8)',
},
});
const slideDownIn = keyframes({
'0%': {
opacity: 0,
transformOrigin: '0% 100%',
transform: 'scaleY(0.8)',
},
'100%': {
opacity: 1,
transformOrigin: '0% 100%',
transform: 'scaleY(1)',
},
});
const slideDownOut = keyframes({
'0%': {
opacity: 1,
transformOrigin: '0% 100%',
transform: 'scaleY(1)',
},
'100%': {
opacity: 0,
transformOrigin: '0% 100%',
transform: 'scaleY(0.8)',
},
});
export const getCascaderStyles = (theme: GrafanaTheme2) => ({
dropdown: css({
'&.rc-cascader-dropdown': {
position: 'absolute',
// Required, otherwise the portal that the popup is shown in will render under other components
zIndex: 9999,
'&-hidden': {
display: 'none',
},
},
'.rc-cascader': {
'&-menus': {
overflow: 'hidden',
background: theme.colors.background.canvas,
border: `1px solid ${theme.colors.border.weak}`,
borderRadius: theme.shape.radius.default,
boxShadow: theme.shadows.z3,
whiteSpace: 'nowrap',
'&.slide-up-enter, &.slide-up-appear': {
animationDuration: '0.3s',
animationFillMode: 'both',
transformOrigin: '0 0',
opacity: 0,
animationTimingFunction: 'cubic-bezier(0.08, 0.82, 0.17, 1)',
animationPlayState: 'paused',
},
'&.slide-up-enter.slide-up-enter-active.rc-cascader-menus-placement, &.slide-up-appear.slide-up-appear-active.rc-cascader-menus-placement':
{
'&-bottomLeft': {
animationName: slideUpIn,
animationPlayState: 'running',
},
'&-topLeft': {
animationName: slideDownIn,
animationPlayState: 'running',
},
},
'&.slide-up-leave': {
animationDuration: '0.3s',
animationFillMode: 'both',
transformOrigin: '0 0',
opacity: 1,
animationTimingFunction: 'cubic-bezier(0.6, 0.04, 0.98, 0.34)',
animationPlayState: 'paused',
'&.slide-up-leave-active.rc-cascader-menus-placement': {
'&-bottomLeft': {
animationName: slideUpOut,
animationPlayState: 'running',
},
'&-topLeft': {
animationName: slideDownOut,
animationPlayState: 'running',
},
},
},
},
'&-menu': {
display: 'inline-block',
maxWidth: '50vw',
height: '192px',
listStyle: 'none',
margin: 0,
padding: 0,
borderRight: `1px solid ${theme.colors.border.weak}`,
overflow: 'auto',
'&:last-child': {
borderRight: 0,
},
'&-item': {
height: theme.spacing(4),
lineHeight: theme.spacing(4),
padding: theme.spacing(0, 4, 0, 2),
cursor: 'pointer',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
transition: 'all 0.3s ease',
position: 'relative',
'&:hover': {
background: theme.colors.action.hover,
},
'&-disabled': {
cursor: 'not-allowed',
color: theme.colors.text.disabled,
'&:hover': {
background: 'transparent',
},
'&:after': {
position: 'absolute',
right: '12px',
content: "'loading'",
color: theme.colors.text.disabled,
fontStyle: 'italic',
},
},
'&-active': {
color: theme.colors.text.maxContrast,
background: theme.colors.background.secondary,
'&:hover': {
background: theme.colors.action.hover,
},
},
'&-expand': {
position: 'relative',
'&:after': {
background: theme.colors.background.secondary,
content: "''",
height: theme.spacing(3),
mask: 'url(../img/icons/unicons/angle-right.svg)',
maskType: 'luminance',
position: 'absolute',
right: 0,
top: theme.spacing(0.5),
width: theme.spacing(3),
},
},
},
},
},
}),
});

View File

@ -15,6 +15,8 @@ import { getDragStyles } from '../DragHandle/DragHandle';
import { IconButton } from '../IconButton/IconButton';
import { Text } from '../Text/Text';
import 'rc-drawer/assets/index.css';
export interface Props {
children: ReactNode;
/** Title shown at the top of the drawer */

View File

@ -1,2 +0,0 @@
// Need to import this to get default styles from rc-drawer
@import 'rc-drawer/assets/index.css';

View File

@ -1 +0,0 @@
@import 'rc-slider/assets/index.css';

View File

@ -3,6 +3,8 @@ import { css as cssCore } from '@emotion/react';
import { GrafanaTheme2 } from '@grafana/data';
import 'rc-slider/assets/index.css';
export const getStyles = (theme: GrafanaTheme2, isHorizontal: boolean, hasMarks = false) => {
const { spacing } = theme;
const railColor = theme.colors.border.strong;

View File

@ -1,6 +1,3 @@
@import 'ButtonCascader/ButtonCascader';
@import 'Drawer/Drawer';
@import 'Forms/Legacy/Select/Select';
@import 'DateTimePickers/TimeOfDayPicker';
@import 'Slider/Slider';
@import 'uPlot/Plot';