Merge pull request #30626 from backstage/bui-motion-to-gsap

BUI - Move from motion to gsap to ensure React 17 support
This commit is contained in:
Charles de Dreuille
2025-07-23 13:42:45 +01:00
committed by GitHub
5 changed files with 52 additions and 69 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/ui': patch
---
We are motion away from `motion` to use `gsap` instead to make Backstage UI backward compatible with React 17.
+3 -2
View File
@@ -64,7 +64,7 @@ export type JsonValue = JsonObject | JsonArray | JsonPrimitive;
// @public
export type Observable<T> = {
[Symbol.observable](): Observable<T>;
subscribe(observer: Observer<T>): Subscription;
subscribe(observer: Observer_2<T>): Subscription;
subscribe(
onNext?: (value: T) => void,
onError?: (error: Error) => void,
@@ -73,11 +73,12 @@ export type Observable<T> = {
};
// @public
export type Observer<T> = {
type Observer_2<T> = {
next?(value: T): void;
error?(error: Error): void;
complete?(): void;
};
export { Observer_2 as Observer };
// @public
export type Subscription = {
+2 -1
View File
@@ -42,10 +42,11 @@
},
"dependencies": {
"@base-ui-components/react": "1.0.0-alpha.7",
"@gsap/react": "^2.1.2",
"@remixicon/react": "^4.6.0",
"@tanstack/react-table": "^8.21.3",
"clsx": "^2.1.1",
"motion": "^12.20.1",
"gsap": "^3.13.0",
"react-aria-components": "^1.10.1"
},
"devDependencies": {
@@ -22,8 +22,12 @@ import type { HeaderToolbarProps } from './types';
import { ButtonIcon } from '../ButtonIcon';
import { Menu } from '../Menu';
import { Text } from '../Text';
import { motion, useScroll, useTransform } from 'motion/react';
import { useNavigate, useHref } from 'react-router-dom';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { useGSAP } from '@gsap/react';
gsap.registerPlugin(ScrollTrigger);
/**
* A component that renders a toolbar.
@@ -42,13 +46,12 @@ export const HeaderToolbar = (props: HeaderToolbarProps) => {
} = props;
const { classNames } = useStyles('Header');
let navigate = useNavigate();
const { scrollY } = useScroll();
const breadcrumbOpacity = useTransform(scrollY, [80, 120], [0, 1]);
// Refs for collision detection
const toolbarWrapperRef = useRef<HTMLDivElement>(null);
const toolbarContentRef = useRef<HTMLDivElement>(null);
const toolbarControlsRef = useRef<HTMLDivElement>(null);
const breadcrumbsRef = useRef<HTMLDivElement>(null);
// State for breadcrumb visibility
const [showBreadcrumbs, setShowBreadcrumbs] = useState(true);
@@ -96,6 +99,19 @@ export const HeaderToolbar = (props: HeaderToolbarProps) => {
};
}, []);
useGSAP(() => {
gsap.to(breadcrumbsRef.current, {
scrollTrigger: {
start: '10% 10%',
end: '20% 20%',
scrub: true,
},
opacity: 1,
duration: 1,
ease: 'power2.inOut',
});
});
const titleContent = (
<>
<div className={classNames.toolbarIcon}>{icon || <RiShapesLine />}</div>
@@ -116,10 +132,11 @@ export const HeaderToolbar = (props: HeaderToolbarProps) => {
<div className={classNames.toolbarName}>{titleContent}</div>
)}
{breadcrumbs && (
<motion.div
<div
ref={breadcrumbsRef}
className={classNames.breadcrumbs}
style={{
opacity: breadcrumbOpacity,
opacity: 0,
visibility: showBreadcrumbs ? 'visible' : 'hidden',
}}
>
@@ -144,7 +161,7 @@ export const HeaderToolbar = (props: HeaderToolbarProps) => {
)}
</div>
))}
</motion.div>
</div>
)}
</div>
<div className={classNames.toolbarControls} ref={toolbarControlsRef}>
+19 -60
View File
@@ -8458,6 +8458,7 @@ __metadata:
dependencies:
"@backstage/cli": "workspace:^"
"@base-ui-components/react": "npm:1.0.0-alpha.7"
"@gsap/react": "npm:^2.1.2"
"@remixicon/react": "npm:^4.6.0"
"@storybook/addon-essentials": "npm:^8.6.12"
"@storybook/addon-interactions": "npm:^8.6.12"
@@ -8476,9 +8477,9 @@ __metadata:
eslint-plugin-storybook: "npm:^0.12.0"
glob: "npm:^11.0.1"
globals: "npm:^15.11.0"
gsap: "npm:^3.13.0"
lightningcss: "npm:^1.29.1"
mini-css-extract-plugin: "npm:^2.9.2"
motion: "npm:^12.20.1"
react: "npm:^18.0.2"
react-aria-components: "npm:^1.10.1"
react-dom: "npm:^18.0.2"
@@ -10316,6 +10317,16 @@ __metadata:
languageName: node
linkType: hard
"@gsap/react@npm:^2.1.2":
version: 2.1.2
resolution: "@gsap/react@npm:2.1.2"
peerDependencies:
gsap: ^3.12.5
react: ">=17"
checksum: 10/48e78bbb31a4ec5a75aab6ea69ec3f4df0bb7cff0c8ebfcdc14553cb8e35f54b7fcf94f933e2aa65fd977135aaac87b855007d376504f1bb93a74328ba1dc0cb
languageName: node
linkType: hard
"@headlessui/react@npm:^1.7.15":
version: 1.7.17
resolution: "@headlessui/react@npm:1.7.17"
@@ -32319,28 +32330,6 @@ __metadata:
languageName: node
linkType: hard
"framer-motion@npm:^12.20.1":
version: 12.20.1
resolution: "framer-motion@npm:12.20.1"
dependencies:
motion-dom: "npm:^12.20.1"
motion-utils: "npm:^12.19.0"
tslib: "npm:^2.4.0"
peerDependencies:
"@emotion/is-prop-valid": "*"
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
peerDependenciesMeta:
"@emotion/is-prop-valid":
optional: true
react:
optional: true
react-dom:
optional: true
checksum: 10/ba36e76dbf66c856f0c5d3cfb80973bb0ef95ca9169fc16b22560a7bc55fbedcf63205266dba7beecac5152c4cd140097ff504c5d529beffca86cd85f97bf5e3
languageName: node
linkType: hard
"framer-motion@npm:^6.5.1":
version: 6.5.1
resolution: "framer-motion@npm:6.5.1"
@@ -33438,6 +33427,13 @@ __metadata:
languageName: node
linkType: hard
"gsap@npm:^3.13.0":
version: 3.13.0
resolution: "gsap@npm:3.13.0"
checksum: 10/1908d51cbdf8f0bec8a69919af352d743a06529639a89f750f1c02e9a165d3ea816f867569df222ee7bfc14e23e268ebeb51dc5df0687fa7918ddd5d3b3efaec
languageName: node
linkType: hard
"gtoken@npm:^7.0.0":
version: 7.0.1
resolution: "gtoken@npm:7.0.1"
@@ -39809,43 +39805,6 @@ __metadata:
languageName: node
linkType: hard
"motion-dom@npm:^12.20.1":
version: 12.20.1
resolution: "motion-dom@npm:12.20.1"
dependencies:
motion-utils: "npm:^12.19.0"
checksum: 10/b92d733fdfe3f66511f73c7f84f9483a0529eadf6ad2ab633cef584d161bcd1af786d257776e1fe7cd2b4e2c9c10912773f96ce9e2bd5b45c6c33ba6d9edfe86
languageName: node
linkType: hard
"motion-utils@npm:^12.19.0":
version: 12.19.0
resolution: "motion-utils@npm:12.19.0"
checksum: 10/a6fe6b329bf4e2070bb95788218f36f0d7ea6cc3098e6ea2646ed68d5be88d25fa30e55284463ace78d19a5e19714875a7b70055bd86ae6e52a5fdee08438470
languageName: node
linkType: hard
"motion@npm:^12.20.1":
version: 12.20.1
resolution: "motion@npm:12.20.1"
dependencies:
framer-motion: "npm:^12.20.1"
tslib: "npm:^2.4.0"
peerDependencies:
"@emotion/is-prop-valid": "*"
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
peerDependenciesMeta:
"@emotion/is-prop-valid":
optional: true
react:
optional: true
react-dom:
optional: true
checksum: 10/49057c8d5812207209ca078a67ee7467d2dca7abacdcf2db486cd28c3ca105a7fe2504c34407df201c85035f6df1c806954af94914417ee89d721133fc4e92ce
languageName: node
linkType: hard
"mri@npm:1.1.4":
version: 1.1.4
resolution: "mri@npm:1.1.4"