diff --git a/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx b/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx index b7bbacb2775..c1c8b47a961 100644 --- a/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx +++ b/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx @@ -241,6 +241,8 @@ export const useUserButtonContext = () => { const afterSwitchSessionUrl = ctx.afterSwitchSessionUrl || displayConfig.afterSwitchSessionUrl; const navigateAfterSwitchSession = () => navigate(afterSwitchSessionUrl); + const userProfileMode = !!userProfileUrl && ctx.userProfileMode === undefined ? 'navigation' : ctx.userProfileMode; + return { ...ctx, componentName, @@ -252,6 +254,7 @@ export const useUserButtonContext = () => { afterMultiSessionSingleSignOutUrl, afterSignOutUrl, afterSwitchSessionUrl, + userProfileMode, }; }; @@ -316,11 +319,19 @@ export const useOrganizationSwitcherContext = () => { const navigateAfterSelectPersonal = (user: UserResource) => navigateAfterSelectOrganizationOrPersonal({ user }); + const organizationProfileMode = + !!ctx.organizationProfileUrl && ctx.organizationProfileMode === undefined + ? 'navigation' + : ctx.organizationProfileMode; + + const createOrganizationMode = + !!ctx.createOrganizationUrl && ctx.createOrganizationMode === undefined ? 'navigation' : ctx.createOrganizationMode; + return { ...ctx, hidePersonal: ctx.hidePersonal || false, - organizationProfileMode: ctx.organizationProfileMode || 'modal', - createOrganizationMode: ctx.createOrganizationMode || 'modal', + organizationProfileMode: organizationProfileMode || 'modal', + createOrganizationMode: createOrganizationMode || 'modal', afterCreateOrganizationUrl, afterLeaveOrganizationUrl, navigateOrganizationProfile, diff --git a/packages/react/src/components/uiComponents.tsx b/packages/react/src/components/uiComponents.tsx index 42edc8f1ff2..7ea7a923e3f 100644 --- a/packages/react/src/components/uiComponents.tsx +++ b/packages/react/src/components/uiComponents.tsx @@ -8,6 +8,7 @@ import type { SignUpProps, UserButtonProps, UserProfileProps, + Without, } from '@clerk/types'; import type { PropsWithChildren } from 'react'; import React, { createElement } from 'react'; @@ -39,7 +40,7 @@ type UserButtonExportType = typeof _UserButton & { UserProfileLink: typeof UserProfileLink; }; -type UserButtonPropsWithoutCustomPages = Omit & { +type UserButtonPropsWithoutCustomPages = Without & { userProfileProps?: Pick; }; @@ -53,7 +54,7 @@ type OrganizationSwitcherExportType = typeof _OrganizationSwitcher & { OrganizationProfileLink: typeof OrganizationProfileLink; }; -type OrganizationSwitcherPropsWithoutCustomPages = Omit & { +type OrganizationSwitcherPropsWithoutCustomPages = Without & { organizationProfileProps?: Pick; }; diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index 3aee2f4d021..09b704b9b02 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -748,7 +748,18 @@ export type CreateOrganizationProps = RoutingOptions & { appearance?: CreateOrganizationTheme; }; -export type UserButtonProps = { +type UserProfileMode = 'modal' | 'navigation'; +type UserButtonProfileMode = + | { + userProfileUrl?: never; + userProfileMode?: Extract; + } + | { + userProfileUrl: string; + userProfileMode?: Extract; + }; + +export type UserButtonProps = UserButtonProfileMode & { /** * Controls if the username is displayed next to the trigger button */ @@ -766,11 +777,6 @@ export type UserButtonProps = { * This option applies to multi-session applications. */ afterMultiSessionSingleSignOutUrl?: string; - /** - * Full URL or path leading to the - * account management interface. - */ - userProfileUrl?: string; /** * Full URL or path to navigate on "Add another account" action. * Multi-session mode only. @@ -781,13 +787,6 @@ export type UserButtonProps = { * Multi-session mode only. */ afterSwitchSessionUrl?: string; - /** - * Controls whether clicking the "Manage your account" button will cause - * the UserProfile component to open as a modal, or if the browser will navigate - * to the `userProfileUrl` where UserProfile is mounted as a page. - * @default 'modal' - */ - userProfileMode?: 'modal' | 'navigation'; /** * Customisation options to fully match the Clerk components to your own brand. * These options serve as overrides and will be merged with the global `appearance` @@ -813,89 +812,74 @@ type PrimitiveKeys = { type LooseExtractedParams = `:${T}` | (string & NonNullable); -export type OrganizationSwitcherProps = { - /** +type OrganizationProfileMode = + | { organizationProfileUrl: string; organizationProfileMode?: 'navigation' } + | { organizationProfileUrl?: never; organizationProfileMode?: 'modal' }; + +type CreateOrganizationMode = + | { createOrganizationUrl: string; createOrganizationMode?: 'navigation' } + | { createOrganizationUrl?: never; createOrganizationMode?: 'modal' }; + +export type OrganizationSwitcherProps = CreateOrganizationMode & + OrganizationProfileMode & { + /** Controls the default state of the OrganizationSwitcher */ - defaultOpen?: boolean; - /** - * By default, users can switch between organization and their personal account. - * This option controls whether OrganizationSwitcher will include the user's personal account - * in the organization list. Setting this to `false` will hide the personal account entry, - * and users will only be able to switch between organizations. - * @default true - */ - hidePersonal?: boolean; - /** - * Full URL or path to navigate after a successful organization switch. - * @default undefined - * @deprecated use `afterSelectOrganizationUrl` or `afterSelectPersonalUrl` - */ - afterSwitchOrganizationUrl?: string; - /** - * Full URL or path to navigate after creating a new organization. - * @default undefined - */ - afterCreateOrganizationUrl?: - | ((organization: OrganizationResource) => string) - | LooseExtractedParams>; - /** - * Full URL or path to navigate after a successful organization selection. - * Accepts a function that returns URL or path - * @default undefined` - */ - afterSelectOrganizationUrl?: - | ((organization: OrganizationResource) => string) - | LooseExtractedParams>; + defaultOpen?: boolean; + /** + * By default, users can switch between organization and their personal account. + * This option controls whether OrganizationSwitcher will include the user's personal account + * in the organization list. Setting this to `false` will hide the personal account entry, + * and users will only be able to switch between organizations. + * @default true + */ + hidePersonal?: boolean; + /** + * Full URL or path to navigate after a successful organization switch. + * @default undefined + * @deprecated use `afterSelectOrganizationUrl` or `afterSelectPersonalUrl` + */ + afterSwitchOrganizationUrl?: string; + /** + * Full URL or path to navigate after creating a new organization. + * @default undefined + */ + afterCreateOrganizationUrl?: + | ((organization: OrganizationResource) => string) + | LooseExtractedParams>; + /** + * Full URL or path to navigate after a successful organization selection. + * Accepts a function that returns URL or path + * @default undefined` + */ + afterSelectOrganizationUrl?: + | ((organization: OrganizationResource) => string) + | LooseExtractedParams>; - /** - * Full URL or path to navigate after a successful selection of personal workspace. - * Accepts a function that returns URL or path - * @default undefined` - */ - afterSelectPersonalUrl?: ((user: UserResource) => string) | LooseExtractedParams>; - /** - * Full URL or path to navigate to after the user leaves the currently active organization. - * @default undefined - */ - afterLeaveOrganizationUrl?: string; - /** - * Controls whether clicking the "Manage organization" button will cause - * the OrganizationProfile component to open as a modal, or if the browser will navigate - * to the `organizationProfileUrl` where OrganizationProfile is mounted as a page. - * @default modal - */ - organizationProfileMode?: 'modal' | 'navigation'; - /** - * Controls whether clicking the "Create organization" button will cause - * the CreateOrganization component to open as a modal, or if the browser will navigate - * to the `createOrganizationUrl` where CreateOrganization is mounted as a page. - * @default modal - */ - createOrganizationMode?: 'modal' | 'navigation'; - /** - * Full URL or path where the component is mounted. - * @default undefined - */ - organizationProfileUrl?: string; - /** - * Full URL or path where the component is mounted. - * @default undefined - */ - createOrganizationUrl?: string; - /** - * Customisation options to fully match the Clerk components to your own brand. - * These options serve as overrides and will be merged with the global `appearance` - * prop of ClerkProvided (if one is provided) - */ - appearance?: OrganizationSwitcherTheme; + /** + * Full URL or path to navigate after a successful selection of personal workspace. + * Accepts a function that returns URL or path + * @default undefined` + */ + afterSelectPersonalUrl?: ((user: UserResource) => string) | LooseExtractedParams>; + /** + * Full URL or path to navigate to after the user leaves the currently active organization. + * @default undefined + */ + afterLeaveOrganizationUrl?: string; + /** + * Customisation options to fully match the Clerk components to your own brand. + * These options serve as overrides and will be merged with the global `appearance` + * prop of ClerkProvided (if one is provided) + */ + appearance?: OrganizationSwitcherTheme; - /* - * Specify options for the underlying component. - * e.g. - */ - organizationProfileProps?: Pick; -}; + /* + * Specify options for the underlying component. + * e.g. + */ + organizationProfileProps?: Pick; + }; export type OrganizationListProps = { /**