0
Fork 0
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:
Peter Zimon 2023-06-02 14:29:46 +02:00
parent 3090fc6067
commit 82b56c319e
6 changed files with 50 additions and 23 deletions

View file

@ -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}) => {

View file

@ -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',

View file

@ -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>
); );
} }

View file

@ -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}

View file

@ -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}

View file

@ -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} />