0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

First round of improving the name and bio modal

refs https://github.com/TryGhost/Team/issues/1756
This commit is contained in:
James Morris 2022-08-09 12:40:59 +01:00
parent 2629c5704d
commit 20fe790e5d
5 changed files with 169 additions and 81 deletions

View file

@ -423,7 +423,7 @@ const Form = (props) => {
handleShowDialog(event, {
bioAutofocus: true
});
}}>{memberBio ? memberBio : 'Add your bio'}
}}>{memberBio ? memberBio : 'Add your expertise'}
{memberBio && <EditIcon className="transition-all duration-100 ease-out opacity-0 -translate-x-[6px] group-hover:opacity-100 group-hover:translate-x-0 w-[12px] h-[12px] stroke-neutral-500 ml-1" />}
</button>
</div>

View file

@ -11,6 +11,9 @@ const AddNameDialog = (props) => {
const [name, setName] = useState(member.name ?? '');
const [bio, setBio] = useState(member.bio ?? '');
const [exampleProfiles, setExampleProfiles] = useState([]);
const [exampleExpertise, setExampleExpertise] = useState('Head of Marketing');
const maxBioChars = 50;
let initialBioChars = maxBioChars;
if (member.bio) {
@ -20,6 +23,10 @@ const AddNameDialog = (props) => {
const [error, setError] = useState({name: '', bio: ''});
const stopPropagation = (event) => {
event.stopPropagation();
};
const close = (succeeded) => {
dispatchAction('closePopup');
props.callback(succeeded);
@ -39,8 +46,49 @@ const AddNameDialog = (props) => {
}
};
const generateExampleProfiles = () => {
let returnable = [];
let dummyData = [
{avatar: 'https://images.unsplash.com/photo-1607746882042-944635dfe10e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2670&q=80', name: 'Sophie Joan', expertise: 'Freelance Writer'},
{avatar: 'https://images.unsplash.com/photo-1569913486515-b74bf7751574?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1289&q=80', name: 'Naomi Schiff', expertise: 'Founder @ Acme Inc'},
{avatar: 'https://images.unsplash.com/photo-1544725176-7c40e5a71c5e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2667&q=80', name: 'Katrina Klosp', expertise: 'Local Resident'},
{avatar: 'https://images.unsplash.com/photo-1580489944761-15a19d654956?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1361&q=80', name: 'Laura Smith', expertise: 'Craft Maker'},
{avatar: 'https://images.unsplash.com/photo-1633332755192-727a05c4013d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80', name: 'Peter Kristy', expertise: 'Design Consultant'},
{avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1364&q=80', name: 'Linda Lo', expertise: 'Wedding Photographer'},
{avatar: 'https://images.unsplash.com/photo-1527980965255-d3b416303d12?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80', name: 'Darren Mckenzie', expertise: 'Senior Engineer'},
{avatar: 'https://images.unsplash.com/photo-1566492031773-4f4e44671857?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80', name: 'Jack Tomlin', expertise: 'Mid-level UX Designer'}
];
// setup fake users
for (let i = 0; i < 4; i++) {
let dummyIndex = Math.floor(Math.random() * dummyData.length);
returnable.push(dummyData[dummyIndex]);
dummyData.splice(dummyIndex, 1);
}
return returnable;
};
const generateExampleExpertise = () => {
let dummyData = [
'Freelance Copywriter',
'Head of Marketing',
'Junior Developer',
'Full-time Parent',
'Local Resident',
'English Teacher',
'Support Officer',
'Professional Athlete'
];
return dummyData[Math.floor(Math.random() * dummyData.length)];
};
// using <input autofocus> breaks transitions in browsers. So we need to use a timer
useEffect(() => {
setExampleProfiles(generateExampleProfiles());
setExampleExpertise(generateExampleExpertise());
const timer = setTimeout(() => {
if (props.bioAutofocus) {
inputBioRef.current?.focus();
@ -52,80 +100,122 @@ const AddNameDialog = (props) => {
return () => {
clearTimeout(timer);
};
}, [inputNameRef]);
}, [inputNameRef, inputBioRef]);
const renderExampleProfile = (index) => {
return (exampleProfiles[index] ?
<Transition
appear
enter={`transition duration-200 delay-[400ms] ease-out`}
enterFrom="opacity-0 translate-y-2"
enterTo="opacity-100 translate-y-0"
leave="transition duration-200 ease-in"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-2"
>
<div className="flex flex-row justify-start items-center gap-3 my-4 pr-4">
<div className="w-10 h-10 rounded-full border-2 border-white bg-no-repeat bg-cover" style={{backgroundImage: `url(${exampleProfiles[index].avatar})`}} />
<div className="flex flex-col justify-center items-start">
<div className="text-base font-sans font-semibold tracking-tight text-white">
{exampleProfiles[index].name}
</div>
<div className="font-sans text-[14px] tracking-tight text-neutral-400">
{exampleProfiles[index].expertise}
</div>
</div>
</div>
</Transition> : null
);
};
return (
<>
<h1 className="font-sans font-bold tracking-tight text-[24px] mb-3 text-black">Add to your profile</h1>
<p className="font-sans text-[1.45rem] text-neutral-500 px-4 sm:px-8 leading-9">For a healthy discussion, let other members know who you are when commenting.</p>
<section className="mt-8 text-left">
<div className="flex flex-row mb-2 justify-between">
<label htmlFor="comments-name" className="font-sans font-medium text-sm">Name</label>
<Transition
show={!!error.name}
enter="transition duration-300 ease-out"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition duration-100 ease-out"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="font-sans text-sm text-red-500">{error.name}</div>
</Transition>
<div className="overflow-hidden relative bg-white w-screen sm:w-[720px] h-screen sm:h-auto p-[28px] sm:p-0 rounded-none sm:rounded-xl text-center shadow-modal" onClick={stopPropagation}>
<div className="flex">
<div className="flex flex-col justify-center items-center w-[40%] bg-[#1C1C1C]">
<div className="flex flex-col">
{exampleProfiles.length > 0 && (
<>
<span>{renderExampleProfile(0)}</span>
<span>{renderExampleProfile(1)}</span>
<span>{renderExampleProfile(2)}</span>
<span>{renderExampleProfile(3)}</span>
</>
)}
</div>
</div>
<input
id="comments-name"
className={`transition-[border-color] duration-200 font-sans px-3 rounded border border-neutral-200 focus:border-neutral-300 w-full outline-0 h-[42px] flex items-center ${error.name && 'border-red-500 focus:border-red-500'}`}
type="text"
name="name"
ref={inputNameRef}
value={name}
placeholder="Jamie Larson"
onChange={(e) => {
setName(e.target.value);
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
setName(e.target.value);
submit();
}
}}
maxLength="64"
/>
<div className="flex flex-row mt-4 mb-2 justify-between">
<label htmlFor="comments-name" className="font-sans font-medium text-sm">Bio</label>
<div className={`font-sans text-sm text-neutral-400 ${(bioCharsLeft === 0) && 'text-red-500'}`}><b>{bioCharsLeft}</b> characters left</div>
<div className="w-[60%] p-8">
<h1 className="font-sans font-bold tracking-tight text-[24px] mb-1 text-black text-left">Complete your profile.</h1>
<p className="font-sans text-base text-neutral-500 pr-4 sm:pr-10 leading-9 text-left">Add context to your comment, share your name and expertise to foster a healthy discussion.</p>
<section className="mt-8 text-left">
<div className="flex flex-row mb-2 justify-between">
<label htmlFor="comments-name" className="font-sans font-medium text-sm">Name</label>
<Transition
show={!!error.name}
enter="transition duration-300 ease-out"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition duration-100 ease-out"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="font-sans text-sm text-red-500">{error.name}</div>
</Transition>
</div>
<input
id="comments-name"
className={`transition-[border-color] duration-200 font-sans px-3 rounded border border-neutral-200 focus:border-neutral-300 w-full outline-0 h-[42px] flex items-center ${error.name && 'border-red-500 focus:border-red-500'}`}
type="text"
name="name"
ref={inputNameRef}
value={name}
placeholder="Jamie Larson"
onChange={(e) => {
setName(e.target.value);
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
setName(e.target.value);
submit();
}
}}
maxLength="64"
/>
<div className="flex flex-row mt-6 mb-2 justify-between">
<label htmlFor="comments-name" className="font-sans font-medium text-sm">Expertise</label>
<div className={`font-sans text-sm text-neutral-400 ${(bioCharsLeft === 0) && 'text-red-500'}`}><b>{bioCharsLeft}</b> characters left</div>
</div>
<input
id="comments-bio"
className={`transition-[border-color] duration-200 font-sans px-3 rounded border border-neutral-200 focus:border-neutral-300 w-full outline-0 h-[42px] flex items-center ${(bioCharsLeft === 0) && 'border-red-500 focus:border-red-500'}`}
type="text"
name="bio"
ref={inputBioRef}
value={bio}
placeholder={exampleExpertise}
onChange={(e) => {
let bioText = e.target.value;
setBioCharsLeft(maxBioChars - bioText.length);
setBio(bioText);
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
setBio(e.target.value);
submit();
}
}}
maxLength={maxBioChars}
/>
<button
className="transition-opacity duration-200 ease-linear w-full h-[44px] mt-8 px-8 flex items-center justify-center rounded-md text-white font-sans font-semibold text-[15px] bg-[#3204F5] opacity-100 hover:opacity-90"
onClick={submit}
>
Save
</button>
</section>
</div>
<input
id="comments-bio"
className={`transition-[border-color] duration-200 font-sans px-3 rounded border border-neutral-200 focus:border-neutral-300 w-full outline-0 h-[42px] flex items-center ${(bioCharsLeft === 0) && 'border-red-500 focus:border-red-500'}`}
type="text"
name="bio"
ref={inputBioRef}
value={bio}
placeholder="Head of Marketing at Acme, Inc"
onChange={(e) => {
let bioText = e.target.value;
setBioCharsLeft(maxBioChars - bioText.length);
setBio(bioText);
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
setBio(e.target.value);
submit();
}
}}
maxLength={maxBioChars}
/>
<button
className="transition-opacity duration-200 ease-linear w-full h-[44px] mt-8 px-8 flex items-center justify-center rounded-md text-white font-sans font-semibold text-[15px] bg-[#3204F5] opacity-100 hover:opacity-90"
onClick={submit}
>
Save
</button>
</section>
<CloseButton close={() => close(false)} />
</>
<CloseButton close={() => close(false)} />
</div>
</div>
);
};

View file

@ -3,7 +3,7 @@ import {ReactComponent as CloseIcon} from '../../images/icons/close.svg';
const CloseButton = (props) => {
return (
<button className="transition-opacity duration-100 ease-out absolute top-[36px] sm:top-10 right-8 sm:right-10 opacity-20 hover:opacity-40" onClick={props.close}>
<button className="transition-opacity duration-100 ease-out absolute top-[36px] sm:top-10 right-8 sm:right-8 opacity-20 hover:opacity-40" onClick={props.close}>
<CloseIcon className="w-[20px] h-[20px]" />
</button>
);

View file

@ -14,10 +14,6 @@ const GenericDialog = (props) => {
}
};
const stopPropagation = (event) => {
event.stopPropagation();
};
useEffect(() => {
const listener = (event) => {
if (event.key === 'Escape') {
@ -52,9 +48,7 @@ const GenericDialog = (props) => {
leaveFrom="translate-y-0 opacity-100"
leaveTo="translate-y-4 opacity-0"
>
<div className="relative bg-white w-screen sm:w-[500px] h-screen sm:h-auto p-[28px] sm:p-8 rounded-none sm:rounded-xl text-center shadow-modal" onClick={stopPropagation}>
{props.children}
</div>
{props.children}
</Transition.Child>
</div>
</Transition.Child>

View file

@ -27,6 +27,10 @@ const ReportDialog = (props) => {
const {dispatchAction} = useContext(AppContext);
const stopPropagation = (event) => {
event.stopPropagation();
};
const close = (event) => {
dispatchAction('closePopup');
};
@ -48,7 +52,7 @@ const ReportDialog = (props) => {
};
return (
<>
<div className="relative bg-white w-screen sm:w-[500px] h-screen sm:h-auto p-[28px] sm:p-8 rounded-none sm:rounded-xl text-center shadow-modal" onClick={stopPropagation}>
<h1 className="font-sans font-bold tracking-tight text-[24px] mb-3 text-black">You sure you want to report?</h1>
<p className="font-sans text-[1.45rem] text-neutral-500">You request will be sent to the owner of this site.</p>
<div className="mt-10">
@ -60,7 +64,7 @@ const ReportDialog = (props) => {
</button>
<p className="font-sans font-medium text-[1.45rem] text-neutral-500 mt-4 -mb-1">No, <button className="font-sans underline" onClick={close}>I've changed my mind</button></p>
</div>
</>
</div>
);
};