Redesign read only state and add save button

This commit is contained in:
Justin Edmund 2022-03-14 16:47:13 -07:00
parent 9291e5501a
commit bda2639d88
2 changed files with 280 additions and 28 deletions

View file

@ -31,6 +31,22 @@
width: 100%;
}
}
.bottom {
display: flex;
flex-direction: row;
gap: $unit;
.left {
flex-grow: 1;
}
.right {
display: flex;
flex-direction: row;
gap: $unit;
}
}
}
&.ReadOnly {
@ -45,26 +61,8 @@
top: 0;
}
h1 {
font-size: $font-xlarge;
font-weight: $normal;
text-align: left;
margin-bottom: $unit;
}
a {
color: $blue;
&:hover {
text-decoration: underline;
}
}
.Raid {
color: $grey-50;
font-size: $font-regular;
font-weight: $medium;
margin-bottom: $unit * 2;
a:hover {
text-decoration: underline;
}
p {
@ -72,5 +70,84 @@
line-height: $font-regular * 1.2;
white-space: pre-line;
}
h1 {
font-size: $font-xlarge;
font-weight: $normal;
text-align: left;
margin-bottom: $unit;
}
.info {
align-items: center;
display: flex;
flex-direction: row;
gap: $unit;
margin-bottom: $unit * 2;
.left {
flex-grow: 1;
}
}
.attribution {
align-items: center;
display: flex;
flex-direction: row;
& > div {
align-items: center;
display: inline-flex;
font-size: $font-small;
height: 26px;
}
time {
font-size: $font-small;
}
& > *:not(:last-child):after {
content: " · ";
margin: 0 calc($unit / 2);
}
}
.user {
align-items: center;
display: inline-flex;
gap: calc($unit / 2);
margin-top: 1px;
img, .no-user {
$diameter: 24px;
border-radius: calc($diameter / 2);
height: $diameter;
width: $diameter;
}
img.gran {
background-color: #CEE7FE;
}
img.djeeta {
background-color: #FFE1FE;
}
.no-user {
background: $grey-80;
}
}
}
}
.EmptyDetails {
display: none;
justify-content: center;
margin-bottom: $unit * 10;
&.Visible {
display: flex;
}
}

View file

@ -1,27 +1,53 @@
import React, { useState } from 'react'
import Head from 'next/head'
import Router, { useRouter } from 'next/router'
import { useRouter } from 'next/router'
import { useSnapshot } from 'valtio'
import { useTranslation } from 'next-i18next'
import Linkify from 'react-linkify'
import classNames from 'classnames'
import * as AlertDialog from '@radix-ui/react-alert-dialog'
import CrossIcon from '~public/icons/Cross.svg'
import Button from '~components/Button'
import CharLimitedFieldset from '~components/CharLimitedFieldset'
import RaidDropdown from '~components/RaidDropdown'
import TextFieldset from '~components/TextFieldset'
import { accountState } from '~utils/accountState'
import { appState } from '~utils/appState'
import './index.scss'
import Link from 'next/link'
import { formatTimeAgo } from '~utils/timeAgo'
const emptyRaid: Raid = {
id: '',
name: {
en: '',
ja: ''
},
slug: '',
level: 0,
group: 0,
element: 0
}
// Props
interface Props {
editable: boolean
updateCallback: (name?: string, description?: string, raid?: Raid) => void
deleteCallback: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
}
const PartyDetails = (props: Props) => {
const { party, raids } = useSnapshot(appState)
const { account } = useSnapshot(accountState)
const { t } = useTranslation('common')
const router = useRouter()
const locale = router.locale || 'en'
const nameInput = React.createRef<HTMLInputElement>()
const descriptionInput = React.createRef<HTMLTextAreaElement>()
@ -39,6 +65,25 @@ const PartyDetails = (props: Props) => {
'Visible': party.detailsVisible
})
const emptyClasses = classNames({
'EmptyDetails': true,
'Visible': !party.detailsVisible
})
const userClass = classNames({
'user': true,
'empty': !party.user
})
const linkClass = classNames({
'wind': party && party.element == 1,
'fire': party && party.element == 2,
'water': party && party.element == 3,
'earth': party && party.element == 4,
'dark': party && party.element == 5,
'light': party && party.element == 6
})
const [errors, setErrors] = useState<{ [key: string]: string }>({
name: '',
description: ''
@ -62,12 +107,100 @@ const PartyDetails = (props: Props) => {
setErrors(newErrors)
}
function updateDetails(event: React.ChangeEvent) {
function toggleDetails() {
appState.party.detailsVisible = !appState.party.detailsVisible
// if (appState.party.detailsVisible)
// scroll.scrollToBottom()
// else
// scroll.scrollToTop()
}
function updateDetails(event: React.MouseEvent) {
const nameValue = nameInput.current?.value
const descriptionValue = descriptionInput.current?.value
const raid = raids.find(raid => raid.slug === raidSelect.current?.value)
props.updateCallback(nameValue, descriptionValue, raid)
toggleDetails()
}
const userImage = () => {
if (party.user)
return (
<img
alt={party.user.picture.picture}
className={`profile ${party.user.picture.element}`}
srcSet={`/profile/${party.user.picture.picture}.png,
/profile/${party.user.picture.picture}@2x.png 2x`}
src={`/profile/${party.user.picture.picture}.png`}
/>
)
else
return (<div className="no-user" />)
}
const userBlock = () => {
return (
<div className={userClass}>
{ userImage() }
{ (party.user) ? party.user.username : t('no_user') }
</div>
)
}
const linkedUserBlock = (user: User) => {
return (
<div>
<Link href={`/${user.username}`} passHref>
<a className={linkClass}>{userBlock()}</a>
</Link>
</div>
)
}
const linkedRaidBlock = (raid: Raid) => {
return (
<div>
<Link href={`/teams?raid=${raid.slug}`} passHref>
<a className={`Raid ${linkClass}`}>
{raid.name[locale]}
</a>
</Link>
</div>
)
}
const deleteButton = () => {
if (party.editable) {
return (
<AlertDialog.Root>
<AlertDialog.Trigger className="Button destructive">
<span className='icon'>
<CrossIcon />
</span>
<span className="text">{t('buttons.delete')}</span>
</AlertDialog.Trigger>
<AlertDialog.Portal>
<AlertDialog.Overlay className="Overlay" />
<AlertDialog.Content className="Dialog">
<AlertDialog.Title className="DialogTitle">
{t('modals.delete_team.title')}
</AlertDialog.Title>
<AlertDialog.Description className="DialogDescription">
{t('modals.delete_team.description')}
</AlertDialog.Description>
<div className="actions">
<AlertDialog.Cancel className="Button modal">{t('modals.delete_team.buttons.cancel')}</AlertDialog.Cancel>
<AlertDialog.Action className="Button modal destructive" onClick={(e) => props.deleteCallback(e)}>{t('modals.delete_team.buttons.confirm')}</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
)
} else {
return ('')
}
}
const editable = (
@ -77,7 +210,6 @@ const PartyDetails = (props: Props) => {
placeholder="Name your team"
value={party.name}
limit={50}
onBlur={updateDetails}
onChange={handleInputChange}
error={errors.name}
ref={nameInput}
@ -85,29 +217,72 @@ const PartyDetails = (props: Props) => {
<RaidDropdown
showAllRaidsOption={false}
currentRaid={party.raid?.slug || ''}
onBlur={updateDetails}
ref={raidSelect}
/>
<TextFieldset
fieldName="name"
placeholder={"Write your notes here\n\n\nWatch out for the 50% trigger!\nMake sure to click Fediels 1 first\nGood luck with RNG!"}
value={party.description}
onBlur={updateDetails}
onChange={handleTextAreaChange}
error={errors.description}
ref={descriptionInput}
/>
<div className="bottom">
<div className="left">
{ (router.pathname !== '/new') ? deleteButton() : '' }
</div>
<div className="right">
<Button
active={true}
onClick={toggleDetails}>
{t('buttons.cancel')}
</Button>
<Button
active={true}
icon="check"
onClick={updateDetails}>
{t('buttons.save_info')}
</Button>
</div>
</div>
</section>
)
const readOnly = (
<section className={readOnlyClasses}>
{ (party.name) ? <h1>{party.name}</h1> : '' }
{ (party.raid) ? <div className="Raid">{party.raid.name.en}</div> : '' }
<div className="info">
<div className="left">
{ (party.name) ? <h1>{party.name}</h1> : '' }
<div className="attribution">
{ (party.user) ? linkedUserBlock(party.user) : userBlock() }
{ (party.raid) ? linkedRaidBlock(party.raid) : '' }
{ (party.created_at != undefined)
? <time
className="last-updated"
dateTime={new Date(party.created_at).toString()}>
{formatTimeAgo(new Date(party.created_at), locale)}
</time>
: '' }
</div>
</div>
<div className="right">
{ (party.editable)
? <Button active={true} icon="edit" onClick={toggleDetails}>{t('buttons.show_info')}</Button>
: <div /> }
</div>
</div>
{ (party.description) ? <p><Linkify>{party.description}</Linkify></p> : '' }
</section>
)
const emptyDetails = (
<div className={emptyClasses}>
<Button active={true} icon="edit" onClick={toggleDetails}>{t('buttons.show_info')}</Button>
</div>
)
const generateTitle = () => {
let title = ''
@ -138,7 +313,7 @@ const PartyDetails = (props: Props) => {
<meta name="twitter:title" content={generateTitle()} />
<meta name="twitter:description" content={ (party.description) ? party.description : '' } />
</Head>
{readOnly}
{ (editable && (party.name || party.description || party.raid)) ? readOnly : emptyDetails}
{editable}
</div>
)