From 433b61166193178fcbba847da0845affabaf03ad Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 1 Nov 2024 13:35:32 -0400 Subject: [PATCH 01/12] feat(elements): Add `` component --- packages/elements/src/react/common/index.ts | 1 + packages/elements/src/react/common/link.tsx | 24 +++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 packages/elements/src/react/common/link.tsx diff --git a/packages/elements/src/react/common/index.ts b/packages/elements/src/react/common/index.ts index 72504ef7901..23898e39274 100644 --- a/packages/elements/src/react/common/index.ts +++ b/packages/elements/src/react/common/index.ts @@ -4,6 +4,7 @@ import 'client-only'; export { Field, FieldError, FieldState, GlobalError, Input, Label, Submit } from '~/react/common/form'; export { Connection, Icon } from '~/react/common/connections'; export { Loading } from '~/react/common/loading'; +export { Link } from '~/react/common/link'; export type { FormFieldErrorProps, diff --git a/packages/elements/src/react/common/link.tsx b/packages/elements/src/react/common/link.tsx new file mode 100644 index 00000000000..b398a7c4fae --- /dev/null +++ b/packages/elements/src/react/common/link.tsx @@ -0,0 +1,24 @@ +import { useClerkRouter } from '@clerk/shared/router'; +import { Slot } from '@radix-ui/react-slot'; + +export interface LinkProps extends React.ButtonHTMLAttributes { + asChild?: boolean; + href: string; +} + +export function Link({ asChild, href, ...rest }: LinkProps) { + const router = useClerkRouter(); + const Comp = asChild ? Slot : 'a'; + return ( + { + if (router) { + e.preventDefault(); + router.push(href); + } + }} + href={href} + {...rest} + /> + ); +} From 5d1dd72194a5801ca728b62889f3bccdb7d836ad Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 1 Nov 2024 13:52:39 -0400 Subject: [PATCH 02/12] allow reading child href --- packages/elements/src/react/common/link.tsx | 39 ++++++++++++++------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/packages/elements/src/react/common/link.tsx b/packages/elements/src/react/common/link.tsx index b398a7c4fae..b4f1bb85b0d 100644 --- a/packages/elements/src/react/common/link.tsx +++ b/packages/elements/src/react/common/link.tsx @@ -1,24 +1,39 @@ import { useClerkRouter } from '@clerk/shared/router'; -import { Slot } from '@radix-ui/react-slot'; +import React from 'react'; export interface LinkProps extends React.ButtonHTMLAttributes { asChild?: boolean; - href: string; + href?: string; } -export function Link({ asChild, href, ...rest }: LinkProps) { +export function Link({ asChild, href, children, ...rest }: LinkProps) { const router = useClerkRouter(); - const Comp = asChild ? Slot : 'a'; + + const handleClick = (e: React.MouseEvent) => { + if (router) { + e.preventDefault(); + const childHref = (React.isValidElement(children) && children.props.href) || href; + if (childHref) { + router.push(childHref); + } + } + }; + + if (asChild && React.isValidElement(children)) { + return React.cloneElement(children as React.ReactElement, { + onClick: handleClick, + href: (children as React.ReactElement).props.href || href, + ...rest, + }); + } + return ( - { - if (router) { - e.preventDefault(); - router.push(href); - } - }} + + > + {children} + ); } From 70034b3457a4d10ab5836724c6f12a2216b9e8ba Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 1 Nov 2024 15:58:20 -0400 Subject: [PATCH 03/12] improve --- packages/clerk-js/src/core/clerk.ts | 4 ++ packages/elements/src/react/common/link.tsx | 45 +++++++++++---------- packages/react/src/isomorphicClerk.ts | 5 +++ packages/shared/src/router/router.ts | 2 + packages/types/src/clerk.ts | 2 + 5 files changed, 36 insertions(+), 22 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 2da6fd302c4..d153b5b4855 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -257,6 +257,10 @@ export class Clerk implements ClerkInterface { return this.#options.standardBrowser || false; } + public getOption(key: K): ClerkOptions[K] { + return this.#options[key]; + } + public constructor(key: string, options?: DomainOrProxyUrl) { key = (key || '').trim(); diff --git a/packages/elements/src/react/common/link.tsx b/packages/elements/src/react/common/link.tsx index b4f1bb85b0d..d16810f30c9 100644 --- a/packages/elements/src/react/common/link.tsx +++ b/packages/elements/src/react/common/link.tsx @@ -1,36 +1,37 @@ +import { useClerk } from '@clerk/shared/react'; import { useClerkRouter } from '@clerk/shared/router'; +import type { ClerkOptions } from '@clerk/types'; import React from 'react'; -export interface LinkProps extends React.ButtonHTMLAttributes { - asChild?: boolean; - href?: string; +type Destination = 'sign-in' | 'sign-up'; +export interface LinkProps extends Omit, 'children'> { + navigate: Destination; + children: React.ReactNode | ((url: string) => React.ReactNode); } -export function Link({ asChild, href, children, ...rest }: LinkProps) { - const router = useClerkRouter(); +const paths: Record> = { + 'sign-in': 'signInUrl', + 'sign-up': 'signUpUrl', +}; - const handleClick = (e: React.MouseEvent) => { - if (router) { - e.preventDefault(); - const childHref = (React.isValidElement(children) && children.props.href) || href; - if (childHref) { - router.push(childHref); - } - } - }; +export function Link({ navigate, children, ...rest }: LinkProps) { + const router = useClerkRouter(); + const clerk = useClerk(); + const destiationUrl = router.makeDestinationUrlWithPreservedQueryParameters(clerk.getOption(paths[navigate])!); - if (asChild && React.isValidElement(children)) { - return React.cloneElement(children as React.ReactElement, { - onClick: handleClick, - href: (children as React.ReactElement).props.href || href, - ...rest, - }); + if (typeof children === 'function') { + return children(destiationUrl); } return ( { + if (router) { + e.preventDefault(); + router.push(destiationUrl); + } + }} + href={destiationUrl} {...rest} > {children} diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index 509e69550b0..d645379fd8d 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -11,6 +11,7 @@ import type { AuthenticateWithMetamaskParams, Clerk, ClerkAuthenticateWithWeb3Params, + ClerkOptions, ClientResource, CreateOrganizationParams, CreateOrganizationProps, @@ -241,6 +242,10 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { return this.#proxyUrl || ''; } + public getOption(key: K): ClerkOptions[K] | undefined { + return this.clerkjs?.getOption(key); + } + constructor(options: IsomorphicClerkOptions) { const { Clerk = null, publishableKey } = options || {}; this.#publishableKey = publishableKey; diff --git a/packages/shared/src/router/router.ts b/packages/shared/src/router/router.ts index e8c16969228..69cd41eb159 100644 --- a/packages/shared/src/router/router.ts +++ b/packages/shared/src/router/router.ts @@ -8,6 +8,7 @@ export const PRESERVED_QUERYSTRING_PARAMS = ['after_sign_in_url', 'after_sign_up * Internal Clerk router, used by Clerk components to interact with the host's router. */ export type ClerkRouter = { + makeDestinationUrlWithPreservedQueryParameters: (path: string) => string; /** * The basePath the router is currently mounted on. */ @@ -132,6 +133,7 @@ export function createClerkRouter(router: ClerkHostRouter, basePath: string = '/ } return { + makeDestinationUrlWithPreservedQueryParameters, child, match, mode: router.mode, diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index 4bf226b257b..fe663c4b0e0 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -104,6 +104,8 @@ export interface Clerk { */ loaded: boolean; + getOption(key: K): ClerkOptions[K]; + frontendApi: string; /** Clerk Publishable Key string. */ From 2a5663552f940d3d717a5f996c4db628447b190b Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 1 Nov 2024 16:12:04 -0400 Subject: [PATCH 04/12] add changeset --- .changeset/short-mails-brush.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .changeset/short-mails-brush.md diff --git a/.changeset/short-mails-brush.md b/.changeset/short-mails-brush.md new file mode 100644 index 00000000000..93eb7405fde --- /dev/null +++ b/.changeset/short-mails-brush.md @@ -0,0 +1,24 @@ +--- +'@clerk/clerk-js': patch +'@clerk/elements': patch +'@clerk/shared': patch +'@clerk/clerk-react': patch +'@clerk/types': patch +--- + +Add Elements `` component. + +```tsx +import * as Clerk from '@clerk/elements/common'; +import NextLink from 'next/link'; + +function SignInPage() { + return ( + <> + Sign up + + {url => Sign up} + + ); +} +``` From b168b08a3c6dad5d0b3d1d6cc3c690ef2b569f08 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 1 Nov 2024 16:12:46 -0400 Subject: [PATCH 05/12] Update short-mails-brush.md --- .changeset/short-mails-brush.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.changeset/short-mails-brush.md b/.changeset/short-mails-brush.md index 93eb7405fde..6ca8d0dfe7b 100644 --- a/.changeset/short-mails-brush.md +++ b/.changeset/short-mails-brush.md @@ -1,9 +1,5 @@ --- -'@clerk/clerk-js': patch '@clerk/elements': patch -'@clerk/shared': patch -'@clerk/clerk-react': patch -'@clerk/types': patch --- Add Elements `` component. From 933434ea1fadb1326d165d619d6a983fc678fbaf Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 1 Nov 2024 16:14:18 -0400 Subject: [PATCH 06/12] add changeset --- .changeset/weak-hornets-hunt.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/weak-hornets-hunt.md diff --git a/.changeset/weak-hornets-hunt.md b/.changeset/weak-hornets-hunt.md new file mode 100644 index 00000000000..257882c004d --- /dev/null +++ b/.changeset/weak-hornets-hunt.md @@ -0,0 +1,8 @@ +--- +'@clerk/clerk-js': patch +'@clerk/shared': patch +'@clerk/clerk-react': patch +'@clerk/types': patch +--- + +Expose internal `getOption` method from Clerk. From 5ada8c8e80a6ccbcb232d20a01f6551eee95f1db Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 1 Nov 2024 16:16:30 -0400 Subject: [PATCH 07/12] mark as internal --- packages/clerk-js/src/core/clerk.ts | 2 +- packages/elements/src/react/common/link.tsx | 4 +++- packages/react/src/isomorphicClerk.ts | 4 ++-- packages/types/src/clerk.ts | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index d153b5b4855..45dc5a802df 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -257,7 +257,7 @@ export class Clerk implements ClerkInterface { return this.#options.standardBrowser || false; } - public getOption(key: K): ClerkOptions[K] { + public __internal_getOption(key: K): ClerkOptions[K] { return this.#options[key]; } diff --git a/packages/elements/src/react/common/link.tsx b/packages/elements/src/react/common/link.tsx index d16810f30c9..a95b5ddd1bf 100644 --- a/packages/elements/src/react/common/link.tsx +++ b/packages/elements/src/react/common/link.tsx @@ -17,7 +17,9 @@ const paths: Record(key: K): ClerkOptions[K] | undefined { - return this.clerkjs?.getOption(key); + public __internal_getOption(key: K): ClerkOptions[K] | undefined { + return this.clerkjs?.__internal_getOption(key); } constructor(options: IsomorphicClerkOptions) { diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index fe663c4b0e0..4c6d067cddc 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -104,7 +104,7 @@ export interface Clerk { */ loaded: boolean; - getOption(key: K): ClerkOptions[K]; + __internal_getOption(key: K): ClerkOptions[K]; frontendApi: string; From 3c41f08e10c348d3eb00f86ea9222e544cfad3ff Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 1 Nov 2024 16:19:06 -0400 Subject: [PATCH 08/12] Update weak-hornets-hunt.md --- .changeset/weak-hornets-hunt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/weak-hornets-hunt.md b/.changeset/weak-hornets-hunt.md index 257882c004d..b15f996d895 100644 --- a/.changeset/weak-hornets-hunt.md +++ b/.changeset/weak-hornets-hunt.md @@ -5,4 +5,4 @@ '@clerk/types': patch --- -Expose internal `getOption` method from Clerk. +Expose internal `__internal_getOption` method from Clerk. From e60ce32972c4b9369555b8c63e2903b2fc9bb631 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 1 Nov 2024 16:47:58 -0400 Subject: [PATCH 09/12] Update packages/elements/src/react/common/link.tsx Co-authored-by: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> --- packages/elements/src/react/common/link.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/elements/src/react/common/link.tsx b/packages/elements/src/react/common/link.tsx index a95b5ddd1bf..27b1053a9a5 100644 --- a/packages/elements/src/react/common/link.tsx +++ b/packages/elements/src/react/common/link.tsx @@ -17,7 +17,7 @@ const paths: Record Date: Fri, 1 Nov 2024 16:50:34 -0400 Subject: [PATCH 10/12] fix variable --- packages/elements/src/react/common/link.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/elements/src/react/common/link.tsx b/packages/elements/src/react/common/link.tsx index 27b1053a9a5..e59492d906c 100644 --- a/packages/elements/src/react/common/link.tsx +++ b/packages/elements/src/react/common/link.tsx @@ -22,7 +22,7 @@ export function Link({ navigate, children, ...rest }: LinkProps) { ); if (typeof children === 'function') { - return children(destiationUrl); + return children(destinationUrl); } return ( @@ -30,10 +30,10 @@ export function Link({ navigate, children, ...rest }: LinkProps) { onClick={e => { if (router) { e.preventDefault(); - router.push(destiationUrl); + router.push(destinationUrl); } }} - href={destiationUrl} + href={destinationUrl} {...rest} > {children} From cbb6952272bb1772da0c81e765cadc5f96fb854e Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 1 Nov 2024 16:58:45 -0400 Subject: [PATCH 11/12] update type --- packages/elements/src/react/common/link.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/elements/src/react/common/link.tsx b/packages/elements/src/react/common/link.tsx index e59492d906c..8e27e7561a0 100644 --- a/packages/elements/src/react/common/link.tsx +++ b/packages/elements/src/react/common/link.tsx @@ -6,7 +6,7 @@ import React from 'react'; type Destination = 'sign-in' | 'sign-up'; export interface LinkProps extends Omit, 'children'> { navigate: Destination; - children: React.ReactNode | ((url: string) => React.ReactNode); + children: React.ReactNode | ((props: { url: string }) => React.ReactNode); } const paths: Record> = { @@ -22,7 +22,7 @@ export function Link({ navigate, children, ...rest }: LinkProps) { ); if (typeof children === 'function') { - return children(destinationUrl); + return children({ url: destinationUrl }); } return ( From 409c69b9b3f950967f11d0ca682eb536aa406684 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 1 Nov 2024 18:24:50 -0400 Subject: [PATCH 12/12] doc block comment --- packages/elements/src/react/common/link.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/elements/src/react/common/link.tsx b/packages/elements/src/react/common/link.tsx index 8e27e7561a0..74d2b6c9ed7 100644 --- a/packages/elements/src/react/common/link.tsx +++ b/packages/elements/src/react/common/link.tsx @@ -14,6 +14,24 @@ const paths: Record` component is used to navigate between sign-in and sign-up flows. + * + * @param {Destination} navigate - The destination to navigate to. + * + * @example + * ```tsx + * Sign in + * ``` + * @example + * ```tsx + * + * {({ url }) => ( + * Sign in + * )} + * + */ + export function Link({ navigate, children, ...rest }: LinkProps) { const router = useClerkRouter(); const clerk = useClerk();