Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dull-stingrays-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': patch
---

Enhance `CreateOrganizationForm` by replacing `AvatarPreview` with an upload button to prevent layout shifts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import React from 'react';

import { useWizard, Wizard } from '../../common';
import { useCoreOrganization, useCoreOrganizationList } from '../../contexts';
import { ContentPage, Form, FormButtonContainer, SuccessPage, useCardState } from '../../elements';
import { QuestionMark } from '../../icons';
import { Icon } from '../../customizables';
import { ContentPage, Form, FormButtonContainer, IconButton, SuccessPage, useCardState } from '../../elements';
import { QuestionMark, Upload } from '../../icons';
import type { LocalizationKey } from '../../localization';
import { localizationKeys } from '../../localization';
import { createSlug, handleError, useFormControl } from '../../utils';
import { colors, createSlug, handleError, useFormControl } from '../../utils';
import { InviteMembersForm } from '../OrganizationProfile/InviteMembersForm';
import { InvitationsSentMessage } from '../OrganizationProfile/InviteMembersPage';
import { OrganizationProfileAvatarUploader } from '../OrganizationProfile/OrganizationProfileAvatarUploader';
Expand Down Expand Up @@ -121,6 +122,34 @@ export const CreateOrganizationForm = (props: CreateOrganizationFormProps) => {
organization={{ name: nameField.value }}
onAvatarChange={async file => await setFile(file)}
onAvatarRemove={file ? onAvatarRemove : null}
avatarPreviewPlaceholder={
<IconButton
variant='ghost'
colorScheme='neutral'
aria-label='Upload organization logo'
icon={
<Icon
size='md'
icon={Upload}
sx={theme => ({
transitionDuration: theme.transitionDuration.$controls,
})}
/>
}
sx={theme => ({
width: theme.sizes.$11,
height: theme.sizes.$11,
borderRadius: theme.radii.$md,
backgroundColor: theme.colors.$avatarBackground,
':hover': {
backgroundColor: colors.makeTransparent(theme.colors.$avatarBackground, 0.2),
svg: {
transform: 'scale(1.2)',
},
},
})}
/>
}
/>
<Form.ControlRow elementId={nameField.id}>
<Form.Control
Expand Down
11 changes: 9 additions & 2 deletions packages/clerk-js/src/ui/elements/AvatarUploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type AvatarUploaderProps = {
avatarPreview: React.ReactElement;
onAvatarChange: (file: File) => Promise<unknown>;
onAvatarRemove?: (() => void) | null;
avatarPreviewPlaceholder?: React.ReactElement | null;
};

export const fileToBase64 = (file: File): Promise<string> => {
Expand All @@ -27,7 +28,7 @@ export const AvatarUploader = (props: AvatarUploaderProps) => {
const [objectUrl, setObjectUrl] = React.useState<string>();
const card = useCardState();

const { onAvatarChange, onAvatarRemove, title, avatarPreview, ...rest } = props;
const { onAvatarChange, onAvatarRemove, title, avatarPreview, avatarPreviewPlaceholder, ...rest } = props;

const toggle = () => {
setShowUpload(!showUpload);
Expand All @@ -54,14 +55,20 @@ export const AvatarUploader = (props: AvatarUploaderProps) => {
return onAvatarRemove?.();
};

const previewElement = objectUrl
? React.cloneElement(avatarPreview, { imageUrl: objectUrl })
: avatarPreviewPlaceholder
? React.cloneElement(avatarPreviewPlaceholder, { onClick: toggle })
: avatarPreview;

return (
<Col gap={4}>
<Flex
gap={4}
align='center'
{...rest}
>
{objectUrl ? React.cloneElement(avatarPreview, { imageUrl: objectUrl }) : avatarPreview}
{previewElement}
<Col gap={1}>
<Text
localizationKey={title}
Expand Down
51 changes: 26 additions & 25 deletions packages/clerk-js/src/ui/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,42 @@
export { default as ArrowLeftIcon } from './arrow-left.svg';
export { default as ArrowRightIcon } from './arrow-right.svg';
export { default as AuthApp } from './auth-app.svg';
export { default as Clipboard } from './clipboard.svg';
export { default as Billing } from './billing.svg';
export { default as Caret } from './caret.svg';
export { default as ChatAltIcon } from './chat-alt.svg';
export { default as CheckCircle } from './check-circle.svg';
export { default as ExclamationTriangle } from './exclamation-triangle.svg';
export { default as Clipboard } from './clipboard.svg';
export { default as Close } from './close.svg';
export { default as CogFilled } from './cog-filled.svg';
export { default as DeviceLaptop } from './device-laptop.svg';
export { default as DeviceMobile } from './device-mobile.svg';
export { default as DotCircle } from './dot-circle-horizontal.svg';
export { default as Email } from './email.svg';
export { default as ExclamationCircle } from './exclamation-circle.svg';
export { default as ExclamationTriangle } from './exclamation-triangle.svg';
export { default as EyeSlash } from './eye-slash.svg';
export { default as Eye } from './eye.svg';
export { default as Folder } from './folder.svg';
export { default as InformationCircle } from './information-circle.svg';
export { default as LinkIcon } from './link.svg';
export { default as LockClosedIcon } from './lock-closed.svg';
export { default as LogoMark } from './logo-mark-new.svg';
export { default as MagnifyingGlass } from './magnifying-glass.svg';
export { default as Caret } from './caret.svg';
export { default as Menu } from './menu.svg';
export { default as Mobile } from './mobile-small.svg';
export { default as PencilEdit } from './pencil-edit.svg';
export { default as Pencil } from './pencil.svg';
export { default as Email } from './email.svg';
export { default as TickShield } from './tick-shield.svg';
export { default as SwitchArrows } from './switch-arrows.svg';
export { default as LogoMark } from './logo-mark-new.svg';
export { default as SignOut } from './signout.svg';
export { default as SignOutDouble } from './signout-double.svg';
export { default as Plus } from './plus.svg';
export { default as CogFilled } from './cog-filled.svg';
export { default as LinkIcon } from './link.svg';
export { default as LockClosedIcon } from './lock-closed.svg';
export { default as ChatAltIcon } from './chat-alt.svg';
export { default as QuestionMark } from './question-mark.svg';
export { default as RequestAuthIcon } from './request-auth.svg';
export { default as User } from './user.svg';
export { default as Mobile } from './mobile-small.svg';
export { default as Folder } from './folder.svg';
export { default as DeviceMobile } from './device-mobile.svg';
export { default as DeviceLaptop } from './device-laptop.svg';
export { default as Menu } from './menu.svg';
export { default as Eye } from './eye.svg';
export { default as DotCircle } from './dot-circle-horizontal.svg';
export { default as Selector } from './selector.svg';
export { default as SignOutDouble } from './signout-double.svg';
export { default as SignOut } from './signout.svg';
export { default as SwitchArrows } from './switch-arrows.svg';
export { default as ThreeDots } from './threeDots.svg';
export { default as TickShield } from './tick-shield.svg';
export { default as Times } from './times.svg';
export { default as Trash } from './trash.svg';
export { default as Upload } from './upload.svg';
export { default as User } from './user.svg';
export { default as UserAdd } from './userAdd.svg';
export { default as ThreeDots } from './threeDots.svg';
export { default as Billing } from './billing.svg';
export { default as Close } from './close.svg';
export { default as QuestionMark } from './question-mark.svg';
1 change: 1 addition & 0 deletions packages/clerk-js/src/ui/icons/upload.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.