-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathLayout.tsx
More file actions
95 lines (84 loc) · 2.65 KB
/
Layout.tsx
File metadata and controls
95 lines (84 loc) · 2.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import { ReactNode, useEffect, useState } from 'react'
import { cn } from '../lib/utils.js'
import Welcome from './Welcome.js'
interface LayoutProps {
children: ReactNode
className?: string
progress?: number
error?: Error
title?: string
}
/**
* Layout for shared UI.
* Content div style can be overridden by className prop.
*
* @param props
* @param props.children - content to display inside the layout
* @param props.className - additional class names to apply to the content container
* @param props.progress - progress bar value
* @param props.error - error message to display
* @param props.title - page title
*/
export default function Layout({ children, className, progress, error, title }: LayoutProps) {
const [showWelcome, setShowWelcome] = useState(false)
// Check localStorage on mount to see if the user has seen the welcome popup
useEffect(() => {
const dismissed = localStorage.getItem('welcome:dismissed') === 'true'
setShowWelcome(!dismissed)
}, [])
// Handle closing the welcome popup
function handleCloseWelcome() {
setShowWelcome(false)
localStorage.setItem('welcome:dismissed', 'true')
}
// Update title
useEffect(() => {
document.title = title ? `${title} - hyperparam` : 'hyperparam'
}, [title])
return <main className='main'>
<Sidebar />
<div className='content-container'>
<div className={cn('content', className)}>
{children}
</div>
<ErrorBar error={error}></ErrorBar>
</div>
{progress !== undefined && progress < 1 &&
<div className={'progress-bar'} role='progressbar'>
<div style={{ width: `${100 * progress}%` }} />
</div>
}
{showWelcome && <Welcome onClose={handleCloseWelcome} />}
</main>
}
function Sidebar() {
return <nav className='nav'>
<div>
<a className="brand" href='/'>hyperparam</a>
</div>
</nav>
}
export function Spinner({ className }: { className?: string }) {
return <div className={cn('spinner', className)}></div>
}
export function ErrorBar({ error }: { error?: Error }) {
const [showError, setShowError] = useState(error !== undefined)
const [prevError, setPrevError] = useState(error)
if (error) console.error(error)
/// Reset error visibility when error prop changes
if (error !== prevError) {
setPrevError(error)
setShowError(error !== undefined)
}
return <div className={cn('error-bar', showError && 'show-error')}>
<div className='error-content'>
<span>{error?.toString()}</span>
<button
aria-label='Close error message'
className='close-button'
onClick={() => { setShowError(false) }}>
×
</button>
</div>
</div>
}