Add toast on copy link

It animates in and out too
This commit is contained in:
Justin Edmund 2023-01-27 21:35:08 -08:00
parent 191f4447d5
commit 6da5a4f320
5 changed files with 122 additions and 47 deletions

View file

@ -33,6 +33,7 @@ import Link from 'next/link'
import LoginModal from '~components/LoginModal'
import SignupModal from '~components/SignupModal'
import AccountModal from '~components/AccountModal'
import Toast from '~components/Toast'
const Header = () => {
// Localization
@ -42,7 +43,7 @@ const Header = () => {
const router = useRouter()
// State management
const [open, setOpen] = useState(false)
const [copyToastOpen, setCopyToastOpen] = useState(false)
const [leftMenuOpen, setLeftMenuOpen] = useState(false)
const [rightMenuOpen, setRightMenuOpen] = useState(false)
@ -50,6 +51,14 @@ const Header = () => {
const { account } = useSnapshot(accountState)
const { party } = useSnapshot(appState)
function handleCopyToastOpenChanged(open: boolean) {
setCopyToastOpen(open)
}
function handleCopyToastCloseClicked() {
setCopyToastOpen(false)
}
function handleLeftMenuButtonClicked() {
setLeftMenuOpen(!leftMenuOpen)
}
@ -58,10 +67,6 @@ const Header = () => {
setRightMenuOpen(!rightMenuOpen)
}
function onClickOutsideMenu() {
setOpen(false)
}
function handleLeftMenuOpenChange(open: boolean) {
setLeftMenuOpen(open)
}
@ -86,11 +91,15 @@ const Header = () => {
el.select()
document.execCommand('copy')
el.remove()
setCopyToastOpen(true)
}
function newParty() {
function handleNewParty(event: React.MouseEvent, path: string) {
event.preventDefault()
// Push the root URL
router.push('/')
router.push(path)
// Clean state
const resetState = clonedeep(initialAppState)
@ -100,6 +109,9 @@ const Header = () => {
// Set party to be editable
appState.party.editable = true
// Close right menu
closeRightMenu()
}
function logout() {
@ -141,7 +153,7 @@ const Header = () => {
else console.error('Failed to unsave team: No party ID')
}
const title = () => {
const pageTitle = () => {
let title = ''
let hasAccessory = false
@ -178,22 +190,7 @@ const Header = () => {
)
}
const saveButton = () => {
return (
<Button
leftAccessoryIcon={<SaveIcon />}
className={classNames({
Save: true,
Saved: party.favorited,
})}
blended={true}
text={party.favorited ? 'Saved' : 'Save'}
onClick={toggleFavorite}
/>
)
}
const image = () => {
const profileImage = () => {
let image
const user = accountState.account.user
@ -214,6 +211,34 @@ const Header = () => {
return image
}
const urlCopyToast = () => {
return (
<Toast
open={copyToastOpen}
duration={2400}
type="foreground"
content="This party's URL was copied to your clipboard"
onOpenChange={handleCopyToastOpenChanged}
onCloseClick={handleCopyToastCloseClicked}
/>
)
}
const saveButton = () => {
return (
<Button
leftAccessoryIcon={<SaveIcon />}
className={classNames({
Save: true,
Saved: party.favorited,
})}
blended={true}
text={party.favorited ? 'Saved' : 'Save'}
onClick={toggleFavorite}
/>
)
}
const left = () => {
return (
<section>
@ -235,7 +260,7 @@ const Header = () => {
</DropdownMenuContent>
</DropdownMenu>
</div>
{title()}
{pageTitle()}
</section>
)
}
@ -249,13 +274,6 @@ const Header = () => {
? saveButton()
: ''}
{/* <Button
leftAccessoryIcon={<AddIcon className="Add" />}
blended={true}
text={t('buttons.new')}
onClick={newParty}
/> */}
<DropdownMenu
open={rightMenuOpen}
onOpenChange={handleRightMenuOpenChange}
@ -263,7 +281,7 @@ const Header = () => {
<DropdownMenuTrigger asChild>
<Button
className={classNames({ Active: rightMenuOpen })}
leftAccessoryIcon={image()}
leftAccessoryIcon={profileImage()}
rightAccessoryIcon={<ArrowIcon />}
rightAccessoryClassName="Arrow"
onClick={handleRightMenuButtonClicked}
@ -340,12 +358,15 @@ const Header = () => {
items = (
<>
<DropdownMenuGroup className="MenuGroup">
<DropdownMenuItem className="MenuItem" onClick={closeRightMenu}>
<Link href={`/new` || ''} passHref>
<DropdownMenuItem className="MenuItem">
<Link
onClick={(e: React.MouseEvent) => handleNewParty(e, '/new')}
href="/new"
>
New party
</Link>
</DropdownMenuItem>
<DropdownMenuItem className="MenuItem" onClick={closeRightMenu}>
<DropdownMenuItem className="MenuItem">
<Link href={`/${account.user.username}` || ''} passHref>
Your profile
</Link>
@ -377,7 +398,10 @@ const Header = () => {
<>
<DropdownMenuGroup className="MenuGroup">
<DropdownMenuItem className="MenuItem">
<Link href={`/new` || ''} passHref>
<Link
onClick={(e: React.MouseEvent) => handleNewParty(e, '/new')}
href="/new"
>
New party
</Link>
</DropdownMenuItem>
@ -398,6 +422,7 @@ const Header = () => {
<nav id="Header">
{left()}
{right()}
{urlCopyToast()}
</nav>
)
}

View file

@ -1,12 +1,33 @@
.Toast {
background: var(--dialog-bg);
border-radius: $card-corner;
box-shadow: 0 1px 12px rgba(0, 0, 0, 0.18);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12), 0 0 1px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
gap: $unit-2x;
padding: $unit-3x;
&[data-state='open'] {
animation: slideLeft 150ms cubic-bezier(0.16, 1, 0.3, 1);
}
&[data-state='closed'] {
animation: fadeOut 300ms cubic-bezier(0.16, 1, 0.3, 1);
}
&[data-swipe='move'] {
transform: translateX(var(--radix-toast-swipe-move-x));
}
&[data-swipe='cancel'] {
transform: translateX(0);
transition: transform 200ms ease-out;
}
&[data-swipe='end'] {
animation: slideRight 100ms ease-out;
}
.Header {
align-items: center;
display: flex;

View file

@ -17,22 +17,24 @@ const Toast = ({
content,
...props
}: PropsWithChildren<Props>) => {
const { onCloseClick, ...toastProps } = props
const classes = classNames(props.className, {
Toast: true,
})
return (
<ToastPrimitive.Root {...props} className={classes}>
<div className="Header">
{title && (
<ToastPrimitive.Root {...toastProps} className={classes}>
{title && (
<div className="Header">
<ToastPrimitive.Title asChild>
<h3>{title}</h3>
</ToastPrimitive.Title>
)}
<ToastPrimitive.Close aria-label="Close" onClick={props.onCloseClick}>
<span aria-hidden>×</span>
</ToastPrimitive.Close>
</div>
<ToastPrimitive.Close aria-label="Close" onClick={onCloseClick}>
<span aria-hidden>×</span>
</ToastPrimitive.Close>
</div>
)}
<ToastPrimitive.Description asChild>
<p>{content}</p>
</ToastPrimitive.Description>

View file

@ -44,7 +44,7 @@ function MyApp({ Component, pageProps }: AppProps) {
return (
<ThemeProvider>
<ToastProvider>
<ToastProvider swipeDirection="right">
<Layout>
<Component {...pageProps} />
</Layout>

View file

@ -368,6 +368,15 @@ i.tag {
}
}
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes openModalDesktop {
0% {
opacity: 0;
@ -392,6 +401,24 @@ i.tag {
}
}
@keyframes slideLeft {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
@keyframes slideRight {
from {
transform: translateX(var(--radix-toast-swipe-end-x));
}
to {
transform: translateX(100%);
}
}
@keyframes fadeInFilter {
from {
backdrop-filter: blur(5px) saturate(100%) brightness(80%) opacity(0);