Skip to content
Merged
8 changes: 8 additions & 0 deletions .changeset/few-news-impress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@clerk/astro': patch
'@clerk/clerk-react': patch
'@clerk/types': patch
'@clerk/vue': patch
---

Add the ability to specify an appearance for modal component usages.
5 changes: 5 additions & 0 deletions .changeset/giant-fans-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/types': minor
---

Extract common button component props.
78 changes: 40 additions & 38 deletions packages/astro/src/react/SignInButton.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,48 @@
import type { SignInProps } from '@clerk/types';
import type { SignInButtonProps, SignInProps } from '@clerk/types';
import React from 'react';

import type { SignInButtonProps } from './types';
import { assertSingleChild, normalizeWithDefaultValue, safeExecute, withClerk, type WithClerkProp } from './utils';

export type { SignInButtonProps };

export const SignInButton = withClerk(({ clerk, children, ...props }: WithClerkProp<SignInButtonProps>) => {
const { signUpFallbackRedirectUrl, forceRedirectUrl, fallbackRedirectUrl, signUpForceRedirectUrl, mode, ...rest } =
props;
children = normalizeWithDefaultValue(children, 'Sign in');
const child = assertSingleChild(children)('SignInButton');

const clickHandler = () => {
const opts: SignInProps = {
forceRedirectUrl,
fallbackRedirectUrl,
signUpFallbackRedirectUrl,
signUpForceRedirectUrl,
export const SignInButton = withClerk(
({ clerk, children, ...props }: WithClerkProp<React.PropsWithChildren<SignInButtonProps>>) => {
const { signUpFallbackRedirectUrl, forceRedirectUrl, fallbackRedirectUrl, signUpForceRedirectUrl, mode, ...rest } =
props;
children = normalizeWithDefaultValue(children, 'Sign in');
const child = assertSingleChild(children)('SignInButton');

const clickHandler = () => {
const opts: SignInProps = {
forceRedirectUrl,
fallbackRedirectUrl,
signUpFallbackRedirectUrl,
signUpForceRedirectUrl,
};

if (!clerk) {
return;
}

if (mode === 'modal') {
return clerk.openSignIn({ ...opts, appearance: props.appearance });
}
return clerk.redirectToSignIn({
...opts,
signInFallbackRedirectUrl: fallbackRedirectUrl,
signInForceRedirectUrl: forceRedirectUrl,
});
};

if (!clerk) {
return;
}

if (mode === 'modal') {
return clerk.openSignIn(opts);
}
return clerk.redirectToSignIn({
...opts,
signInFallbackRedirectUrl: fallbackRedirectUrl,
signInForceRedirectUrl: forceRedirectUrl,
});
};

const wrappedChildClickHandler: React.MouseEventHandler = async e => {
if (child && typeof child === 'object' && 'props' in child) {
await safeExecute(child.props.onClick)(e);
}
return clickHandler();
};

const childProps = { ...rest, onClick: wrappedChildClickHandler };
return React.cloneElement(child as React.ReactElement<unknown>, childProps);
}, 'SignInButton');
const wrappedChildClickHandler: React.MouseEventHandler = async e => {
if (child && typeof child === 'object' && 'props' in child) {
await safeExecute(child.props.onClick)(e);
}
return clickHandler();
};

const childProps = { ...rest, onClick: wrappedChildClickHandler };
return React.cloneElement(child as React.ReactElement<unknown>, childProps);
},
'SignInButton',
);
88 changes: 45 additions & 43 deletions packages/astro/src/react/SignUpButton.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,58 @@
import type { SignUpProps } from '@clerk/types';
import type { SignUpButtonProps, SignUpProps } from '@clerk/types';
import React from 'react';

import type { SignUpButtonProps } from './types';
import { assertSingleChild, normalizeWithDefaultValue, safeExecute, withClerk, type WithClerkProp } from './utils';

export type { SignUpButtonProps };

export const SignUpButton = withClerk(({ clerk, children, ...props }: WithClerkProp<SignUpButtonProps>) => {
const {
fallbackRedirectUrl,
forceRedirectUrl,
signInFallbackRedirectUrl,
signInForceRedirectUrl,
mode,
unsafeMetadata,
...rest
} = props;

children = normalizeWithDefaultValue(children, 'Sign up');
const child = assertSingleChild(children)('SignUpButton');

const clickHandler = () => {
const opts: SignUpProps = {
export const SignUpButton = withClerk(
({ clerk, children, ...props }: WithClerkProp<React.PropsWithChildren<SignUpButtonProps>>) => {
const {
fallbackRedirectUrl,
forceRedirectUrl,
signInFallbackRedirectUrl,
signInForceRedirectUrl,
mode,
unsafeMetadata,
...rest
} = props;

children = normalizeWithDefaultValue(children, 'Sign up');
const child = assertSingleChild(children)('SignUpButton');

const clickHandler = () => {
const opts: SignUpProps = {
fallbackRedirectUrl,
forceRedirectUrl,
signInFallbackRedirectUrl,
signInForceRedirectUrl,
unsafeMetadata,
};

if (!clerk) {
return;
}

if (mode === 'modal') {
return clerk.openSignUp({ ...opts, appearance: props.appearance });
}

return clerk.redirectToSignUp({
...opts,
signUpFallbackRedirectUrl: fallbackRedirectUrl,
signUpForceRedirectUrl: forceRedirectUrl,
});
};

if (!clerk) {
return;
}

if (mode === 'modal') {
return clerk.openSignUp(opts);
}

return clerk.redirectToSignUp({
...opts,
signUpFallbackRedirectUrl: fallbackRedirectUrl,
signUpForceRedirectUrl: forceRedirectUrl,
});
};

const wrappedChildClickHandler: React.MouseEventHandler = async e => {
if (child && typeof child === 'object' && 'props' in child) {
await safeExecute(child.props.onClick)(e);
}
return clickHandler();
};

const childProps = { ...rest, onClick: wrappedChildClickHandler };
return React.cloneElement(child as React.ReactElement<unknown>, childProps);
}, 'SignUpButton');
const wrappedChildClickHandler: React.MouseEventHandler = async e => {
if (child && typeof child === 'object' && 'props' in child) {
await safeExecute(child.props.onClick)(e);
}
return clickHandler();
};

const childProps = { ...rest, onClick: wrappedChildClickHandler };
return React.cloneElement(child as React.ReactElement<unknown>, childProps);
},
'SignUpButton',
);
24 changes: 5 additions & 19 deletions packages/astro/src/react/types.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
import type { SignInProps, SignUpProps } from '@clerk/types';
import type { SignInButtonProps as _SignInButtonProps, SignUpButtonProps as _SignUpButtonProps } from '@clerk/types';

// TODO-SHARED: Duplicate from @clerk/clerk-react
type ButtonProps = {
mode?: 'redirect' | 'modal';
export type SignInButtonProps = _SignInButtonProps & {
children?: React.ReactNode;
};

// TODO-SHARED: Duplicate from @clerk/clerk-react
export type SignInButtonProps = ButtonProps &
Pick<
SignInProps,
'fallbackRedirectUrl' | 'forceRedirectUrl' | 'signUpForceRedirectUrl' | 'signUpFallbackRedirectUrl'
>;

// TODO-SHARED: Duplicate from @clerk/clerk-react
export type SignUpButtonProps = {
unsafeMetadata?: SignUpUnsafeMetadata;
} & ButtonProps &
Pick<
SignUpProps,
'fallbackRedirectUrl' | 'forceRedirectUrl' | 'signInForceRedirectUrl' | 'signInFallbackRedirectUrl'
>;
export type SignUpButtonProps = _SignUpButtonProps & {
children?: React.ReactNode;
};
8 changes: 4 additions & 4 deletions packages/react/src/components/SignInButton.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { SignInProps } from '@clerk/types';
import type { SignInButtonProps, SignInProps } from '@clerk/types';
import React from 'react';

import type { SignInButtonProps, WithClerkProp } from '../types';
import type { WithClerkProp } from '../types';
import { assertSingleChild, normalizeWithDefaultValue, safeExecute } from '../utils';
import { withClerk } from './withClerk';

export const SignInButton = withClerk(
({ clerk, children, ...props }: WithClerkProp<SignInButtonProps>) => {
({ clerk, children, ...props }: WithClerkProp<React.PropsWithChildren<SignInButtonProps>>) => {
const {
signUpFallbackRedirectUrl,
forceRedirectUrl,
Expand All @@ -31,7 +31,7 @@ export const SignInButton = withClerk(
};

if (mode === 'modal') {
return clerk.openSignIn(opts);
return clerk.openSignIn({ ...opts, appearance: props.appearance });
}
return clerk.redirectToSignIn({
...opts,
Expand Down
8 changes: 4 additions & 4 deletions packages/react/src/components/SignUpButton.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { SignUpProps } from '@clerk/types';
import type { SignUpButtonProps, SignUpProps } from '@clerk/types';
import React from 'react';

import type { SignUpButtonProps, WithClerkProp } from '../types';
import type { WithClerkProp } from '../types';
import { assertSingleChild, normalizeWithDefaultValue, safeExecute } from '../utils';
import { withClerk } from './withClerk';

export const SignUpButton = withClerk(
({ clerk, children, ...props }: WithClerkProp<SignUpButtonProps>) => {
({ clerk, children, ...props }: WithClerkProp<React.PropsWithChildren<SignUpButtonProps>>) => {
const {
fallbackRedirectUrl,
forceRedirectUrl,
Expand All @@ -32,7 +32,7 @@ export const SignUpButton = withClerk(
};

if (mode === 'modal') {
return clerk.openSignUp(opts);
return clerk.openSignUp({ ...opts, appearance: props.appearance });
}

return clerk.redirectToSignUp({
Expand Down
31 changes: 2 additions & 29 deletions packages/react/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import type {
LoadedClerk,
MultiDomainAndOrProxy,
RedirectUrlProp,
SignInProps,
SignInRedirectOptions,
SignUpProps,
SignUpRedirectOptions,
Without,
} from '@clerk/types';
Expand Down Expand Up @@ -109,35 +107,10 @@ export type ClerkProp =
| undefined
| null;

type ButtonProps = {
export type SignInWithMetamaskButtonProps = {
mode?: 'redirect' | 'modal';
children?: React.ReactNode;
};

export type SignInButtonProps = ButtonProps &
Pick<
SignInProps,
| 'fallbackRedirectUrl'
| 'forceRedirectUrl'
| 'signUpForceRedirectUrl'
| 'signUpFallbackRedirectUrl'
| 'initialValues'
| 'withSignUp'
>;

export type SignUpButtonProps = {
unsafeMetadata?: SignUpUnsafeMetadata;
} & ButtonProps &
Pick<
SignUpProps,
| 'fallbackRedirectUrl'
| 'forceRedirectUrl'
| 'signInForceRedirectUrl'
| 'signInFallbackRedirectUrl'
| 'initialValues'
>;

export type SignInWithMetamaskButtonProps = ButtonProps & RedirectUrlProp;
} & RedirectUrlProp;

export type RedirectToSignInProps = SignInRedirectOptions;
export type RedirectToSignUpProps = SignUpRedirectOptions;
Expand Down
34 changes: 34 additions & 0 deletions packages/types/src/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1388,6 +1388,40 @@ export interface HandleEmailLinkVerificationParams {
onVerifiedOnOtherDevice?: () => void;
}

type ButtonPropsModal<T extends SignInProps | SignUpProps> = {
mode: 'modal';
appearance?: T['appearance'];
};

type ButtonPropsRedirect = {
mode?: 'redirect';
};

type ButtonProps<T extends SignInProps | SignUpProps> = ButtonPropsModal<T> | ButtonPropsRedirect;

export type SignInButtonProps = ButtonProps<SignInProps> &
Pick<
SignInProps,
| 'fallbackRedirectUrl'
| 'forceRedirectUrl'
| 'signUpForceRedirectUrl'
| 'signUpFallbackRedirectUrl'
| 'initialValues'
| 'withSignUp'
>;

export type SignUpButtonProps = {
unsafeMetadata?: SignUpUnsafeMetadata;
} & ButtonProps<SignUpProps> &
Pick<
SignUpProps,
| 'fallbackRedirectUrl'
| 'forceRedirectUrl'
| 'signInForceRedirectUrl'
| 'signInFallbackRedirectUrl'
| 'initialValues'
>;

export type CreateOrganizationInvitationParams = {
emailAddress: string;
role: OrganizationCustomRoleKey;
Expand Down
Loading