Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions static/app/components/core/principles/motion/motion.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,21 @@ const theme = useTheme();
theme.motion.smooth.moderate;
```

If you need to use the tokens with [Framer Motion](https://motion.dev) you can access the Bézier curve control points values and durations directly.

```jsx
const theme = useTheme();

<motion.div
animate={{opacity: 0}}
transition={{
type: 'tween',
ease: theme.motionControlPoints.enter,
duration: theme.motionDurations.slow,
}}
></motion.div>;
```

## Easing

The easing curve of an animation drastically changes our perception of it. These easing tokens have been chosen to provide snappy, natural motion to interactions.
Expand Down
10 changes: 6 additions & 4 deletions static/app/components/slideOverPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useEffect} from 'react';
import isPropValid from '@emotion/is-prop-valid';
import {css} from '@emotion/react';
import {css, useTheme} from '@emotion/react';
import styled from '@emotion/styled';
import {motion, type Transition} from 'framer-motion';

Expand Down Expand Up @@ -49,6 +49,8 @@ function SlideOverPanel({
panelWidth,
ref,
}: SlideOverPanelProps) {
const theme = useTheme();

useEffect(() => {
if (!collapsed && onOpen) {
onOpen();
Expand All @@ -69,9 +71,9 @@ function SlideOverPanel({
exit={collapsedStyle}
slidePosition={slidePosition}
transition={{
type: 'spring',
stiffness: 1000,
damping: 50,
type: 'tween',
ease: theme.motionControlPoints.enter,
Comment on lines +74 to +75
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of a bummer we went from physics controller moment to a fixed linear bezier motion curve. Will have to play with it, but my experience with simple bezier curves is that it's hard to get it to feel nice

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷🏻‍♂️ you tell me, I'm opting to use the motion tokens that we have. If that's not appropriate for panels I'm totally fine to move it back, as long as they can run at high FPS in busy situations

duration: theme.motionDurations.slow,
...transitionProps,
}}
role="complementary"
Expand Down
42 changes: 33 additions & 9 deletions static/app/utils/theme/theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -252,20 +252,42 @@ const generateTokens = (colors: Colors) => ({
},
});

const generateMotion = () => {
type Curve = 'smooth' | 'snap' | 'enter' | 'exit';
type ControlPoints = [number, number, number, number];

const BEZIER_CONTROL_POINTS: Record<Curve, ControlPoints> = {
smooth: [0.72, 0, 0.16, 1],
snap: [0.8, -0.4, 0.5, 1],
enter: [0.24, 1, 0.32, 1],
exit: [0.64, 0, 0.8, 0],
};

function formatBezierCurve(points: ControlPoints): string {
return `cubic-bezier(${points.join(', ')})`;
}

type AnimationDuration = 'fast' | 'moderate' | 'slow';

const MOTION_DURATIONS: Record<AnimationDuration, number> = {
fast: 0.12,
moderate: 0.16,
slow: 0.24,
};

const withDuration = (easing: string) => {
return {
smooth: withDuration('cubic-bezier(0.72, 0, 0.16, 1)'),
snap: withDuration('cubic-bezier(0.8, -0.4, 0.5, 1)'),
enter: withDuration('cubic-bezier(0.24, 1, 0.32, 1)'),
exit: withDuration('cubic-bezier(0.64, 0, 0.8, 0)'),
fast: `${MOTION_DURATIONS.fast}s ${easing}`,
moderate: `${MOTION_DURATIONS.moderate}s ${easing}`,
slow: `${MOTION_DURATIONS.slow}s ${easing}`,
};
};

const withDuration = (easing: string) => {
const generateMotion = () => {
return {
fast: `120ms ${easing}`,
moderate: `160ms ${easing}`,
slow: `240ms ${easing}`,
smooth: withDuration(formatBezierCurve(BEZIER_CONTROL_POINTS.smooth)),
snap: withDuration(formatBezierCurve(BEZIER_CONTROL_POINTS.snap)),
enter: withDuration(formatBezierCurve(BEZIER_CONTROL_POINTS.enter)),
exit: withDuration(formatBezierCurve(BEZIER_CONTROL_POINTS.exit)),
};
};

Expand Down Expand Up @@ -1152,6 +1174,8 @@ const commonTheme = {

space,
motion: generateMotion(),
motionControlPoints: BEZIER_CONTROL_POINTS,
motionDurations: MOTION_DURATIONS,
Comment on lines 1176 to +1178
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could these be merged into theme.motion ? Could be a follow-up but I don’t think we should have too many top-level things on the theme, makes it harder to discover imo. Same as we have all tokens now grouped in theme.tokens

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TkDodo yes definitely! I'm waiting for #102531 and then I'll work on top of that 👍🏻


// Icons
iconSizes,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
type DashboardFilters,
type Widget,
} from 'sentry/views/dashboards/types';
import {animationTransitionSettings} from 'sentry/views/dashboards/widgetBuilder/components/common/animationSettings';
import {
DEFAULT_WIDGET_DRAG_POSITIONING,
DRAGGABLE_PREVIEW_HEIGHT_PX,
Expand All @@ -43,6 +42,7 @@ import {
useWidgetBuilderContext,
WidgetBuilderProvider,
} from 'sentry/views/dashboards/widgetBuilder/contexts/widgetBuilderContext';
import {useWidgetBuilderTransitionSettings} from 'sentry/views/dashboards/widgetBuilder/hooks/useWidgetBuilderAnimationSettings';
import {DashboardsMEPProvider} from 'sentry/views/dashboards/widgetCard/dashboardsMEPContext';
import {TraceItemAttributeProvider} from 'sentry/views/explore/contexts/traceItemAttributeContext';
import {isLogsEnabled} from 'sentry/views/explore/logs/isLogsEnabled';
Expand Down Expand Up @@ -269,6 +269,7 @@ export function WidgetPreviewContainer({
const organization = useOrganization();
const location = useLocation();
const theme = useTheme();
const transitionSettings = useWidgetBuilderTransitionSettings();
const isSmallScreen = useMedia(`(max-width: ${theme.breakpoints.sm})`);
// if small screen and draggable, enable dragging
const isDragEnabled = isSmallScreen && isDraggable;
Expand Down Expand Up @@ -334,7 +335,7 @@ export function WidgetPreviewContainer({
initial: {opacity: 0, x: '100%', y: 0},
animate: {opacity: 1, x: 0, y: 0},
exit: {opacity: 0, x: '100%', y: 0},
transition: animationTransitionSettings,
transition: transitionSettings,
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
type DashboardFilters,
type Widget,
} from 'sentry/views/dashboards/types';
import {animationTransitionSettings} from 'sentry/views/dashboards/widgetBuilder/components/common/animationSettings';
import WidgetBuilderDatasetSelector from 'sentry/views/dashboards/widgetBuilder/components/datasetSelector';
import WidgetBuilderFilterBar from 'sentry/views/dashboards/widgetBuilder/components/filtersBar';
import WidgetBuilderGroupBySelector from 'sentry/views/dashboards/widgetBuilder/components/groupBySelector';
Expand All @@ -49,6 +48,7 @@ import useDashboardWidgetSource from 'sentry/views/dashboards/widgetBuilder/hook
import {useDisableTransactionWidget} from 'sentry/views/dashboards/widgetBuilder/hooks/useDisableTransactionWidget';
import useIsEditingWidget from 'sentry/views/dashboards/widgetBuilder/hooks/useIsEditingWidget';
import {useSegmentSpanWidgetState} from 'sentry/views/dashboards/widgetBuilder/hooks/useSegmentSpanWidgetState';
import {useWidgetBuilderTransitionSettings} from 'sentry/views/dashboards/widgetBuilder/hooks/useWidgetBuilderAnimationSettings';
import {convertBuilderStateToWidget} from 'sentry/views/dashboards/widgetBuilder/utils/convertBuilderStateToWidget';
import {convertWidgetToBuilderStateParams} from 'sentry/views/dashboards/widgetBuilder/utils/convertWidgetToBuilderStateParams';
import {getTopNConvertedDefaultWidgets} from 'sentry/views/dashboards/widgetLibrary/data';
Expand Down Expand Up @@ -90,6 +90,7 @@ function WidgetBuilderSlideout({
const [error, setError] = useState<Record<string, any>>({});
const theme = useTheme();
const isEditing = useIsEditingWidget();
const transitionSettings = useWidgetBuilderTransitionSettings();
const source = useDashboardWidgetSource();
const {cacheBuilderState} = useCacheBuilderState();
const {setSegmentSpanBuilderState} = useSegmentSpanWidgetState();
Expand Down Expand Up @@ -208,7 +209,7 @@ function WidgetBuilderSlideout({
collapsed={!isOpen}
slidePosition="left"
data-test-id="widget-slideout"
transitionProps={animationTransitionSettings}
transitionProps={transitionSettings}
>
<SlideoutHeaderWrapper>
<Breadcrumbs crumbs={breadcrumbs} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {useTheme} from '@emotion/react';
import type {Transition} from 'framer-motion';

export function useWidgetBuilderTransitionSettings(): Transition {
const theme = useTheme();

return {
type: 'tween',
ease: theme.motionControlPoints.snap,
duration: 0.5, // TODO: Introduce a slower value
};
}
Loading