MVP0
This commit is contained in:
8
frontend/src/components/AutoTextarea.tsx
Normal file
8
frontend/src/components/AutoTextarea.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
type Props = React.TextareaHTMLAttributes<HTMLTextAreaElement> & { value: string; onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void }
|
||||
export default function AutoTextarea(props: Props){
|
||||
const ref = useRef<HTMLTextAreaElement|null>(null)
|
||||
const resize=()=>{ const el=ref.current; if(!el) return; el.style.height='0px'; el.style.height=el.scrollHeight+'px' }
|
||||
useEffect(()=>{ resize() },[props.value])
|
||||
return <textarea {...props} ref={ref} onChange={(e)=>{ props.onChange(e); requestAnimationFrame(resize) }} style={{...(props.style||{}), overflow:'hidden'}}/>
|
||||
}
|
||||
62
frontend/src/components/NavBar.tsx
Normal file
62
frontend/src/components/NavBar.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import React from 'react'
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
||||
import { clearToken, getToken } from '../api/client'
|
||||
|
||||
function NavItem({ to, label }: { to: string; label: string }) {
|
||||
const loc = useLocation()
|
||||
const active = loc.pathname === to || loc.pathname.startsWith(to + '/')
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
className="small"
|
||||
style={{
|
||||
padding: '10px 12px',
|
||||
borderRadius: 12,
|
||||
border: '1px solid transparent',
|
||||
background: active ? 'rgba(45,96,255,.10)' : 'transparent',
|
||||
color: active ? '#2d60ff' : undefined,
|
||||
fontWeight: active ? 700 : 600,
|
||||
textDecoration: 'none',
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default function NavBar() {
|
||||
const nav = useNavigate()
|
||||
const token = getToken()
|
||||
|
||||
return (
|
||||
<div className="card compact" style={{ marginBottom: 16 }}>
|
||||
<div className="row" style={{ justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div className="row" style={{ alignItems: 'center' }}>
|
||||
<div style={{ fontWeight: 800, letterSpacing: '-0.03em' }}>AdsAssistant</div>
|
||||
<div className="row" style={{ marginLeft: 10, gap: 8 }}>
|
||||
<NavItem to="/briefs" label="Брифы" />
|
||||
<NavItem to="/tests" label="Тесты" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row" style={{ alignItems: 'center' }}>
|
||||
{token ? (
|
||||
<button
|
||||
className="btn secondary"
|
||||
onClick={() => {
|
||||
clearToken()
|
||||
nav('/login')
|
||||
}}
|
||||
>
|
||||
Выйти
|
||||
</button>
|
||||
) : (
|
||||
<Link to="/login" className="small">
|
||||
Вход
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
31
frontend/src/components/Tooltip.tsx
Normal file
31
frontend/src/components/Tooltip.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import React, { useId, useState } from 'react'
|
||||
|
||||
export default function Tooltip({ text }: { text: string }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const id = useId()
|
||||
|
||||
return (
|
||||
<span
|
||||
style={{ position: 'relative', display: 'inline-flex', alignItems: 'center' }}
|
||||
onMouseEnter={() => setOpen(true)}
|
||||
onMouseLeave={() => setOpen(false)}
|
||||
onFocus={() => setOpen(true)}
|
||||
onBlur={() => setOpen(false)}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
aria-describedby={id}
|
||||
className="tipbtn"
|
||||
title={text}
|
||||
onClick={() => setOpen((v) => !v)}
|
||||
>
|
||||
i
|
||||
</button>
|
||||
{open && (
|
||||
<span id={id} role="tooltip" className="tooltip">
|
||||
{text}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user