| import { useState, useRef, useEffect } from 'react' | |
| import { Link } from 'react-router-dom' | |
| import { useAuth } from '../context/AuthContext' | |
| import { SampleDataMenuItem } from './SampleDataMenuItem' | |
| import { SpeechSettingsMenuItem } from './SpeechSettingsPanel' | |
| type Props = { | |
| onProfile: () => void | |
| onAccount: () => void | |
| onClearData: () => void | |
| } | |
| export function UserMenu({ onProfile, onAccount, onClearData }: Props) { | |
| const { user, signOut } = useAuth() | |
| const [open, setOpen] = useState(false) | |
| const [speechOpen, setSpeechOpen] = useState(false) | |
| const [sampleDataOpen, setSampleDataOpen] = useState(false) | |
| const ref = useRef<HTMLDivElement>(null) | |
| useEffect(() => { | |
| function close(e: MouseEvent) { | |
| if (ref.current && !ref.current.contains(e.target as Node)) { | |
| setOpen(false) | |
| setSpeechOpen(false) | |
| setSampleDataOpen(false) | |
| } | |
| } | |
| document.addEventListener('mousedown', close) | |
| return () => document.removeEventListener('mousedown', close) | |
| }, []) | |
| const label = user ? user.firstName || user.email : 'Guest' | |
| return ( | |
| <div className="user-menu-wrap" ref={ref}> | |
| <button | |
| type="button" | |
| className="user-menu-trigger" | |
| onClick={() => setOpen((o) => !o)} | |
| aria-expanded={open} | |
| aria-haspopup="menu" | |
| aria-label="Account menu" | |
| > | |
| <span className="user-menu-name">{label}</span> | |
| <span className="user-menu-kebab" aria-hidden title="Menu"> | |
| ⋮ | |
| </span> | |
| </button> | |
| {open && ( | |
| <div className="user-dropdown" role="menu"> | |
| <button | |
| type="button" | |
| role="menuitem" | |
| onClick={() => { | |
| setOpen(false) | |
| setSpeechOpen(false) | |
| setSampleDataOpen(false) | |
| onProfile() | |
| }} | |
| > | |
| Profile | |
| </button> | |
| <button | |
| type="button" | |
| role="menuitem" | |
| onClick={() => { | |
| setOpen(false) | |
| setSpeechOpen(false) | |
| setSampleDataOpen(false) | |
| if (user) onAccount() | |
| }} | |
| disabled={!user} | |
| title={!user ? 'Sign in to change account settings' : undefined} | |
| style={!user ? { opacity: 0.55, cursor: 'not-allowed' } : undefined} | |
| > | |
| Account | |
| </button> | |
| <button | |
| type="button" | |
| role="menuitem" | |
| onClick={() => { | |
| setOpen(false) | |
| setSpeechOpen(false) | |
| setSampleDataOpen(false) | |
| onClearData() | |
| }} | |
| > | |
| Clear User Data | |
| </button> | |
| <SampleDataMenuItem | |
| open={sampleDataOpen} | |
| onToggle={() => { | |
| setSampleDataOpen((s) => !s) | |
| setSpeechOpen(false) | |
| }} | |
| onActionComplete={() => { | |
| setOpen(false) | |
| setSpeechOpen(false) | |
| setSampleDataOpen(false) | |
| }} | |
| /> | |
| <div className="user-dropdown-divider" role="presentation" /> | |
| <SpeechSettingsMenuItem | |
| open={speechOpen} | |
| onToggle={() => { | |
| setSpeechOpen((s) => !s) | |
| setSampleDataOpen(false) | |
| }} | |
| /> | |
| {user ? ( | |
| <button | |
| type="button" | |
| role="menuitem" | |
| className="danger" | |
| onClick={() => { | |
| setOpen(false) | |
| setSpeechOpen(false) | |
| setSampleDataOpen(false) | |
| signOut() | |
| }} | |
| > | |
| Sign out | |
| </button> | |
| ) : ( | |
| <Link | |
| to="/auth" | |
| role="menuitem" | |
| className="user-dropdown-link" | |
| onClick={() => { | |
| setOpen(false) | |
| setSpeechOpen(false) | |
| setSampleDataOpen(false) | |
| }} | |
| style={{ display: 'block', padding: '0.5rem 1rem', textDecoration: 'none', color: 'inherit' }} | |
| > | |
| Sign In / Create Account | |
| </Link> | |
| )} | |
| </div> | |
| )} | |
| </div> | |
| ) | |
| } | |