mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Added cover image interaction to user details
refs. https://github.com/TryGhost/Team/issues/3351
This commit is contained in:
parent
3090fc6067
commit
82b56c319e
6 changed files with 50 additions and 23 deletions
|
@ -9,7 +9,7 @@ export interface FileUploadProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
onUpload: (file: File) => void;
|
onUpload: (file: File) => void;
|
||||||
style: {}
|
style?: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FileUpload: React.FC<FileUploadProps> = ({id, onUpload, children, style, ...props}) => {
|
const FileUpload: React.FC<FileUploadProps> = ({id, onUpload, children, style, ...props}) => {
|
||||||
|
|
|
@ -15,7 +15,7 @@ type Story = StoryObj<typeof ImageUpload>;
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
args: {
|
args: {
|
||||||
id: 'image-upload-test',
|
id: 'image-upload-test',
|
||||||
label: 'Upload image',
|
children: 'Upload image',
|
||||||
onUpload: (file: File) => {
|
onUpload: (file: File) => {
|
||||||
alert(`You're uploading: ${file.name}`);
|
alert(`You're uploading: ${file.name}`);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ export const Default: Story = {
|
||||||
export const Resized: Story = {
|
export const Resized: Story = {
|
||||||
args: {
|
args: {
|
||||||
id: 'image-upload-test',
|
id: 'image-upload-test',
|
||||||
label: 'Upload image',
|
children: 'Upload image',
|
||||||
width: '480px',
|
width: '480px',
|
||||||
height: '320px',
|
height: '320px',
|
||||||
onUpload: (file: File) => {
|
onUpload: (file: File) => {
|
||||||
|
@ -37,7 +37,7 @@ export const Resized: Story = {
|
||||||
export const ImageUploaded: Story = {
|
export const ImageUploaded: Story = {
|
||||||
args: {
|
args: {
|
||||||
id: 'image-upload-test',
|
id: 'image-upload-test',
|
||||||
label: 'Upload image',
|
children: 'Upload image',
|
||||||
width: '480px',
|
width: '480px',
|
||||||
height: '320px',
|
height: '320px',
|
||||||
imageURL: 'https://images.unsplash.com/photo-1685374156924-5230519f4ab3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wxMTc3M3wwfDF8YWxsfDI1fHx8fHx8Mnx8MTY4NTYzNzE3M3w&ixlib=rb-4.0.3&q=80&w=2000',
|
imageURL: 'https://images.unsplash.com/photo-1685374156924-5230519f4ab3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wxMTc3M3wwfDF8YWxsfDI1fHx8fHx8Mnx8MTY4NTYzNzE3M3w&ixlib=rb-4.0.3&q=80&w=2000',
|
||||||
|
|
|
@ -4,44 +4,52 @@ import React from 'react';
|
||||||
|
|
||||||
interface ImageUploadProps {
|
interface ImageUploadProps {
|
||||||
id: string;
|
id: string;
|
||||||
label: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
width?: string;
|
width?: string;
|
||||||
height?: string;
|
height?: string;
|
||||||
imageURL?: string;
|
imageURL?: string;
|
||||||
|
imageClassName?: string;
|
||||||
|
fileUploadClassName?: string;
|
||||||
|
deleteButtonClassName?: string;
|
||||||
|
deleteButtonContent?: React.ReactNode;
|
||||||
onUpload: (file: File) => void;
|
onUpload: (file: File) => void;
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImageUpload: React.FC<ImageUploadProps> = ({
|
const ImageUpload: React.FC<ImageUploadProps> = ({
|
||||||
id,
|
id,
|
||||||
label,
|
children,
|
||||||
width,
|
width,
|
||||||
height = '120px',
|
height = '120px',
|
||||||
imageURL,
|
imageURL,
|
||||||
|
imageClassName = 'group relative bg-cover',
|
||||||
|
fileUploadClassName = 'flex cursor-pointer items-center justify-center rounded border border-grey-100 bg-grey-75 p-3 text-sm font-semibold text-grey-800 hover:text-black',
|
||||||
|
deleteButtonClassName = 'invisible absolute right-4 top-4 flex h-8 w-8 cursor-pointer items-center justify-center rounded bg-[rgba(0,0,0,0.75)] text-white hover:bg-black group-hover:!visible',
|
||||||
|
deleteButtonContent = <Icon color='white' name='trash' size='sm' />,
|
||||||
onUpload,
|
onUpload,
|
||||||
onDelete
|
onDelete
|
||||||
}) => {
|
}) => {
|
||||||
if (imageURL) {
|
if (imageURL) {
|
||||||
return (
|
return (
|
||||||
<div className='group relative bg-cover' style={{
|
<div className={imageClassName} style={{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
backgroundImage: `url(${imageURL})`
|
backgroundImage: `url(${imageURL})`
|
||||||
}}>
|
}}>
|
||||||
<button className='invisible absolute right-4 top-4 flex h-8 w-8 cursor-pointer items-center justify-center rounded bg-[rgba(0,0,0,0.75)] text-white hover:bg-black group-hover:!visible' type='button' onClick={onDelete}>
|
<button className={deleteButtonClassName} type='button' onClick={onDelete}>
|
||||||
<Icon color='white' name='trash' size='sm' />
|
{deleteButtonContent}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<FileUpload className={`flex cursor-pointer items-center justify-center rounded border border-grey-100 bg-grey-75 p-3 text-sm font-semibold text-grey-800 hover:text-black`} id={id} style={
|
<FileUpload className={fileUploadClassName} id={id} style={
|
||||||
{
|
{
|
||||||
width: width,
|
width: width,
|
||||||
height: height
|
height: height
|
||||||
}
|
}
|
||||||
} onUpload={onUpload}>
|
} onUpload={onUpload}>
|
||||||
{label}
|
{children}
|
||||||
</FileUpload>
|
</FileUpload>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,10 +51,11 @@ const Facebook: React.FC = () => {
|
||||||
height='200px'
|
height='200px'
|
||||||
id='twitter-image'
|
id='twitter-image'
|
||||||
imageURL={facebookImage}
|
imageURL={facebookImage}
|
||||||
label='Upload Facebook image'
|
|
||||||
onDelete={handleImageDelete}
|
onDelete={handleImageDelete}
|
||||||
onUpload={handleImageUpload}
|
onUpload={handleImageUpload}
|
||||||
/>
|
>
|
||||||
|
Upload Facebook image
|
||||||
|
</ImageUpload>
|
||||||
<TextField
|
<TextField
|
||||||
inputRef={focusRef}
|
inputRef={focusRef}
|
||||||
placeholder={siteTitle}
|
placeholder={siteTitle}
|
||||||
|
|
|
@ -55,10 +55,11 @@ const Twitter: React.FC = () => {
|
||||||
height='200px'
|
height='200px'
|
||||||
id='twitter-image'
|
id='twitter-image'
|
||||||
imageURL={twitterImage}
|
imageURL={twitterImage}
|
||||||
label='Upload twitter image'
|
|
||||||
onDelete={handleImageDelete}
|
onDelete={handleImageDelete}
|
||||||
onUpload={handleImageUpload}
|
onUpload={handleImageUpload}
|
||||||
/>
|
>
|
||||||
|
Upload twitter image
|
||||||
|
</ImageUpload>
|
||||||
<TextField
|
<TextField
|
||||||
inputRef={focusRef}
|
inputRef={focusRef}
|
||||||
placeholder={siteTitle}
|
placeholder={siteTitle}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import Button from '../../../../admin-x-ds/global/Button';
|
||||||
import ConfirmationModal from '../../../../admin-x-ds/global/ConfirmationModal';
|
import ConfirmationModal from '../../../../admin-x-ds/global/ConfirmationModal';
|
||||||
import Heading from '../../../../admin-x-ds/global/Heading';
|
import Heading from '../../../../admin-x-ds/global/Heading';
|
||||||
import Icon from '../../../../admin-x-ds/global/Icon';
|
import Icon from '../../../../admin-x-ds/global/Icon';
|
||||||
|
import ImageUpload from '../../../../admin-x-ds/global/ImageUpload';
|
||||||
import Menu from '../../../../admin-x-ds/global/Menu';
|
import Menu from '../../../../admin-x-ds/global/Menu';
|
||||||
import Modal from '../../../../admin-x-ds/global/Modal';
|
import Modal from '../../../../admin-x-ds/global/Modal';
|
||||||
import NiceModal from '@ebay/nice-modal-react';
|
import NiceModal from '@ebay/nice-modal-react';
|
||||||
|
@ -387,7 +388,9 @@ interface UserDetailModalProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserMenuTrigger = () => (
|
const UserMenuTrigger = () => (
|
||||||
<Button color='white' icon='menu-horizontal' size='sm' />
|
<div className='flex h-8 cursor-pointer items-center justify-center rounded bg-[rgba(0,0,0,0.75)] px-3 opacity-80 hover:opacity-100'>
|
||||||
|
<Icon color='white' name='menu-horizontal' size='sm' />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const confirmMakeOwner = () => {
|
const confirmMakeOwner = () => {
|
||||||
|
@ -466,6 +469,8 @@ const UserDetailModal:React.FC<UserDetailModalProps> = ({user, updateUser}) => {
|
||||||
}
|
}
|
||||||
}, [saveState]);
|
}, [saveState]);
|
||||||
|
|
||||||
|
const fileUploadButtonClasses = 'absolute right-[104px] bottom-12 bg-[rgba(0,0,0,0.75)] rounded text-sm text-white flex items-center justify-center px-3 h-8 opacity-80 hover:opacity-100 transition cursor-pointer font-medium';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
okColor='green'
|
okColor='green'
|
||||||
|
@ -478,21 +483,33 @@ const UserDetailModal:React.FC<UserDetailModalProps> = ({user, updateUser}) => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div className={`relative -mx-12 -mt-12 bg-gradient-to-tr from-grey-900 to-black p-12`} style={userData.cover_image ? {backgroundImage: `url(${userData.cover_image})`, backgroundSize: 'cover'} : {}}>
|
<div className={`relative -mx-12 -mt-12 bg-gradient-to-tr from-grey-900 to-black`}>
|
||||||
{userData.cover_image && (
|
<ImageUpload
|
||||||
<div className='absolute inset-0 z-0 block bg-gradient-to-tr from-[rgba(0,0,0,0.5)] to-[rgba(0,0,0,0.01)]'></div>
|
deleteButtonClassName={fileUploadButtonClasses}
|
||||||
)}
|
deleteButtonContent='Delete cover image'
|
||||||
<div className="absolute right-8 top-8">
|
fileUploadClassName={fileUploadButtonClasses}
|
||||||
|
height={userData.cover_image ? '100%' : '32px'}
|
||||||
|
id='cover-image'
|
||||||
|
imageClassName='absolute inset-0 bg-cover group'
|
||||||
|
imageURL={userData.cover_image || ''}
|
||||||
|
onDelete={() => {
|
||||||
|
alert('deleted');
|
||||||
|
}}
|
||||||
|
onUpload={() => {
|
||||||
|
alert('uploaded');
|
||||||
|
}}
|
||||||
|
>Upload cover image</ImageUpload>
|
||||||
|
<div className="absolute bottom-12 right-12">
|
||||||
<Menu items={menuItems} position='left' trigger={<UserMenuTrigger />}></Menu>
|
<Menu items={menuItems} position='left' trigger={<UserMenuTrigger />}></Menu>
|
||||||
</div>
|
</div>
|
||||||
<div className='relative z-10 mt-60 flex gap-4'>
|
<div className='pointer-events-none relative z-10 flex gap-4 px-12 pb-12 pt-60'>
|
||||||
<Avatar bgColor={generateAvatarColor((userData.name ? userData.name : userData.email))} className='-ml-1' image={userData.profile_image} label={getInitials(userData.name)} labelColor='white' size='xl' />
|
<Avatar bgColor={generateAvatarColor((userData.name ? userData.name : userData.email))} className='-ml-1' image={userData.profile_image} label={getInitials(userData.name)} labelColor='white' size='xl' />
|
||||||
<div>
|
<div>
|
||||||
<Heading styles='text-white'>{user.name}</Heading>
|
<Heading styles='text-white'>{user.name}</Heading>
|
||||||
<span className='text-md font-semibold text-white'>Administrator</span>
|
<span className='text-md font-semibold text-white'>Administrator</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-10 grid grid-cols-2 gap-x-12 gap-y-20 pb-10'>
|
<div className='mt-10 grid grid-cols-2 gap-x-12 gap-y-20 pb-10'>
|
||||||
<Basic setUserData={setUserData} user={userData} />
|
<Basic setUserData={setUserData} user={userData} />
|
||||||
<Details setUserData={setUserData} user={userData} />
|
<Details setUserData={setUserData} user={userData} />
|
||||||
|
|
Loading…
Add table
Reference in a new issue