diff --git a/.changeset/fancy-lies-sell.md b/.changeset/fancy-lies-sell.md new file mode 100644 index 00000000000..21e5a2de128 --- /dev/null +++ b/.changeset/fancy-lies-sell.md @@ -0,0 +1,7 @@ +--- +'@clerk/clerk-js': patch +'@clerk/clerk-react': patch +'@clerk/types': patch +--- + +Introduce `clerk.__internal_openCheckout()` and `clerk.__internal_closeCheckout()` methods and remove `` from within the `` component. diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index c26190bfe4e..d433931fe9c 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -4,7 +4,7 @@ { "path": "./dist/clerk.browser.js", "maxSize": "81KB" }, { "path": "./dist/clerk.headless*.js", "maxSize": "55KB" }, { "path": "./dist/ui-common*.js", "maxSize": "96KB" }, - { "path": "./dist/vendors*.js", "maxSize": "30KB" }, + { "path": "./dist/vendors*.js", "maxSize": "35KB" }, { "path": "./dist/coinbase*.js", "maxSize": "35.5KB" }, { "path": "./dist/createorganization*.js", "maxSize": "5KB" }, { "path": "./dist/impersonationfab*.js", "maxSize": "5KB" }, diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index d3e9a464261..7c21cc14d80 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -13,6 +13,7 @@ import { import { addClerkPrefix, isAbsoluteUrl, stripScheme } from '@clerk/shared/url'; import { allSettled, handleValueOrFn, noop } from '@clerk/shared/utils'; import type { + __experimental_CheckoutProps, __experimental_CommerceNamespace, __experimental_PricingTableProps, __internal_ComponentNavigationContext, @@ -520,6 +521,18 @@ export class Clerk implements ClerkInterface { void this.#componentControls.ensureMounted().then(controls => controls.closeModal('signIn')); }; + public __internal_openCheckout = (props?: __experimental_CheckoutProps): void => { + this.assertComponentsReady(this.#componentControls); + void this.#componentControls + .ensureMounted({ preloadHint: 'Checkout' }) + .then(controls => controls.openDrawer('checkout', props || {})); + }; + + public __internal_closeCheckout = (): void => { + this.assertComponentsReady(this.#componentControls); + void this.#componentControls.ensureMounted().then(controls => controls.closeDrawer('checkout')); + }; + public __internal_openReverification = (props?: __internal_UserVerificationModalProps): void => { this.assertComponentsReady(this.#componentControls); if (noUserExists(this)) { diff --git a/packages/clerk-js/src/ui/Components.tsx b/packages/clerk-js/src/ui/Components.tsx index 3ebbfd9e213..1d17012f6ba 100644 --- a/packages/clerk-js/src/ui/Components.tsx +++ b/packages/clerk-js/src/ui/Components.tsx @@ -1,6 +1,7 @@ import { useSafeLayoutEffect } from '@clerk/shared/react'; import { createDeferredPromise } from '@clerk/shared/utils'; import type { + __experimental_CheckoutProps, __internal_UserVerificationProps, Appearance, Clerk, @@ -24,6 +25,7 @@ import { useClerkModalStateParams } from './hooks/useClerkModalStateParams'; import type { ClerkComponentName } from './lazyModules/components'; import { BlankCaptchaModal, + Checkout, CreateOrganizationModal, ImpersonationFab, KeylessPrompt, @@ -37,6 +39,7 @@ import { } from './lazyModules/components'; import { LazyComponentRenderer, + LazyDrawerRenderer, LazyImpersonationFabProvider, LazyModalRenderer, LazyOneTapRenderer, @@ -99,6 +102,16 @@ export type ComponentControls = { notify?: boolean; }, ) => void; + openDrawer: ( + drawer: T, + props: T extends 'checkout' ? __experimental_CheckoutProps : never, + ) => void; + closeDrawer: ( + drawer: 'checkout', + options?: { + notify?: boolean; + }, + ) => void; prefetch: (component: 'organizationSwitcher') => void; // Special case, as the impersonation fab mounts automatically mountImpersonationFab: () => void; @@ -131,6 +144,10 @@ interface ComponentsState { blankCaptchaModal: null; organizationSwitcherPrefetch: boolean; waitlistModal: null | WaitlistProps; + checkoutDrawer: { + open: false; + props: null | __experimental_CheckoutProps; + }; nodes: Map; impersonationFab: boolean; } @@ -212,6 +229,10 @@ const Components = (props: ComponentsProps) => { organizationSwitcherPrefetch: false, waitlistModal: null, blankCaptchaModal: null, + checkoutDrawer: { + open: false, + props: null, + }, nodes: new Map(), impersonationFab: false, }); @@ -226,6 +247,7 @@ const Components = (props: ComponentsProps) => { createOrganizationModal, waitlistModal, blankCaptchaModal, + checkoutDrawer, nodes, } = state; @@ -322,6 +344,26 @@ const Components = (props: ComponentsProps) => { setState(s => ({ ...s, impersonationFab: true })); }; + componentsControls.openDrawer = (name, props) => { + setState(s => ({ + ...s, + [`${name}Drawer`]: { + open: true, + props, + }, + })); + }; + + componentsControls.closeDrawer = name => { + setState(s => ({ + ...s, + [`${name}Drawer`]: { + ...s[`${name}Drawer`], + open: false, + }, + })); + }; + componentsControls.prefetch = component => { setState(s => ({ ...s, [`${component}Prefetch`]: true })); }; @@ -481,6 +523,26 @@ const Components = (props: ComponentsProps) => { ); + const mountedCheckoutDrawer = checkoutDrawer.props && ( + componentsControls.closeDrawer('checkout')} + componentName={'Checkout'} + portalId={checkoutDrawer.props.portalId} + > + + + ); + return ( { {createOrganizationModal && mountedCreateOrganizationModal} {waitlistModal && mountedWaitlistModal} {blankCaptchaModal && mountedBlankCaptchaModal} + {mountedCheckoutDrawer} {state.impersonationFab && ( diff --git a/packages/clerk-js/src/ui/components/Checkout/Checkout.tsx b/packages/clerk-js/src/ui/components/Checkout/Checkout.tsx index 7d4e4bcb933..dd869dbc7d0 100644 --- a/packages/clerk-js/src/ui/components/Checkout/Checkout.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/Checkout.tsx @@ -1,43 +1,25 @@ import type { __experimental_CheckoutProps } from '@clerk/types'; -import { PROFILE_CARD_SCROLLBOX_ID } from '../../constants'; -import { useCheckoutContext, withCoreUserGuard } from '../../contexts'; +import { __experimental_CheckoutContext } from '../../contexts'; import { Flow } from '../../customizables'; import { Drawer } from '../../elements'; -import { Route, Switch } from '../../router'; import { CheckoutPage } from './CheckoutPage'; export const __experimental_Checkout = (props: __experimental_CheckoutProps) => { return ( - - - - - + <__experimental_CheckoutContext.Provider + value={{ + componentName: 'Checkout', + }} + > + + + + + ); }; - -const AuthenticatedRoutes = withCoreUserGuard((props: __experimental_CheckoutProps) => { - const { mode = 'mounted', isOpen = false, setIsOpen = () => {} } = useCheckoutContext(); - - return ( - - - - - - - - ); -}); diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx index b75f027e55f..437cd1e21be 100644 --- a/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx @@ -1,14 +1,13 @@ import type { __experimental_CommerceCheckoutResource } from '@clerk/types'; -import { useCheckoutContext } from '../../contexts'; import { Box, Button, descriptors, Heading, Icon, localizationKeys, Span, Text } from '../../customizables'; -import { Drawer, LineItems } from '../../elements'; +import { Drawer, LineItems, useDrawerContext } from '../../elements'; import { Check } from '../../icons'; const capitalize = (name: string) => name[0].toUpperCase() + name.slice(1); export const CheckoutComplete = ({ checkout }: { checkout: __experimental_CommerceCheckoutResource }) => { - const { setIsOpen } = useCheckoutContext(); + const { setIsOpen } = useDrawerContext(); const handleClose = () => { if (setIsOpen) { diff --git a/packages/clerk-js/src/ui/components/PricingTable/PricingTable.tsx b/packages/clerk-js/src/ui/components/PricingTable/PricingTable.tsx index 016738c435c..58d37abf5cc 100644 --- a/packages/clerk-js/src/ui/components/PricingTable/PricingTable.tsx +++ b/packages/clerk-js/src/ui/components/PricingTable/PricingTable.tsx @@ -8,10 +8,9 @@ import type { import { useState } from 'react'; import { PROFILE_CARD_SCROLLBOX_ID } from '../../constants'; -import { __experimental_CheckoutContext, usePricingTableContext } from '../../contexts'; +import { usePricingTableContext } from '../../contexts'; import { AppearanceProvider } from '../../customizables'; import { usePlans } from '../../hooks'; -import { __experimental_Checkout } from '../Checkout'; import { PricingTableDefault } from './PricingTableDefault'; import { PricingTableMatrix } from './PricingTableMatrix'; import { SubscriptionDetailDrawer } from './SubscriptionDetailDrawer'; @@ -25,10 +24,8 @@ export const __experimental_PricingTable = (props: __experimental_PricingTablePr const { plans, subscriptions, revalidate } = usePlans({ subscriberType }); const [planPeriod, setPlanPeriod] = useState<__experimental_CommerceSubscriptionPlanPeriod>('month'); - const [checkoutPlan, setCheckoutPlan] = useState<__experimental_CommercePlanResource>(); const [detailSubscription, setDetailSubscription] = useState<__experimental_CommerceSubscriptionResource>(); - const [showCheckout, setShowCheckout] = useState(false); const [showSubscriptionDetailDrawer, setShowSubscriptionDetailDrawer] = useState(false); const selectPlan = (plan: __experimental_CommercePlanResource) => { @@ -40,8 +37,13 @@ export const __experimental_PricingTable = (props: __experimental_PricingTablePr setDetailSubscription(activeSubscription); setShowSubscriptionDetailDrawer(true); } else { - setCheckoutPlan(plan); - setShowCheckout(true); + clerk.__internal_openCheckout({ + planId: plan.id, + planPeriod, + orgId: subscriberType === 'org' ? organization?.id : undefined, + onSubscriptionComplete: onSubscriptionChange, + portalId: mode === 'modal' ? PROFILE_CARD_SCROLLBOX_ID : undefined, + }); } }; @@ -74,26 +76,6 @@ export const __experimental_PricingTable = (props: __experimental_PricingTablePr appearanceKey='checkout' appearance={props.checkoutProps?.appearance} > - <__experimental_CheckoutContext.Provider - value={{ - componentName: 'Checkout', - mode, - isOpen: showCheckout, - setIsOpen: setShowCheckout, - }} - > - {/*TODO: Used by InvisibleRootBox, can we simplify? */} -
- {checkoutPlan && ( - <__experimental_Checkout - planPeriod={planPeriod} - planId={checkoutPlan.id} - orgId={subscriberType === 'org' ? organization?.id : undefined} - onSubscriptionComplete={onSubscriptionChange} - /> - )} -
- ; @@ -71,164 +72,166 @@ export function SubscriptionDetailDrawer({ }; return ( - - - - - !hasFeatures - ? { - flex: 1, - borderBottomWidth: 0, - background: t.colors.$colorBackground, - } - : null - } - > -
} - /> - + + + + + + !hasFeatures + ? { + flex: 1, + borderBottomWidth: 0, + background: t.colors.$colorBackground, + } + : null + } + > +
} + /> + - {hasFeatures ? ( - - ({ - display: 'grid', - rowGap: t.space.$4, - padding: t.space.$4, - })} - > - {features.map(feature => ( - ({ - display: 'flex', - alignItems: 'baseline', - gap: t.space.$3, - })} - > - {feature.avatarUrl ? ( - 24} - title={feature.name} - initials={feature.name[0]} - rounded={false} - imageUrl={feature.avatarUrl} - /> - ) : null} - - ({ - fontWeight: t.fontWeights.$medium, - })} - > - {feature.name} - - {feature.description ? ( + {hasFeatures ? ( + + ({ + display: 'grid', + rowGap: t.space.$4, + padding: t.space.$4, + })} + > + {features.map(feature => ( + ({ + display: 'flex', + alignItems: 'baseline', + gap: t.space.$3, + })} + > + {feature.avatarUrl ? ( + 24} + title={feature.name} + initials={feature.name[0]} + rounded={false} + imageUrl={feature.avatarUrl} + /> + ) : null} + ({ - marginBlockStart: t.space.$0x25, - fontSize: t.fontSizes.$xs, + fontWeight: t.fontWeights.$medium, })} > - {feature.description} + {feature.name} - ) : null} - - - ))} - - - ) : null} + {feature.description ? ( + ({ + marginBlockStart: t.space.$0x25, + fontSize: t.fontSizes.$xs, + })} + > + {feature.description} + + ) : null} + + + ))} + + + ) : null} - - - + + + - - {!isSubmitting && ( + + {!isSubmitting && ( + + )} - )} - - - } - > - + } > - {/* TODO(@COMMERCE): needs localization */} - Cancel {subscription.plan.name} Subscription? - - - {/* TODO(@COMMERCE): needs localization */} - You can keep using “{subscription.plan.name}” features until [DATE], after which you will no - longer have access. - - {cancelError && ( - // TODO(@COMMERCE): needs localization - {typeof cancelError === 'string' ? cancelError : cancelError.message} - )} - - - + + {/* TODO(@COMMERCE): needs localization */} + Cancel {subscription.plan.name} Subscription? + + + {/* TODO(@COMMERCE): needs localization */} + You can keep using “{subscription.plan.name}” features until [DATE], after which you will no + longer have access. + + {cancelError && ( + // TODO(@COMMERCE): needs localization + {typeof cancelError === 'string' ? cancelError : cancelError.message} + )} + + + + ); } diff --git a/packages/clerk-js/src/ui/elements/Drawer.tsx b/packages/clerk-js/src/ui/elements/Drawer.tsx index b7552cdf102..32f0f88b115 100644 --- a/packages/clerk-js/src/ui/elements/Drawer.tsx +++ b/packages/clerk-js/src/ui/elements/Drawer.tsx @@ -20,7 +20,7 @@ import { usePrefersReducedMotion } from '../hooks'; import { useScrollLock } from '../hooks/useScrollLock'; import { Close as CloseIcon } from '../icons'; import type { ThemableCssProp } from '../styledSystem'; -import { common, InternalThemeProvider } from '../styledSystem'; +import { common } from '../styledSystem'; import { colors } from '../utils'; import { IconButton } from './IconButton'; @@ -102,21 +102,19 @@ function Root({ ]); return ( - - - {children} - - + + {children} + ); } @@ -196,7 +194,7 @@ const Content = React.forwardRef(({ children }, re const prefersReducedMotion = usePrefersReducedMotion(); const { animations: layoutAnimations } = useAppearance().parsedLayout; const isMotionSafe = !prefersReducedMotion && layoutAnimations === true; - const { strategy, portalProps, refs, context, getFloatingProps } = useDrawerContext(); + const { strategy, refs, context, getFloatingProps } = useDrawerContext(); const mergedRefs = useMergeRefs([ref, refs.setFloating]); const { isMounted, styles: transitionStyles } = useTransitionStyles(context, { @@ -213,47 +211,45 @@ const Content = React.forwardRef(({ children }, re if (!isMounted) return null; return ( - - + ({ + // Apply the conditional right offset + the spread of the + // box shadow to ensure it is fully offscreen before unmounting + '--transform-offset': + strategy === 'fixed' ? `calc(100% + ${t.space.$3} + ${t.space.$8x75})` : `calc(100% + ${t.space.$8x75})`, + willChange: 'transform', + position: strategy, + insetBlock: strategy === 'fixed' ? t.space.$3 : 0, + insetInlineEnd: strategy === 'fixed' ? t.space.$3 : 0, + outline: 0, + width: t.sizes.$100, + backgroundColor: t.colors.$colorBackground, + borderStartStartRadius: t.radii.$xl, + borderEndStartRadius: t.radii.$xl, + borderEndEndRadius: strategy === 'fixed' ? t.radii.$xl : 0, + borderStartEndRadius: strategy === 'fixed' ? t.radii.$xl : 0, + borderWidth: t.borderWidths.$normal, + borderStyle: t.borderStyles.$solid, + borderColor: t.colors.$neutralAlpha100, + boxShadow: t.shadows.$cardBoxShadow, + overflow: 'hidden', + zIndex: t.zIndices.$modal, + })} > - ({ - // Apply the conditional right offset + the spread of the - // box shadow to ensure it is fully offscreen before unmounting - '--transform-offset': - strategy === 'fixed' ? `calc(100% + ${t.space.$3} + ${t.space.$8x75})` : `calc(100% + ${t.space.$8x75})`, - willChange: 'transform', - position: strategy, - insetBlock: strategy === 'fixed' ? t.space.$3 : 0, - insetInlineEnd: strategy === 'fixed' ? t.space.$3 : 0, - outline: 0, - width: t.sizes.$100, - backgroundColor: t.colors.$colorBackground, - borderStartStartRadius: t.radii.$xl, - borderEndStartRadius: t.radii.$xl, - borderEndEndRadius: strategy === 'fixed' ? t.radii.$xl : 0, - borderStartEndRadius: strategy === 'fixed' ? t.radii.$xl : 0, - borderWidth: t.borderWidths.$normal, - borderStyle: t.borderStyles.$solid, - borderColor: t.colors.$neutralAlpha100, - boxShadow: t.shadows.$cardBoxShadow, - overflow: 'hidden', - zIndex: t.zIndices.$modal, - })} - > - {children} - - - + {children} + + ); }); diff --git a/packages/clerk-js/src/ui/lazyModules/components.ts b/packages/clerk-js/src/ui/lazyModules/components.ts index 4ce33dc78c6..b26c3f33343 100644 --- a/packages/clerk-js/src/ui/lazyModules/components.ts +++ b/packages/clerk-js/src/ui/lazyModules/components.ts @@ -95,6 +95,10 @@ export const PricingTable = lazy(() => componentImportPaths.PricingTable().then(module => ({ default: module.__experimental_PricingTable })), ); +export const Checkout = lazy(() => + componentImportPaths.Checkout().then(module => ({ default: module.__experimental_Checkout })), +); + export const SessionTasks = lazy(() => componentImportPaths.SessionTasks().then(module => ({ default: module.SessionTask })), ); @@ -124,6 +128,7 @@ export const ClerkComponents = { WaitlistModal, BlankCaptchaModal, PricingTable, + Checkout, }; export type ClerkComponentName = keyof typeof ClerkComponents; diff --git a/packages/clerk-js/src/ui/lazyModules/providers.tsx b/packages/clerk-js/src/ui/lazyModules/providers.tsx index 61162e7ee32..e99d2542a58 100644 --- a/packages/clerk-js/src/ui/lazyModules/providers.tsx +++ b/packages/clerk-js/src/ui/lazyModules/providers.tsx @@ -2,7 +2,7 @@ import { deprecated } from '@clerk/shared/deprecated'; import type { Appearance } from '@clerk/types'; import React, { lazy, Suspense } from 'react'; -import type { FlowMetadata } from '../elements'; +import type { Drawer, FlowMetadata } from '../elements'; import type { ThemableCssProp } from '../styledSystem'; import type { ClerkComponentName } from './components'; import { ClerkComponents } from './components'; @@ -20,6 +20,8 @@ const Portal = lazy(() => import('./../portal').then(m => ({ default: m.Portal } const VirtualBodyRootPortal = lazy(() => import('./../portal').then(m => ({ default: m.VirtualBodyRootPortal }))); const FlowMetadataProvider = lazy(() => import('./../elements').then(m => ({ default: m.FlowMetadataProvider }))); const Modal = lazy(() => import('./../elements').then(m => ({ default: m.Modal }))); +const DrawerRoot = lazy(() => import('./../elements').then(m => ({ default: m.Drawer.Root }))); +const DrawerOverlay = lazy(() => import('./../elements').then(m => ({ default: m.Drawer.Overlay }))); const OrganizationSwitcherPrefetch = lazy(() => import(/* webpackChunkName: "prefetchorganizationlist" */ '../components/prefetch-organization-list').then(m => ({ default: m.OrganizationSwitcherPrefetch, @@ -131,6 +133,46 @@ export const LazyModalRenderer = (props: LazyModalRendererProps) => { ); }; +type DrawerProps = Parameters[0]; + +type LazyDrawerRendererProps = React.PropsWithChildren< + { + componentName: ClerkComponentName; + flowName?: FlowMetadata['flow']; + open: DrawerProps['open']; + onOpenChange: DrawerProps['onOpenChange']; + portalId?: string; + } & AppearanceProviderProps +>; + +export const LazyDrawerRenderer = (props: LazyDrawerRendererProps) => { + return ( + + + + + + + {props.children} + + + + + + ); +}; + /** * This component automatically mounts when impersonating, without a user action. * We want to hotload the /ui dependencies only if we're actually impersonating. diff --git a/packages/clerk-js/src/ui/types.ts b/packages/clerk-js/src/ui/types.ts index b915f4240d1..3e65b2d8acd 100644 --- a/packages/clerk-js/src/ui/types.ts +++ b/packages/clerk-js/src/ui/types.ts @@ -109,9 +109,6 @@ export type __experimental_PricingTableCtx = __experimental_PricingTableProps & export type __experimental_CheckoutCtx = __experimental_CheckoutProps & { componentName: 'Checkout'; - mode?: ComponentMode; - isOpen?: boolean; - setIsOpen?: (open: boolean) => void; }; export type SessionTasksCtx = { diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index e74152e7000..0c5ce92ceb6 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -2,6 +2,7 @@ import { inBrowser } from '@clerk/shared/browser'; import { loadClerkJsScript } from '@clerk/shared/loadClerkJsScript'; import { handleValueOrFn } from '@clerk/shared/utils'; import type { + __experimental_CheckoutProps, __experimental_CommerceNamespace, __experimental_PricingTableProps, __internal_UserVerificationModalProps, @@ -109,6 +110,7 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { private preopenOneTap?: null | GoogleOneTapProps = null; private preopenUserVerification?: null | __internal_UserVerificationProps = null; private preopenSignIn?: null | SignInProps = null; + private preopenCheckout?: null | __experimental_CheckoutProps = null; private preopenSignUp?: null | SignUpProps = null; private preopenUserProfile?: null | UserProfileProps = null; private preopenOrganizationProfile?: null | OrganizationProfileProps = null; @@ -472,6 +474,10 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { clerkjs.openSignIn(this.preopenSignIn); } + if (this.preopenCheckout !== null) { + clerkjs.__internal_openCheckout(this.preopenCheckout); + } + if (this.preopenSignUp !== null) { clerkjs.openSignUp(this.preopenSignUp); } @@ -650,6 +656,22 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; + __internal_openCheckout = (props?: __experimental_CheckoutProps) => { + if (this.clerkjs && this.#loaded) { + this.clerkjs.__internal_openCheckout(props); + } else { + this.preopenCheckout = props; + } + }; + + __internal_closeCheckout = () => { + if (this.clerkjs && this.#loaded) { + this.clerkjs.__internal_closeCheckout(); + } else { + this.preopenCheckout = null; + } + }; + __internal_openReverification = (props?: __internal_UserVerificationModalProps) => { if (this.clerkjs && this.#loaded) { this.clerkjs.__internal_openReverification(props); diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index e4f80529c48..444f5f40853 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -178,6 +178,17 @@ export interface Clerk { */ closeSignIn: () => void; + /** + * Opens the Clerk Checkout component in a drawer. + * @param props Optional checkout configuration parameters. + */ + __internal_openCheckout: (props?: __experimental_CheckoutProps) => void; + + /** + * Closes the Clerk Checkout drawer. + */ + __internal_closeCheckout: () => void; + /** * Opens the Clerk UserVerification component in a modal. * @param props Optional user verification configuration parameters. @@ -1515,6 +1526,7 @@ export type __experimental_CheckoutProps = { planPeriod?: __experimental_CommerceSubscriptionPlanPeriod; orgId?: string; onSubscriptionComplete?: () => void; + portalId?: string; }; export type __experimental_PaymentSourcesProps = {