Skip to content
64 changes: 32 additions & 32 deletions packages/clerk-js/src/ui/components/OAuthConsent/OAuthConsent.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useUser } from '@clerk/shared/react';
import type { ComponentProps } from 'react';
import { useState } from 'react';

import { useEnvironment, useOAuthConsentContext } from '@/ui/contexts';
import { Box, Button, Flex, Flow, Grid, Icon, Text } from '@/ui/customizables';
import { ApplicationLogo } from '@/ui/elements/ApplicationLogo';
import { Avatar } from '@/ui/elements/Avatar';
import { Card } from '@/ui/elements/Card';
import { withCardStateProvider } from '@/ui/elements/contexts';
import { Header } from '@/ui/elements/Header';
Expand Down Expand Up @@ -42,13 +42,13 @@ export function OAuthConsentInternal() {
{/* both have avatars */}
{oAuthApplicationLogoUrl && logoImageUrl && (
<ConnectionHeader>
<Avatar
imageUrl={oAuthApplicationLogoUrl}
size={t => t.space.$12}
rounded={false}
/>
<ConnectionItem justify='end'>
<ApplicationLogo src={oAuthApplicationLogoUrl} />
</ConnectionItem>
<ConnectionSeparator />
<ApplicationLogo />
<ConnectionItem justify='start'>
<ApplicationLogo />
</ConnectionItem>
</ConnectionHeader>
)}
{/* only OAuth app has an avatar */}
Expand All @@ -59,11 +59,7 @@ export function OAuthConsentInternal() {
position: 'relative',
}}
>
<Avatar
imageUrl={oAuthApplicationLogoUrl}
size={t => t.space.$12}
rounded={false}
/>
<ApplicationLogo src={oAuthApplicationLogoUrl} />
<ConnectionIcon
size='sm'
sx={t => ({
Expand All @@ -77,31 +73,21 @@ export function OAuthConsentInternal() {
)}
{/* only Clerk application has an avatar */}
{!oAuthApplicationLogoUrl && logoImageUrl && (
<Flex
justify='center'
align='center'
gap={4}
sx={t => ({
marginBlockEnd: t.space.$6,
})}
>
<ConnectionIcon />
<ConnectionHeader>
<ConnectionItem justify='end'>
<ConnectionIcon />
</ConnectionItem>
<ConnectionSeparator />
<ApplicationLogo />
</Flex>
<ConnectionItem justify='start'>
<ApplicationLogo />
</ConnectionItem>
</ConnectionHeader>
)}
{/* no avatars */}
{!oAuthApplicationLogoUrl && !logoImageUrl && (
<Flex
justify='center'
align='center'
gap={4}
sx={t => ({
marginBlockEnd: t.space.$6,
})}
>
<ConnectionHeader>
<ConnectionIcon />
</Flex>
</ConnectionHeader>
)}
<Header.Title localizationKey={oAuthApplicationName} />
<Header.Subtitle
Expand Down Expand Up @@ -320,6 +306,20 @@ function ConnectionHeader({ children }: { children: React.ReactNode }) {
);
}

function ConnectionItem({ children, ...props }: ComponentProps<typeof Flex>) {
return (
<Flex
{...props}
sx={{
flex: 1,
...(props.sx || {}),
}}
>
{children}
</Flex>
);
}

function ConnectionIcon({ size = 'md', sx }: { size?: 'sm' | 'md'; sx?: ThemableCssProp }) {
const scale: ThemableCssProp = t => {
const value = size === 'sm' ? t.space.$6 : t.space.$12;
Expand Down
55 changes: 43 additions & 12 deletions packages/clerk-js/src/ui/elements/ApplicationLogo.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { isExternalUrl } from '@clerk/shared/url';
import React from 'react';

import { useEnvironment } from '../contexts';
import { descriptors, Flex, Image, useAppearance } from '../customizables';
import type { PropsOfComponent } from '../styledSystem';
import { Link } from '../primitives';
import type { InternalTheme, PropsOfComponent } from '../styledSystem';
import { common } from '../styledSystem';
import { RouterLink } from './RouterLink';

const getContainerHeightForImageRatio = (imageRef: React.RefObject<HTMLImageElement>, width: string) => {
Expand All @@ -21,15 +24,24 @@ const getContainerHeightForImageRatio = (imageRef: React.RefObject<HTMLImageElem
return width;
};

type ApplicationLogoProps = PropsOfComponent<typeof Flex>;
type ApplicationLogoProps = PropsOfComponent<typeof Flex> & {
/**
* The URL of the image to display.
*/
src?: string;
/**
* The URL to navigate to when the logo is clicked.
*/
href?: string;
};

export const ApplicationLogo = (props: ApplicationLogoProps) => {
const imageRef = React.useRef<HTMLImageElement>(null);
const [loaded, setLoaded] = React.useState(false);
const { logoImageUrl, applicationName, homeUrl } = useEnvironment().displayConfig;
const { parsedLayout } = useAppearance();
const imageSrc = parsedLayout.logoImageUrl || logoImageUrl;
const logoUrl = parsedLayout.logoLinkUrl || homeUrl;
const imageSrc = props.src || parsedLayout.logoImageUrl || logoImageUrl;
const logoUrl = props.href || parsedLayout.logoLinkUrl || homeUrl;

if (!imageSrc) {
return null;
Expand Down Expand Up @@ -65,14 +77,33 @@ export const ApplicationLogo = (props: ApplicationLogoProps) => {
]}
>
{logoUrl ? (
<RouterLink
sx={{
justifyContent: 'center',
}}
to={logoUrl}
>
{image}
</RouterLink>
isExternalUrl(logoUrl) ? (
<Link
href={logoUrl}
isExternal
css={(theme: InternalTheme) => ({
'&:focus': {
...common.focusRing(theme),
outline: 'none',
},
})}
>
{image}
</Link>
) : (
<RouterLink
sx={theme => ({
justifyContent: 'center',
'&:focus': {
...common.focusRing(theme),
outline: 'none',
},
})}
to={logoUrl}
>
{image}
</RouterLink>
)
) : (
image
)}
Expand Down
4 changes: 4 additions & 0 deletions packages/shared/src/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,7 @@ export function joinURL(base: string, ...input: string[]): string {
// Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3
const ABSOLUTE_URL_REGEX = /^[a-zA-Z][a-zA-Z\d+\-.]*?:/;
export const isAbsoluteUrl = (url: string) => ABSOLUTE_URL_REGEX.test(url);

export const isExternalUrl = (url: string) => {
return isAbsoluteUrl(url) && !url.startsWith(window.location.origin);
};
Loading