diff --git a/.changeset/dull-stingrays-fix.md b/.changeset/dull-stingrays-fix.md new file mode 100644 index 00000000000..7c2e680bbab --- /dev/null +++ b/.changeset/dull-stingrays-fix.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-js': patch +--- + +Enhance `CreateOrganizationForm` by replacing `AvatarPreview` with an upload button to prevent layout shifts \ No newline at end of file diff --git a/packages/clerk-js/src/ui/components/CreateOrganization/CreateOrganizationForm.tsx b/packages/clerk-js/src/ui/components/CreateOrganization/CreateOrganizationForm.tsx index 9bd8725c142..9aadb1512b6 100644 --- a/packages/clerk-js/src/ui/components/CreateOrganization/CreateOrganizationForm.tsx +++ b/packages/clerk-js/src/ui/components/CreateOrganization/CreateOrganizationForm.tsx @@ -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'; @@ -121,6 +122,34 @@ export const CreateOrganizationForm = (props: CreateOrganizationFormProps) => { organization={{ name: nameField.value }} onAvatarChange={async file => await setFile(file)} onAvatarRemove={file ? onAvatarRemove : null} + avatarPreviewPlaceholder={ + ({ + 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)', + }, + }, + })} + /> + } /> Promise; onAvatarRemove?: (() => void) | null; + avatarPreviewPlaceholder?: React.ReactElement | null; }; export const fileToBase64 = (file: File): Promise => { @@ -27,7 +28,7 @@ export const AvatarUploader = (props: AvatarUploaderProps) => { const [objectUrl, setObjectUrl] = React.useState(); const card = useCardState(); - const { onAvatarChange, onAvatarRemove, title, avatarPreview, ...rest } = props; + const { onAvatarChange, onAvatarRemove, title, avatarPreview, avatarPreviewPlaceholder, ...rest } = props; const toggle = () => { setShowUpload(!showUpload); @@ -54,6 +55,12 @@ export const AvatarUploader = (props: AvatarUploaderProps) => { return onAvatarRemove?.(); }; + const previewElement = objectUrl + ? React.cloneElement(avatarPreview, { imageUrl: objectUrl }) + : avatarPreviewPlaceholder + ? React.cloneElement(avatarPreviewPlaceholder, { onClick: toggle }) + : avatarPreview; + return ( { align='center' {...rest} > - {objectUrl ? React.cloneElement(avatarPreview, { imageUrl: objectUrl }) : avatarPreview} + {previewElement} \ No newline at end of file