From e68c22b1423f9f9e2de8cd5ca72c6abada27ce3e Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 2 May 2025 10:09:34 -0400 Subject: [PATCH 1/6] init --- .../components/Checkout/CheckoutComplete.tsx | 142 ++++++++++++++++-- packages/clerk-js/src/ui/elements/Drawer.tsx | 10 +- 2 files changed, 134 insertions(+), 18 deletions(-) diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx index f52c11cb318..78c6fa915da 100644 --- a/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx @@ -1,14 +1,18 @@ import type { __experimental_CommerceCheckoutResource } from '@clerk/types'; -import { Box, Button, descriptors, Heading, Icon, localizationKeys, Span, Text } from '../../customizables'; +import { Box, Button, descriptors, Heading, localizationKeys, Span, Text, useAppearance } from '../../customizables'; import { Drawer, LineItems, useDrawerContext } from '../../elements'; -import { Check } from '../../icons'; +import { transitionDurationValues, transitionTiming } from '../../foundations/transitions'; +import { usePrefersReducedMotion } from '../../hooks'; +import { animations } from '../../styledSystem'; import { formatDate } from '../../utils'; - const capitalize = (name: string) => name[0].toUpperCase() + name.slice(1); export const CheckoutComplete = ({ checkout }: { checkout: __experimental_CommerceCheckoutResource }) => { const { setIsOpen } = useDrawerContext(); + const prefersReducedMotion = usePrefersReducedMotion(); + const { animations: layoutAnimations } = useAppearance().parsedLayout; + const isMotionSafe = !prefersReducedMotion && layoutAnimations === true; const handleClose = () => { if (setIsOpen) { @@ -29,11 +33,33 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer width: '100%', padding: t.space.$4, flexShrink: 0, + transformOrigin: 'bottom center', + animationName: 'scaleIn', + animationDuration: `${transitionDurationValues.slowest}ms`, + animationTimingFunction: transitionTiming.bezier, + '@keyframes scaleIn': { + '0%': { + filter: 'blur(10px)', + transform: 'scale(0.85)', + opacity: 0, + }, + '100%': { + filter: 'blur(0px)', + transform: 'scale(1)', + opacity: 1, + }, + }, })} > - - - + {[1, 0.75, 0.5].map((scale, index, array) => { + return ( + + ); + })} ({ @@ -55,15 +81,44 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer }, })} > - ({ @@ -83,11 +138,47 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer ? localizationKeys('__experimental_commerce.checkout.title__paymentSuccessful') : localizationKeys('__experimental_commerce.checkout.title__subscriptionSuccessful') } + sx={{ + opacity: 0, + animationName: 'slideUp', + animationDuration: `${transitionDurationValues.slowest}ms`, + animationTimingFunction: transitionTiming.bezier, + animationFillMode: 'forwards', + '@keyframes slideUp': { + '0%': { + transform: 'translateY(30px)', + opacity: 0, + }, + '100%': { + transform: 'translateY(0)', + opacity: 1, + }, + }, + }} /> ({ textAlign: 'center', paddingInline: t.space.$8, marginBlockStart: t.space.$2 })} + sx={t => ({ + textAlign: 'center', + paddingInline: t.space.$8, + marginBlockStart: t.space.$2, + opacity: 0, + animationName: 'slideUp', + animationDuration: `${transitionDurationValues.slowest * 1.5}ms`, + animationTimingFunction: transitionTiming.bezier, + animationFillMode: 'forwards', + '@keyframes slideUp': { + '0%': { + transform: 'translateY(30px)', + opacity: 0, + }, + '100%': { + transform: 'translateY(0)', + opacity: 1, + }, + }, + })} localizationKey={ checkout.subscription?.status === 'active' ? localizationKeys('__experimental_commerce.checkout.description__paymentSuccessful') @@ -97,10 +188,22 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer - ({ rowGap: t.space.$4, + animationName: 'footerSlideInUp', + animationDuration: `${transitionDurationValues.drawer}ms`, + animationTimingFunction: transitionTiming.bezier, + '@keyframes footerSlideInUp': { + '0%': { + transform: 'translateY(100%)', + opacity: 0, + }, + '100%': { + transform: 'translateY(0)', + opacity: 1, + }, + }, })} > @@ -151,11 +254,16 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer function Ring({ scale, + index, }: { /** * Number between 0-1 */ scale: number; + /** + * Index of the ring (0-2) + */ + index: number; }) { return ( ); diff --git a/packages/clerk-js/src/ui/elements/Drawer.tsx b/packages/clerk-js/src/ui/elements/Drawer.tsx index b0800e62795..0542c6c66b6 100644 --- a/packages/clerk-js/src/ui/elements/Drawer.tsx +++ b/packages/clerk-js/src/ui/elements/Drawer.tsx @@ -313,11 +313,11 @@ const Header = React.forwardRef(({ title, children, * Drawer.Body * -----------------------------------------------------------------------------------------------*/ -interface BodyProps { +interface BodyProps extends React.HTMLAttributes { children: React.ReactNode; } -const Body = React.forwardRef(({ children }, ref) => { +const Body = React.forwardRef(({ children, ...props }, ref) => { return ( (({ children }, ref) => overflowY: 'auto', overflowX: 'hidden', }} + {...props} > {children} @@ -339,12 +340,12 @@ const Body = React.forwardRef(({ children }, ref) => * Drawer.Footer * -----------------------------------------------------------------------------------------------*/ -interface FooterProps { +interface FooterProps extends React.HTMLAttributes { children?: React.ReactNode; sx?: ThemableCssProp; } -const Footer = React.forwardRef(({ children, sx }, ref) => { +const Footer = React.forwardRef(({ children, sx, ...props }, ref) => { return ( (({ children, sx }, }), sx, ]} + {...props} > {children} From 59f20408c7a92524a1ee2abf5e668571e7d591d7 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 2 May 2025 10:13:56 -0400 Subject: [PATCH 2/6] removes spaces --- packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx index 235644563b7..2954832753c 100644 --- a/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx @@ -87,12 +87,12 @@ export const CheckoutForm = ({ > {/* TODO(@Commerce): needs localization */} - + {/* TODO(@Commerce): needs localization */} - + {/* TODO(@Commerce): needs localization */} From c31ca55076dc27d29bc97a69cb7f90019ac76748 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 2 May 2025 10:41:21 -0400 Subject: [PATCH 3/6] reduced motion --- .../components/Checkout/CheckoutComplete.tsx | 42 ++++++++++++++++--- .../ui/components/Checkout/CheckoutPage.tsx | 14 +++++-- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx index 78c6fa915da..c6170ebb728 100644 --- a/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx @@ -1,18 +1,20 @@ import type { __experimental_CommerceCheckoutResource } from '@clerk/types'; -import { Box, Button, descriptors, Heading, localizationKeys, Span, Text, useAppearance } from '../../customizables'; +import { Box, Button, descriptors, Heading, localizationKeys, Span, Text } from '../../customizables'; import { Drawer, LineItems, useDrawerContext } from '../../elements'; import { transitionDurationValues, transitionTiming } from '../../foundations/transitions'; -import { usePrefersReducedMotion } from '../../hooks'; import { animations } from '../../styledSystem'; import { formatDate } from '../../utils'; const capitalize = (name: string) => name[0].toUpperCase() + name.slice(1); -export const CheckoutComplete = ({ checkout }: { checkout: __experimental_CommerceCheckoutResource }) => { +export const CheckoutComplete = ({ + checkout, + isMotionSafe, +}: { + checkout: __experimental_CommerceCheckoutResource; + isMotionSafe: boolean; +}) => { const { setIsOpen } = useDrawerContext(); - const prefersReducedMotion = usePrefersReducedMotion(); - const { animations: layoutAnimations } = useAppearance().parsedLayout; - const isMotionSafe = !prefersReducedMotion && layoutAnimations === true; const handleClose = () => { if (setIsOpen) { @@ -37,6 +39,8 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer animationName: 'scaleIn', animationDuration: `${transitionDurationValues.slowest}ms`, animationTimingFunction: transitionTiming.bezier, + animationFillMode: 'forwards', + opacity: 0, '@keyframes scaleIn': { '0%': { filter: 'blur(10px)', @@ -49,6 +53,10 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer opacity: 1, }, }, + ...(!isMotionSafe && { + animation: 'none', + opacity: 1, + }), })} > {[1, 0.75, 0.5].map((scale, index, array) => { @@ -57,6 +65,7 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer key={scale} scale={scale} index={array.length - 1 - index} + isMotionSafe={isMotionSafe} /> ); })} @@ -107,6 +116,10 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer animationTimingFunction: transitionTiming.bezier, animationFillMode: 'forwards', animationDelay: `${transitionDurationValues.slow}ms`, + ...(!isMotionSafe && { + strokeDashoffset: '0', + animation: 'none', + }), }} /> @@ -154,6 +167,10 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer opacity: 1, }, }, + ...(!isMotionSafe && { + opacity: 1, + animation: 'none', + }), }} /> @@ -255,6 +279,7 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer function Ring({ scale, index, + isMotionSafe, }: { /** * Number between 0-1 @@ -264,6 +289,7 @@ function Ring({ * Index of the ring (0-2) */ index: number; + isMotionSafe: boolean; }) { return ( ); diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutPage.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutPage.tsx index 3f888afc149..cbe707ea052 100644 --- a/packages/clerk-js/src/ui/components/Checkout/CheckoutPage.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutPage.tsx @@ -1,9 +1,9 @@ import type { __experimental_CheckoutProps, __experimental_CommerceCheckoutResource } from '@clerk/types'; import { useEffect } from 'react'; -import { Alert, Box, localizationKeys, Spinner } from '../../customizables'; +import { Alert, Box, localizationKeys, Spinner, useAppearance } from '../../customizables'; import { Drawer, useDrawerContext } from '../../elements'; -import { useCheckout } from '../../hooks'; +import { useCheckout, usePrefersReducedMotion } from '../../hooks'; import { EmailForm } from '../UserProfile/EmailForm'; import { CheckoutComplete } from './CheckoutComplete'; import { CheckoutForm } from './CheckoutForm'; @@ -11,6 +11,9 @@ import { CheckoutForm } from './CheckoutForm'; export const CheckoutPage = (props: __experimental_CheckoutProps) => { const { planId, planPeriod, subscriberType, onSubscriptionComplete } = props; const { setIsOpen, isOpen } = useDrawerContext(); + const prefersReducedMotion = usePrefersReducedMotion(); + const { animations: layoutAnimations } = useAppearance().parsedLayout; + const isMotionSafe = !prefersReducedMotion && layoutAnimations === true; const { checkout, isLoading, invalidate, revalidate, updateCheckout, isMissingPayerEmail } = useCheckout({ planId, @@ -42,7 +45,12 @@ export const CheckoutPage = (props: __experimental_CheckoutProps) => { if (checkout) { if (checkout?.status === 'completed') { - return ; + return ( + + ); } return ( From 01ab9fd0f0497d40f5c8bddb858605a6089666ec Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 2 May 2025 10:45:24 -0400 Subject: [PATCH 4/6] add changeset --- .changeset/purple-cities-study.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/purple-cities-study.md diff --git a/.changeset/purple-cities-study.md b/.changeset/purple-cities-study.md new file mode 100644 index 00000000000..c7a40b91e51 --- /dev/null +++ b/.changeset/purple-cities-study.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-js': patch +--- + +Add entry animations to CheckoutComplete component to smooth our the transition between checking out to successful state. From 44db4141ee5b01cae0ef33fd0ece90da4547dfd9 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 2 May 2025 16:46:24 -0400 Subject: [PATCH 5/6] bump bundle size --- packages/clerk-js/bundlewatch.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index 47b4a4fdd85..016bff3941f 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -21,7 +21,7 @@ { "path": "./dist/waitlist*.js", "maxSize": "1.3KB" }, { "path": "./dist/keylessPrompt*.js", "maxSize": "6.5KB" }, { "path": "./dist/pricingTable*.js", "maxSize": "4.02KB" }, - { "path": "./dist/checkout*.js", "maxSize": "5.3KB" }, + { "path": "./dist/checkout*.js", "maxSize": "5.5KB" }, { "path": "./dist/paymentSources*.js", "maxSize": "8.9KB" }, { "path": "./dist/up-billing-page*.js", "maxSize": "2.4KB" }, { "path": "./dist/op-billing-page*.js", "maxSize": "2.4KB" }, From 6bf454d4da27d239492be6e014b6c7d2c3301a37 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 2 May 2025 17:21:15 -0400 Subject: [PATCH 6/6] bump --- packages/clerk-js/bundlewatch.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index 016bff3941f..d24606d3c2d 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -21,7 +21,7 @@ { "path": "./dist/waitlist*.js", "maxSize": "1.3KB" }, { "path": "./dist/keylessPrompt*.js", "maxSize": "6.5KB" }, { "path": "./dist/pricingTable*.js", "maxSize": "4.02KB" }, - { "path": "./dist/checkout*.js", "maxSize": "5.5KB" }, + { "path": "./dist/checkout*.js", "maxSize": "5.75KB" }, { "path": "./dist/paymentSources*.js", "maxSize": "8.9KB" }, { "path": "./dist/up-billing-page*.js", "maxSize": "2.4KB" }, { "path": "./dist/op-billing-page*.js", "maxSize": "2.4KB" },