mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Enable no-unreduced-motion
and fix errors (#86572)
* enable `no-unreduced-motion` in betterer * move all animation calls inside handleReducedMotion * fix violations + enable rule * update rule README * remove unnecessary transition from <Collapse> * remove handleReducedMotion utility and add handleMotion to theme * update to use new theme value * handle Dropdown and IconButton * handle AppChromeMenu and update lint message * keep rotation at a reduced speed * handle DashboardLoading
This commit is contained in:
parent
8a1f43a65d
commit
c151a97110
@ -8,6 +8,7 @@
|
|||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"@grafana/no-border-radius-literal": "error",
|
"@grafana/no-border-radius-literal": "error",
|
||||||
|
"@grafana/no-unreduced-motion": "error",
|
||||||
"react/prop-types": "off",
|
"react/prop-types": "off",
|
||||||
// need to ignore emotion's `css` prop, see https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md#rule-options
|
// need to ignore emotion's `css` prop, see https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md#rule-options
|
||||||
"react/no-unknown-property": ["error", { "ignore": ["css"] }],
|
"react/no-unknown-property": ["error", { "ignore": ["css"] }],
|
||||||
|
@ -53,6 +53,12 @@ export function create(props: string | string[] = ['all'], options: CreateTransi
|
|||||||
.join(',');
|
.join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReducedMotionProps = 'no-preference' | 'reduce';
|
||||||
|
|
||||||
|
export function handleMotion(...props: ReducedMotionProps[]) {
|
||||||
|
return props.map((prop) => `@media (prefers-reduced-motion: ${prop})`).join(',');
|
||||||
|
}
|
||||||
|
|
||||||
export function getAutoHeightDuration(height: number) {
|
export function getAutoHeightDuration(height: number) {
|
||||||
if (!height) {
|
if (!height) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -74,6 +80,7 @@ export interface ThemeTransitions {
|
|||||||
duration: typeof duration;
|
duration: typeof duration;
|
||||||
easing: typeof easing;
|
easing: typeof easing;
|
||||||
getAutoHeightDuration: typeof getAutoHeightDuration;
|
getAutoHeightDuration: typeof getAutoHeightDuration;
|
||||||
|
handleMotion: typeof handleMotion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
@ -83,5 +90,6 @@ export function createTransitions(): ThemeTransitions {
|
|||||||
duration,
|
duration,
|
||||||
easing,
|
easing,
|
||||||
getAutoHeightDuration,
|
getAutoHeightDuration,
|
||||||
|
handleMotion,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,89 @@ Avoid direct use of `animation*` or `transition*` properties.
|
|||||||
|
|
||||||
To account for users with motion sensitivities, these should always be wrapped in a [`prefers-reduced-motion`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion) media query.
|
To account for users with motion sensitivities, these should always be wrapped in a [`prefers-reduced-motion`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion) media query.
|
||||||
|
|
||||||
`@grafana/ui` exposes a `handledReducedMotion` utility function that can be used to handle this.
|
There is a `handleMotion` utility function exposed on the theme that can help with this.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Bad ❌
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
loading: css({
|
||||||
|
animationName: rotate,
|
||||||
|
animationDuration: '2s',
|
||||||
|
animationIterationCount: 'infinite',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Good ✅
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
loading: css({
|
||||||
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
|
animationName: rotate,
|
||||||
|
animationDuration: '2s',
|
||||||
|
animationIterationCount: 'infinite',
|
||||||
|
},
|
||||||
|
[theme.transitions.handleMotion('reduce')]: {
|
||||||
|
animationName: pulse,
|
||||||
|
animationDuration: '2s',
|
||||||
|
animationIterationCount: 'infinite',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Good ✅
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
loading: css({
|
||||||
|
'@media (prefers-reduced-motion: no-preference)': {
|
||||||
|
animationName: rotate,
|
||||||
|
animationDuration: '2s',
|
||||||
|
animationIterationCount: 'infinite',
|
||||||
|
},
|
||||||
|
'@media (prefers-reduced-motion: reduce)': {
|
||||||
|
animationName: pulse,
|
||||||
|
animationDuration: '2s',
|
||||||
|
animationIterationCount: 'infinite',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Note we've switched the potentially sensitive rotating animation to a less intense pulse animation when `prefers-reduced-motion` is set.
|
||||||
|
|
||||||
|
Animations that involve only non-moving properties, like opacity, color, and blurs, are unlikely to be problematic. In those cases, you still need to wrap the animation in a `prefers-reduced-motion` media query, but you can use the same animation for both cases:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Bad ❌
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
card: css({
|
||||||
|
transition: theme.transitions.create(['background-color'], {
|
||||||
|
duration: theme.transitions.duration.short,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Good ✅
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
card: css({
|
||||||
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
transition: theme.transitions.create(['background-color'], {
|
||||||
|
duration: theme.transitions.duration.short,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Good ✅
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
card: css({
|
||||||
|
'@media (prefers-reduced-motion: no-preference), @media (prefers-reduced-motion: reduce)': {
|
||||||
|
transition: theme.transitions.create(['background-color'], {
|
||||||
|
duration: theme.transitions.duration.short,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### `theme-token-usage`
|
### `theme-token-usage`
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ const rule = createRule({
|
|||||||
description: 'Check if animation or transition properties are used directly.',
|
description: 'Check if animation or transition properties are used directly.',
|
||||||
},
|
},
|
||||||
messages: {
|
messages: {
|
||||||
noUnreducedMotion: 'Avoid direct use of `animation*` or `transition*` properties. Use the `handleReducedMotion` utility function or wrap in a `prefers-reduced-motion` media query.',
|
noUnreducedMotion: 'Avoid direct use of `animation*` or `transition*` properties. Use the `handleMotion` utility function from theme.transitions or wrap in a `prefers-reduced-motion` media query.',
|
||||||
},
|
},
|
||||||
schema: [],
|
schema: [],
|
||||||
},
|
},
|
||||||
|
@ -238,7 +238,9 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
borderRadius: theme.shape.radius.default,
|
borderRadius: theme.shape.radius.default,
|
||||||
marginBottom: theme.spacing(1),
|
marginBottom: theme.spacing(1),
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
transition: 'all 0.5s ease-in 0s',
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
transition: 'all 0.5s ease-in 0s',
|
||||||
|
},
|
||||||
height: '100%',
|
height: '100%',
|
||||||
}),
|
}),
|
||||||
cardError: css({
|
cardError: css({
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { css, keyframes } from '@emotion/css';
|
import { css, keyframes } from '@emotion/css';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
|
||||||
import { useStyles2 } from '../../themes';
|
import { useStyles2 } from '../../themes';
|
||||||
|
|
||||||
export const EllipsisAnimated = React.memo(() => {
|
export const EllipsisAnimated = React.memo(() => {
|
||||||
@ -16,19 +18,25 @@ export const EllipsisAnimated = React.memo(() => {
|
|||||||
|
|
||||||
EllipsisAnimated.displayName = 'EllipsisAnimated';
|
EllipsisAnimated.displayName = 'EllipsisAnimated';
|
||||||
|
|
||||||
const getStyles = () => {
|
const getStyles = (theme: GrafanaTheme2) => {
|
||||||
return {
|
return {
|
||||||
ellipsis: css({
|
ellipsis: css({
|
||||||
display: 'inline',
|
display: 'inline',
|
||||||
}),
|
}),
|
||||||
firstDot: css({
|
firstDot: css({
|
||||||
animation: `${firstDot} 2s linear infinite`,
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
animation: `${firstDot} 2s linear infinite`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
secondDot: css({
|
secondDot: css({
|
||||||
animation: `${secondDot} 2s linear infinite`,
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
animation: `${secondDot} 2s linear infinite`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
thirdDot: css({
|
thirdDot: css({
|
||||||
animation: `${thirdDot} 2s linear infinite`,
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
animation: `${thirdDot} 2s linear infinite`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -120,7 +120,9 @@ const getLabelStyles = (theme: GrafanaTheme2) => ({
|
|||||||
fontWeight: theme.typography.fontWeightMedium,
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
backgroundColor: theme.colors.primary.shade,
|
backgroundColor: theme.colors.primary.shade,
|
||||||
color: theme.colors.text.primary,
|
color: theme.colors.text.primary,
|
||||||
animation: 'pulse 3s ease-out 0s infinite normal forwards',
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
animation: 'pulse 3s ease-out 0s infinite normal forwards',
|
||||||
|
},
|
||||||
'@keyframes pulse': {
|
'@keyframes pulse': {
|
||||||
'0%': {
|
'0%': {
|
||||||
color: theme.colors.text.primary,
|
color: theme.colors.text.primary,
|
||||||
|
@ -94,9 +94,11 @@ export const getCardContainerStyles = (
|
|||||||
borderRadius: theme.shape.radius.default,
|
borderRadius: theme.shape.radius.default,
|
||||||
marginBottom: '8px',
|
marginBottom: '8px',
|
||||||
pointerEvents: disabled ? 'none' : 'auto',
|
pointerEvents: disabled ? 'none' : 'auto',
|
||||||
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
duration: theme.transitions.duration.short,
|
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
|
||||||
}),
|
duration: theme.transitions.duration.short,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
...(!disableHover && {
|
...(!disableHover && {
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
@ -123,9 +125,11 @@ export const getCardContainerStyles = (
|
|||||||
position: 'relative',
|
position: 'relative',
|
||||||
pointerEvents: disabled ? 'none' : 'auto',
|
pointerEvents: disabled ? 'none' : 'auto',
|
||||||
marginBottom: theme.spacing(1),
|
marginBottom: theme.spacing(1),
|
||||||
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
duration: theme.transitions.duration.short,
|
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
|
||||||
}),
|
duration: theme.transitions.duration.short,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
...(!disableHover && {
|
...(!disableHover && {
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
|
@ -71,7 +71,6 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
label: 'collapse__header',
|
label: 'collapse__header',
|
||||||
padding: theme.spacing(1, 2, 1, 2),
|
padding: theme.spacing(1, 2, 1, 2),
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
transition: 'all 0.1s linear',
|
|
||||||
}),
|
}),
|
||||||
headerCollapsed: css({
|
headerCollapsed: css({
|
||||||
label: 'collapse__header--collapsed',
|
label: 'collapse__header--collapsed',
|
||||||
|
@ -80,9 +80,11 @@ const getStyles = (
|
|||||||
boxShadow: isSelected
|
boxShadow: isSelected
|
||||||
? `inset 0 0 0 2px ${color}, inset 0 0 0 4px ${theme.colors.getContrastText(color)}`
|
? `inset 0 0 0 2px ${color}, inset 0 0 0 4px ${theme.colors.getContrastText(color)}`
|
||||||
: 'none',
|
: 'none',
|
||||||
transition: theme.transitions.create(['transform'], {
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
duration: theme.transitions.duration.short,
|
transition: theme.transitions.create(['transform'], {
|
||||||
}),
|
duration: theme.transitions.duration.short,
|
||||||
|
}),
|
||||||
|
},
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
transform: 'scale(1.1)',
|
transform: 'scale(1.1)',
|
||||||
},
|
},
|
||||||
|
@ -133,18 +133,22 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
}),
|
}),
|
||||||
mainButton: css({
|
mainButton: css({
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
transition: theme.transitions.create(['opacity'], {
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
duration: theme.transitions.duration.shortest,
|
transition: theme.transitions.create(['opacity'], {
|
||||||
easing: theme.transitions.easing.easeOut,
|
duration: theme.transitions.duration.shortest,
|
||||||
}),
|
easing: theme.transitions.easing.easeOut,
|
||||||
|
}),
|
||||||
|
},
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
}),
|
}),
|
||||||
mainButtonHide: css({
|
mainButtonHide: css({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transition: theme.transitions.create(['opacity', 'visibility'], {
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
duration: theme.transitions.duration.shortest,
|
transition: theme.transitions.create(['opacity', 'visibility'], {
|
||||||
easing: theme.transitions.easing.easeIn,
|
duration: theme.transitions.duration.shortest,
|
||||||
}),
|
easing: theme.transitions.easing.easeIn,
|
||||||
|
}),
|
||||||
|
},
|
||||||
visibility: 'hidden',
|
visibility: 'hidden',
|
||||||
zIndex: 0,
|
zIndex: 0,
|
||||||
}),
|
}),
|
||||||
@ -164,19 +168,23 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
transform: 'translateX(0)',
|
transform: 'translateX(0)',
|
||||||
transition: theme.transitions.create(['opacity', 'transform'], {
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
duration: theme.transitions.duration.shortest,
|
transition: theme.transitions.create(['opacity', 'transform'], {
|
||||||
easing: theme.transitions.easing.easeOut,
|
duration: theme.transitions.duration.shortest,
|
||||||
}),
|
easing: theme.transitions.easing.easeOut,
|
||||||
|
}),
|
||||||
|
},
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
}),
|
}),
|
||||||
confirmButtonHide: css({
|
confirmButtonHide: css({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transform: 'translateX(100%)',
|
transform: 'translateX(100%)',
|
||||||
transition: theme.transitions.create(['opacity', 'transform', 'visibility'], {
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
duration: theme.transitions.duration.shortest,
|
transition: theme.transitions.create(['opacity', 'transform', 'visibility'], {
|
||||||
easing: theme.transitions.easing.easeIn,
|
duration: theme.transitions.duration.shortest,
|
||||||
}),
|
easing: theme.transitions.easing.easeIn,
|
||||||
|
}),
|
||||||
|
},
|
||||||
visibility: 'hidden',
|
visibility: 'hidden',
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
@ -65,7 +65,9 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
right: 0,
|
right: 0,
|
||||||
transition: theme.transitions.create('opacity'),
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
transition: theme.transitions.create('opacity'),
|
||||||
|
},
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
}),
|
}),
|
||||||
scrollTopIndicator: css({
|
scrollTopIndicator: css({
|
||||||
|
@ -13,7 +13,10 @@ import {
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { CSSTransition } from 'react-transition-group';
|
import { CSSTransition } from 'react-transition-group';
|
||||||
|
|
||||||
import { ReactUtils, handleReducedMotion } from '../../utils';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
|
||||||
|
import { useStyles2 } from '../../themes';
|
||||||
|
import { ReactUtils } from '../../utils';
|
||||||
import { getPlacement } from '../../utils/tooltipUtils';
|
import { getPlacement } from '../../utils/tooltipUtils';
|
||||||
import { Portal } from '../Portal/Portal';
|
import { Portal } from '../Portal/Portal';
|
||||||
import { TooltipPlacement } from '../Tooltip/types';
|
import { TooltipPlacement } from '../Tooltip/types';
|
||||||
@ -63,7 +66,7 @@ export const Dropdown = React.memo(({ children, overlay, placement, offset, onVi
|
|||||||
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, click]);
|
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, click]);
|
||||||
|
|
||||||
const animationDuration = 150;
|
const animationDuration = 150;
|
||||||
const animationStyles = getStyles(animationDuration);
|
const animationStyles = useStyles2(getStyles, animationDuration);
|
||||||
|
|
||||||
const onOverlayClicked = () => {
|
const onOverlayClicked = () => {
|
||||||
setShow(false);
|
setShow(false);
|
||||||
@ -109,22 +112,22 @@ export const Dropdown = React.memo(({ children, overlay, placement, offset, onVi
|
|||||||
|
|
||||||
Dropdown.displayName = 'Dropdown';
|
Dropdown.displayName = 'Dropdown';
|
||||||
|
|
||||||
const getStyles = (duration: number) => {
|
const getStyles = (theme: GrafanaTheme2, duration: number) => {
|
||||||
return {
|
return {
|
||||||
appear: css({
|
appear: css({
|
||||||
opacity: '0',
|
opacity: '0',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
transformOrigin: 'top',
|
transformOrigin: 'top',
|
||||||
...handleReducedMotion({
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
transform: 'scaleY(0.5)',
|
transform: 'scaleY(0.5)',
|
||||||
}),
|
},
|
||||||
}),
|
}),
|
||||||
appearActive: css({
|
appearActive: css({
|
||||||
opacity: '1',
|
opacity: '1',
|
||||||
...handleReducedMotion({
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
transform: 'scaleY(1)',
|
transform: 'scaleY(1)',
|
||||||
transition: `transform ${duration}ms cubic-bezier(0.2, 0, 0.2, 1), opacity ${duration}ms cubic-bezier(0.2, 0, 0.2, 1)`,
|
transition: `transform ${duration}ms cubic-bezier(0.2, 0, 0.2, 1), opacity ${duration}ms cubic-bezier(0.2, 0, 0.2, 1)`,
|
||||||
}),
|
},
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -7,7 +7,6 @@ import { useStyles2 } from '../../themes';
|
|||||||
import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins';
|
import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins';
|
||||||
import { ComponentSize } from '../../types';
|
import { ComponentSize } from '../../types';
|
||||||
import { IconName, IconSize, IconType } from '../../types/icon';
|
import { IconName, IconSize, IconType } from '../../types/icon';
|
||||||
import { handleReducedMotion } from '../../utils/handleReducedMotion';
|
|
||||||
import { Icon } from '../Icon/Icon';
|
import { Icon } from '../Icon/Icon';
|
||||||
import { getSvgSize } from '../Icon/utils';
|
import { getSvgSize } from '../Icon/utils';
|
||||||
import { TooltipPlacement, PopoverContent, Tooltip } from '../Tooltip';
|
import { TooltipPlacement, PopoverContent, Tooltip } from '../Tooltip';
|
||||||
@ -144,11 +143,11 @@ const getStyles = (theme: GrafanaTheme2, size: IconSize, variant: IconButtonVari
|
|||||||
height: `${hoverSize}px`,
|
height: `${hoverSize}px`,
|
||||||
borderRadius: theme.shape.radius.default,
|
borderRadius: theme.shape.radius.default,
|
||||||
content: '""',
|
content: '""',
|
||||||
...handleReducedMotion({
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
transitionDuration: '0.2s',
|
transitionDuration: '0.2s',
|
||||||
transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
transitionProperty: 'opacity',
|
transitionProperty: 'opacity',
|
||||||
}),
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
'&:focus, &:focus-visible': getFocusStyles(theme),
|
'&:focus, &:focus-visible': getFocusStyles(theme),
|
||||||
|
@ -4,7 +4,6 @@ import React, { CSSProperties } from 'react';
|
|||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
|
||||||
import { useStyles2 } from '../../themes';
|
import { useStyles2 } from '../../themes';
|
||||||
import { handleReducedMotion } from '../../utils/handleReducedMotion';
|
|
||||||
|
|
||||||
export interface LoadingBarProps {
|
export interface LoadingBarProps {
|
||||||
width: number;
|
width: number;
|
||||||
@ -33,7 +32,7 @@ export function LoadingBar({ width, delay = DEFAULT_ANIMATION_DELAY, ariaLabel =
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStyles = (_theme: GrafanaTheme2, delay: number, duration: number) => {
|
const getStyles = (theme: GrafanaTheme2, delay: number, duration: number) => {
|
||||||
const animation = keyframes({
|
const animation = keyframes({
|
||||||
'0%': {
|
'0%': {
|
||||||
transform: 'translateX(-100%)',
|
transform: 'translateX(-100%)',
|
||||||
@ -50,20 +49,23 @@ const getStyles = (_theme: GrafanaTheme2, delay: number, duration: number) => {
|
|||||||
height: 1,
|
height: 1,
|
||||||
background: 'linear-gradient(90deg, rgba(110, 159, 255, 0) 0%, #6E9FFF 80.75%, rgba(110, 159, 255, 0) 100%)',
|
background: 'linear-gradient(90deg, rgba(110, 159, 255, 0) 0%, #6E9FFF 80.75%, rgba(110, 159, 255, 0) 100%)',
|
||||||
transform: 'translateX(-100%)',
|
transform: 'translateX(-100%)',
|
||||||
animationName: animation,
|
|
||||||
// an initial delay to prevent the loader from showing if the response is faster than the delay
|
|
||||||
animationDelay: `${delay}ms`,
|
|
||||||
animationTimingFunction: 'linear',
|
|
||||||
animationIterationCount: 'infinite',
|
|
||||||
willChange: 'transform',
|
willChange: 'transform',
|
||||||
...handleReducedMotion(
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
{
|
animationName: animation,
|
||||||
animationDuration: `${duration}ms`,
|
// an initial delay to prevent the loader from showing if the response is faster than the delay
|
||||||
},
|
animationDelay: `${delay}ms`,
|
||||||
{
|
animationTimingFunction: 'linear',
|
||||||
animationDuration: `${4 * duration}ms`,
|
animationIterationCount: 'infinite',
|
||||||
}
|
animationDuration: `${duration}ms`,
|
||||||
),
|
},
|
||||||
|
[theme.transitions.handleMotion('reduce')]: {
|
||||||
|
animationName: animation,
|
||||||
|
// an initial delay to prevent the loader from showing if the response is faster than the delay
|
||||||
|
animationDelay: `${delay}ms`,
|
||||||
|
animationTimingFunction: 'linear',
|
||||||
|
animationIterationCount: 'infinite',
|
||||||
|
animationDuration: `${4 * duration}ms`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -66,7 +66,9 @@ function getStyles(theme: GrafanaTheme2) {
|
|||||||
return {
|
return {
|
||||||
container: css({
|
container: css({
|
||||||
label: 'hover-container-widget',
|
label: 'hover-container-widget',
|
||||||
transition: `all .1s linear`,
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
transition: `all .1s linear`,
|
||||||
|
},
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
|
@ -263,7 +263,9 @@ export function useTableStyles(theme: GrafanaTheme2, cellHeightOption: TableCell
|
|||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
background: resizerColor,
|
background: resizerColor,
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transition: 'opacity 0.2s ease-in-out',
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
transition: 'opacity 0.2s ease-in-out',
|
||||||
|
},
|
||||||
width: '8px',
|
width: '8px',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
@ -152,9 +152,11 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
fontWeight: theme.typography.fontWeightMedium,
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
border: `1px solid ${theme.colors.secondary.border}`,
|
border: `1px solid ${theme.colors.secondary.border}`,
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
transition: theme.transitions.create(['background', 'box-shadow', 'border-color', 'color'], {
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
duration: theme.transitions.duration.short,
|
transition: theme.transitions.create(['background', 'box-shadow', 'border-color', 'color'], {
|
||||||
}),
|
duration: theme.transitions.duration.short,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
'&:focus, &:focus-visible': {
|
'&:focus, &:focus-visible': {
|
||||||
...getFocusStyles(theme),
|
...getFocusStyles(theme),
|
||||||
|
@ -36,8 +36,10 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
display: 'block',
|
display: 'block',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
transition:
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
'color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), background 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), padding 0.15s cubic-bezier(0.645, 0.045, 0.355, 1)',
|
transition:
|
||||||
|
'color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), background 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), padding 0.15s cubic-bezier(0.645, 0.045, 0.355, 1)',
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
typeaheadItemSelected: css({
|
typeaheadItemSelected: css({
|
||||||
|
@ -23,7 +23,7 @@ export function FadeTransition(props: Props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStyles = (_theme: GrafanaTheme2, duration: number) => ({
|
const getStyles = (theme: GrafanaTheme2, duration: number) => ({
|
||||||
enter: css({
|
enter: css({
|
||||||
label: 'enter',
|
label: 'enter',
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
@ -31,7 +31,9 @@ const getStyles = (_theme: GrafanaTheme2, duration: number) => ({
|
|||||||
enterActive: css({
|
enterActive: css({
|
||||||
label: 'enterActive',
|
label: 'enterActive',
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
transition: `opacity ${duration}ms ease-out`,
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
transition: `opacity ${duration}ms ease-out`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
exit: css({
|
exit: css({
|
||||||
label: 'exit',
|
label: 'exit',
|
||||||
@ -40,6 +42,8 @@ const getStyles = (_theme: GrafanaTheme2, duration: number) => ({
|
|||||||
exitActive: css({
|
exitActive: css({
|
||||||
label: 'exitActive',
|
label: 'exitActive',
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transition: `opacity ${duration}ms ease-out`,
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
transition: `opacity ${duration}ms ease-out`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -26,7 +26,7 @@ export function SlideOutTransition(props: Props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStyles = (_theme: GrafanaTheme2, duration: number, measurement: 'width' | 'height', size: number) => ({
|
const getStyles = (theme: GrafanaTheme2, duration: number, measurement: 'width' | 'height', size: number) => ({
|
||||||
enter: css({
|
enter: css({
|
||||||
label: 'enter',
|
label: 'enter',
|
||||||
[`${measurement}`]: 0,
|
[`${measurement}`]: 0,
|
||||||
@ -36,7 +36,12 @@ const getStyles = (_theme: GrafanaTheme2, duration: number, measurement: 'width'
|
|||||||
label: 'enterActive',
|
label: 'enterActive',
|
||||||
[`${measurement}`]: `${size}px`,
|
[`${measurement}`]: `${size}px`,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
transition: `opacity ${duration}ms ease-out, ${measurement} ${duration}ms ease-out`,
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
|
transition: `opacity ${duration}ms ease-out, ${measurement} ${duration}ms ease-out`,
|
||||||
|
},
|
||||||
|
[theme.transitions.handleMotion('reduce')]: {
|
||||||
|
transition: `opacity ${duration}ms ease-out`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
exit: css({
|
exit: css({
|
||||||
label: 'exit',
|
label: 'exit',
|
||||||
@ -47,6 +52,11 @@ const getStyles = (_theme: GrafanaTheme2, duration: number, measurement: 'width'
|
|||||||
label: 'exitActive',
|
label: 'exitActive',
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
[`${measurement}`]: 0,
|
[`${measurement}`]: 0,
|
||||||
transition: `opacity ${duration}ms ease-out, ${measurement} ${duration}ms ease-out`,
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
|
transition: `opacity ${duration}ms ease-out, ${measurement} ${duration}ms ease-out`,
|
||||||
|
},
|
||||||
|
[theme.transitions.handleMotion('reduce')]: {
|
||||||
|
transition: `opacity ${duration}ms ease-out`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
import { CSSInterpolation } from '@emotion/css';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param styles - Styles to apply when no `prefers-reduced-motion` preference is set.
|
|
||||||
* @param reducedMotionStyles - Styles to apply when `prefers-reduced-motion` is enabled.
|
|
||||||
* Applies one of `styles` or `reducedMotionStyles` depending on a users `prefers-reduced-motion` setting. Omitting `reducedMotionStyles` entirely will result in no styles being applied when `prefers-reduced-motion` is enabled. In most cases this is a reasonable default.
|
|
||||||
*/
|
|
||||||
export const handleReducedMotion = (styles: CSSInterpolation, reducedMotionStyles?: CSSInterpolation) => {
|
|
||||||
const result: Record<string, CSSInterpolation> = {
|
|
||||||
'@media (prefers-reduced-motion: no-preference)': styles,
|
|
||||||
};
|
|
||||||
if (reducedMotionStyles) {
|
|
||||||
result['@media (prefers-reduced-motion: reduce)'] = reducedMotionStyles;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
@ -19,6 +19,5 @@ export { createLogger } from './logger';
|
|||||||
export { attachDebugger } from './debug';
|
export { attachDebugger } from './debug';
|
||||||
export * from './nodeGraph';
|
export * from './nodeGraph';
|
||||||
export { fuzzyMatch } from './fuzzy';
|
export { fuzzyMatch } from './fuzzy';
|
||||||
export { handleReducedMotion } from './handleReducedMotion';
|
|
||||||
|
|
||||||
export { ReactUtils };
|
export { ReactUtils };
|
||||||
|
@ -37,7 +37,9 @@ export function buildTooltipTheme(
|
|||||||
color: tooltipText,
|
color: tooltipText,
|
||||||
fontSize: theme.typography.bodySmall.fontSize,
|
fontSize: theme.typography.bodySmall.fontSize,
|
||||||
padding: theme.spacing(tooltipPadding.topBottom, tooltipPadding.rightLeft),
|
padding: theme.spacing(tooltipPadding.topBottom, tooltipPadding.rightLeft),
|
||||||
transition: 'opacity 0.3s',
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
transition: 'opacity 0.3s',
|
||||||
|
},
|
||||||
zIndex: theme.zIndex.tooltip,
|
zIndex: theme.zIndex.tooltip,
|
||||||
maxWidth: '400px',
|
maxWidth: '400px',
|
||||||
overflowWrap: 'break-word',
|
overflowWrap: 'break-word',
|
||||||
|
@ -6,7 +6,7 @@ import React, { useRef } from 'react';
|
|||||||
import CSSTransition from 'react-transition-group/CSSTransition';
|
import CSSTransition from 'react-transition-group/CSSTransition';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { handleReducedMotion, useStyles2, useTheme2 } from '@grafana/ui';
|
import { useStyles2, useTheme2 } from '@grafana/ui';
|
||||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||||
import { KioskMode } from 'app/types';
|
import { KioskMode } from 'app/types';
|
||||||
|
|
||||||
@ -125,10 +125,10 @@ const getStyles = (theme: GrafanaTheme2, searchBarHidden?: boolean) => {
|
|||||||
|
|
||||||
const getAnimStyles = (theme: GrafanaTheme2, animationDuration: number) => {
|
const getAnimStyles = (theme: GrafanaTheme2, animationDuration: number) => {
|
||||||
const commonTransition = {
|
const commonTransition = {
|
||||||
...handleReducedMotion({
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
transitionDuration: `${animationDuration}ms`,
|
transitionDuration: `${animationDuration}ms`,
|
||||||
transitionTimingFunction: theme.transitions.easing.easeInOut,
|
transitionTimingFunction: theme.transitions.easing.easeInOut,
|
||||||
}),
|
},
|
||||||
[theme.breakpoints.down('md')]: {
|
[theme.breakpoints.down('md')]: {
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
},
|
},
|
||||||
|
@ -33,6 +33,18 @@ const fadeIn = keyframes({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const pulse = keyframes({
|
||||||
|
'0%': {
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
'50%': {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const bounce = keyframes({
|
const bounce = keyframes({
|
||||||
'from, to': {
|
'from, to': {
|
||||||
transform: 'translateY(0px)',
|
transform: 'translateY(0px)',
|
||||||
@ -70,25 +82,37 @@ const squash = keyframes({
|
|||||||
const getStyles = (theme: GrafanaTheme2) => ({
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
container: css({
|
container: css({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
animationName: fadeIn,
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
animationIterationCount: 1,
|
animationName: fadeIn,
|
||||||
animationDuration: '0.9s',
|
animationIterationCount: 1,
|
||||||
animationDelay: '0.5s',
|
animationDuration: '0.9s',
|
||||||
animationFillMode: 'forwards',
|
animationDelay: '0.5s',
|
||||||
|
animationFillMode: 'forwards',
|
||||||
|
},
|
||||||
|
[theme.transitions.handleMotion('reduce')]: {
|
||||||
|
animationName: pulse,
|
||||||
|
animationIterationCount: 'infinite',
|
||||||
|
animationDuration: '4s',
|
||||||
|
animationDelay: '0.5s',
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
bounce: css({
|
bounce: css({
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
animationName: bounce,
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
animationDuration: '0.9s',
|
animationName: bounce,
|
||||||
animationIterationCount: 'infinite',
|
animationDuration: '0.9s',
|
||||||
|
animationIterationCount: 'infinite',
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
logo: css({
|
logo: css({
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
animationName: squash,
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
animationDuration: '0.9s',
|
animationName: squash,
|
||||||
animationIterationCount: 'infinite',
|
animationDuration: '0.9s',
|
||||||
|
animationIterationCount: 'infinite',
|
||||||
|
},
|
||||||
width: '60px',
|
width: '60px',
|
||||||
height: '60px',
|
height: '60px',
|
||||||
}),
|
}),
|
||||||
|
@ -2,7 +2,7 @@ import { cx, css, keyframes } from '@emotion/css';
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { handleReducedMotion, useStyles2 } from '@grafana/ui';
|
import { useStyles2 } from '@grafana/ui';
|
||||||
|
|
||||||
import { Branding } from '../Branding/Branding';
|
import { Branding } from '../Branding/Branding';
|
||||||
import { BrandingSettings } from '../Branding/types';
|
import { BrandingSettings } from '../Branding/types';
|
||||||
@ -148,7 +148,9 @@ export const getLoginStyles = (theme: GrafanaTheme2) => {
|
|||||||
borderRadius: theme.shape.radius.default,
|
borderRadius: theme.shape.radius.default,
|
||||||
padding: theme.spacing(2, 0),
|
padding: theme.spacing(2, 0),
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transition: 'opacity 0.5s ease-in-out',
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
transition: 'opacity 0.5s ease-in-out',
|
||||||
|
},
|
||||||
|
|
||||||
[theme.breakpoints.up('sm')]: {
|
[theme.breakpoints.up('sm')]: {
|
||||||
minHeight: theme.spacing(40),
|
minHeight: theme.spacing(40),
|
||||||
@ -171,12 +173,14 @@ export const getLoginStyles = (theme: GrafanaTheme2) => {
|
|||||||
maxWidth: 415,
|
maxWidth: 415,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
transform: 'translate(0px, 0px)',
|
transform: 'translate(0px, 0px)',
|
||||||
transition: '0.25s ease',
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
|
transition: '0.25s ease',
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
enterAnimation: css({
|
enterAnimation: css({
|
||||||
...handleReducedMotion({
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
animation: `${flyInAnimation} ease-out 0.2s`,
|
animation: `${flyInAnimation} ease-out 0.2s`,
|
||||||
}),
|
},
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css, cx, keyframes } from '@emotion/css';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { useFormContext, Controller } from 'react-hook-form';
|
import { useFormContext, Controller } from 'react-hook-form';
|
||||||
|
|
||||||
@ -149,6 +149,15 @@ function LinkToContactPoints() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rotation = keyframes({
|
||||||
|
from: {
|
||||||
|
transform: 'rotate(720deg)',
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
transform: 'rotate(0deg)',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => ({
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
contactPointsSelector: css({
|
contactPointsSelector: css({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -172,14 +181,11 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
}),
|
}),
|
||||||
loading: css({
|
loading: css({
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
animation: 'rotation 2s infinite linear',
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
'@keyframes rotation': {
|
animation: `${rotation} 2s infinite linear`,
|
||||||
from: {
|
},
|
||||||
transform: 'rotate(720deg)',
|
[theme.transitions.handleMotion('reduce')]: {
|
||||||
},
|
animation: `${rotation} 6s infinite linear`,
|
||||||
to: {
|
|
||||||
transform: 'rotate(0deg)',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
warn: css({
|
warn: css({
|
||||||
|
@ -122,6 +122,8 @@ export const droneFrontItem: CanvasElementItem = {
|
|||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => ({
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
droneFront: css({
|
droneFront: css({
|
||||||
|
// TODO: figure out what styles to apply when prefers-reduced-motion is set
|
||||||
|
// eslint-disable-next-line @grafana/no-unreduced-motion
|
||||||
transition: 'transform 0.4s',
|
transition: 'transform 0.4s',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -121,6 +121,8 @@ export const droneSideItem: CanvasElementItem = {
|
|||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => ({
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
droneSide: css({
|
droneSide: css({
|
||||||
|
// TODO: figure out what styles to apply when prefers-reduced-motion is set
|
||||||
|
// eslint-disable-next-line @grafana/no-unreduced-motion
|
||||||
transition: 'transform 0.4s',
|
transition: 'transform 0.4s',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -180,9 +180,13 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
propellerCW: css({
|
propellerCW: css({
|
||||||
|
// TODO: figure out what styles to apply when prefers-reduced-motion is set
|
||||||
|
// eslint-disable-next-line @grafana/no-unreduced-motion
|
||||||
animationDirection: 'normal',
|
animationDirection: 'normal',
|
||||||
}),
|
}),
|
||||||
propellerCCW: css({
|
propellerCCW: css({
|
||||||
|
// TODO: figure out what styles to apply when prefers-reduced-motion is set
|
||||||
|
// eslint-disable-next-line @grafana/no-unreduced-motion
|
||||||
animationDirection: 'reverse',
|
animationDirection: 'reverse',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -173,7 +173,9 @@ export const getServerStyles = (data: ServerData | undefined) => (theme: Grafana
|
|||||||
fill: data?.statusColor ?? 'transparent',
|
fill: data?.statusColor ?? 'transparent',
|
||||||
}),
|
}),
|
||||||
circle: css({
|
circle: css({
|
||||||
animation: `blink ${data?.blinkRate ? 1 / data.blinkRate : 0}s infinite step-end`,
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
animation: `blink ${data?.blinkRate ? 1 / data.blinkRate : 0}s infinite step-end`,
|
||||||
|
},
|
||||||
fill: data?.bulbColor,
|
fill: data?.bulbColor,
|
||||||
stroke: 'none',
|
stroke: 'none',
|
||||||
}),
|
}),
|
||||||
|
@ -90,7 +90,9 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
fontSize: theme.typography.fontSize,
|
fontSize: theme.typography.fontSize,
|
||||||
fontWeight: theme.typography.fontWeightMedium,
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
paddingLeft: `${theme.spacing(1)}`,
|
paddingLeft: `${theme.spacing(1)}`,
|
||||||
transition: 'background-color 0.1s ease-in-out',
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
transition: 'background-color 0.1s ease-in-out',
|
||||||
|
},
|
||||||
cursor: 'move',
|
cursor: 'move',
|
||||||
|
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
@ -102,7 +104,9 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
outline: '2px dotted transparent',
|
outline: '2px dotted transparent',
|
||||||
outlineOffset: '2px',
|
outlineOffset: '2px',
|
||||||
boxShadow: '0 0 0 2px black, 0 0 0px 4px #1f60c4',
|
boxShadow: '0 0 0 2px black, 0 0 0px 4px #1f60c4',
|
||||||
animation: `${pulsate} 2s ease infinite`,
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
animation: `${pulsate} 2s ease infinite`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -50,7 +50,9 @@ export const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
opacity: '0%',
|
opacity: '0%',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
animation: `${invisibleToVisible} 0s step-end ${slowStartThreshold} 1 normal forwards`,
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
animation: `${invisibleToVisible} 0s step-end ${slowStartThreshold} 1 normal forwards`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
dashboardLoadingText: css({
|
dashboardLoadingText: css({
|
||||||
fontSize: theme.typography.h4.fontSize,
|
fontSize: theme.typography.h4.fontSize,
|
||||||
|
@ -68,6 +68,8 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
}),
|
}),
|
||||||
drawerActive: css({
|
drawerActive: css({
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
animation: `0.5s ease-out ${drawerSlide(theme)}`,
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
|
animation: `0.5s ease-out ${drawerSlide(theme)}`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -567,7 +567,9 @@ function getStyles(theme: GrafanaTheme2, height: number, width: number) {
|
|||||||
}),
|
}),
|
||||||
rzHandle: css({
|
rzHandle: css({
|
||||||
background: theme.colors.secondary.main,
|
background: theme.colors.secondary.main,
|
||||||
transition: '0.3s background ease-in-out',
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
|
transition: '0.3s background ease-in-out',
|
||||||
|
},
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
height: '50% !important',
|
height: '50% !important',
|
||||||
width: `${theme.spacing(1)} !important`,
|
width: `${theme.spacing(1)} !important`,
|
||||||
|
@ -138,9 +138,11 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
padding: theme.spacing(1),
|
padding: theme.spacing(1),
|
||||||
width: '100%',
|
width: '100%',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
transition: theme.transitions.create(['background'], {
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
duration: theme.transitions.duration.short,
|
transition: theme.transitions.create(['background'], {
|
||||||
}),
|
duration: theme.transitions.duration.short,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
background: theme.colors.emphasize(theme.colors.background.secondary, 0.03),
|
background: theme.colors.emphasize(theme.colors.background.secondary, 0.03),
|
||||||
|
@ -100,9 +100,11 @@ export const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
background: theme.colors.background.secondary,
|
background: theme.colors.background.secondary,
|
||||||
borderRadius: theme.shape.radius.default,
|
borderRadius: theme.shape.radius.default,
|
||||||
padding: theme.spacing(3),
|
padding: theme.spacing(3),
|
||||||
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
|
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||||
duration: theme.transitions.duration.short,
|
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
|
||||||
}),
|
duration: theme.transitions.duration.short,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
background: theme.colors.emphasize(theme.colors.background.secondary, 0.03),
|
background: theme.colors.emphasize(theme.colors.background.secondary, 0.03),
|
||||||
|
@ -307,7 +307,9 @@ const getExemplarMarkerStyles = (theme: GrafanaTheme2) => {
|
|||||||
marble: css({
|
marble: css({
|
||||||
display: 'block',
|
display: 'block',
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
transition: 'transform 0.15s ease-out',
|
[theme.transitions.handleMotion('no-preference')]: {
|
||||||
|
transition: 'transform 0.15s ease-out',
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
activeMarble: css({
|
activeMarble: css({
|
||||||
transform: 'scale(1.3)',
|
transform: 'scale(1.3)',
|
||||||
|
Loading…
Reference in New Issue
Block a user