navbar
This commit is contained in:
parent
e0e1a775bc
commit
8a1a511a9b
|
@ -8,6 +8,7 @@
|
|||
"name": "goshort",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"classnames": "^2.3.2",
|
||||
"react": "^18.2.0",
|
||||
|
@ -469,6 +470,21 @@
|
|||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@headlessui/react": {
|
||||
"version": "1.7.17",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.17.tgz",
|
||||
"integrity": "sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==",
|
||||
"dependencies": {
|
||||
"client-only": "^0.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16 || ^17 || ^18",
|
||||
"react-dom": "^16 || ^17 || ^18"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroicons/react": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.18.tgz",
|
||||
|
@ -1614,6 +1630,11 @@
|
|||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
|
||||
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
|
||||
},
|
||||
"node_modules/client-only": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"classnames": "^2.3.2",
|
||||
"react": "^18.2.0",
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
import { FunctionComponent } from "react"
|
||||
|
||||
import { Outlet } from "react-router-dom"
|
||||
import { Outlet, ScrollRestoration } from "react-router-dom"
|
||||
|
||||
import Button from "./components/Button"
|
||||
// import Button from "./components/Button"
|
||||
import Container from "./components/Container"
|
||||
import { useIsAuthenticated } from "./hooks/useAuth"
|
||||
import Navbar from "./components/Navbar"
|
||||
// import { useIsAuthenticated } from "./hooks/useAuth"
|
||||
|
||||
const App: FunctionComponent = () => {
|
||||
const isAuthenticated = useIsAuthenticated()
|
||||
// const isAuthenticated = useIsAuthenticated()
|
||||
return (
|
||||
<Container>
|
||||
<div className="flex flex-col">
|
||||
<div className="grid grid-cols-3 justify-between text-slate-500 font-medium">
|
||||
<>
|
||||
<Navbar />
|
||||
<Container>
|
||||
<div className="flex flex-col">
|
||||
{/* <div className="grid grid-cols-3 justify-between text-slate-500 font-medium">
|
||||
<div className="flex-flex-row mr-auto text-lg">
|
||||
<Button text="GoShort" link="/" />
|
||||
</div>
|
||||
|
@ -29,12 +32,14 @@ const App: FunctionComponent = () => {
|
|||
{isAuthenticated || <Button text="Login" link="lgn" />}
|
||||
{isAuthenticated && <Button text="Logout" link="lgo" />}
|
||||
</div>
|
||||
</div> */}
|
||||
<div className="flex flex-col">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</Container>
|
||||
<ScrollRestoration />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import { FunctionComponent, useCallback } from "react"
|
||||
|
||||
import { ChevronLeftIcon } from "@heroicons/react/24/outline"
|
||||
import classNames from "classnames"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
|
||||
const BackButton: FunctionComponent<{ className?: string }> = ({
|
||||
className,
|
||||
}) => {
|
||||
const navigate = useNavigate()
|
||||
const goBack = useCallback(() => navigate(-1), [navigate])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
className,
|
||||
"text-blue-500 flex flex-row align-middle justify-items-center hover:text-blue-600 duration-200 transition-colors"
|
||||
)}>
|
||||
<ChevronLeftIcon className="w-5 5-6" />
|
||||
<button className="" onClick={goBack}>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BackButton
|
|
@ -2,7 +2,7 @@ import { FunctionComponent, PropsWithChildren } from "react"
|
|||
|
||||
const Container: FunctionComponent<PropsWithChildren> = ({ children }) => {
|
||||
return (
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full">
|
||||
<div className="mx-auto max-w-7xl px-4 pb-20 sm:px-6 lg:px-8 w-full">
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ import { FunctionComponent } from "react"
|
|||
|
||||
const Header: FunctionComponent<{ title: string }> = ({ title }) => {
|
||||
return (
|
||||
<header className="pt-20 pb-10 px-3">
|
||||
<header className="pt-14 pb-10">
|
||||
<h1 className="text-5xl font-bold">{title}</h1>
|
||||
</header>
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@ import { FunctionComponent } from "react"
|
|||
import classNames from "classnames"
|
||||
import { NavLink, useSearchParams } from "react-router-dom"
|
||||
|
||||
const Button: FunctionComponent<{
|
||||
const NavButton: FunctionComponent<{
|
||||
text: string
|
||||
onClick?: () => void
|
||||
link?: string
|
||||
|
@ -11,7 +11,7 @@ const Button: FunctionComponent<{
|
|||
const Content: FunctionComponent<{ active?: boolean }> = ({ active }) => (
|
||||
<div
|
||||
className={classNames(
|
||||
"px-0 mx-3 py-2 group border-b hover:border-blue-600 transition-colors duration-200",
|
||||
"text-xl px-0 mx-3 py-2 group border-b hover:border-blue-600 transition-colors duration-200",
|
||||
{ "border-b-2 border-blue-600": active, "border-transparent": !active }
|
||||
)}>
|
||||
<button
|
||||
|
@ -41,4 +41,4 @@ const Button: FunctionComponent<{
|
|||
return <Content />
|
||||
}
|
||||
|
||||
export default Button
|
||||
export default NavButton
|
|
@ -0,0 +1,146 @@
|
|||
import { Fragment, useMemo } from "react"
|
||||
|
||||
import { Disclosure, Menu, Transition } from "@headlessui/react"
|
||||
import {
|
||||
Bars3Icon,
|
||||
ChevronDownIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline"
|
||||
import classNames from "classnames"
|
||||
import { Link, NavLink } from "react-router-dom"
|
||||
|
||||
import { useIsAuthenticated, useUser } from "../hooks/useAuth"
|
||||
|
||||
import NavButton from "./NavButton"
|
||||
|
||||
export default function Navbar() {
|
||||
const isAuthenticated = useIsAuthenticated()
|
||||
const user = useUser()
|
||||
|
||||
const navigation = useMemo(() => {
|
||||
const authed = [
|
||||
{ name: "Shorts", href: "sht" },
|
||||
{ name: "Tokens", href: "tkn" },
|
||||
{ name: "Sessions", href: "ses" },
|
||||
]
|
||||
const unauthed = [
|
||||
{ name: "Login", href: "lgn" },
|
||||
{ name: "Signup", href: "sgn" },
|
||||
]
|
||||
return isAuthenticated ? authed : unauthed
|
||||
}, [isAuthenticated])
|
||||
|
||||
return (
|
||||
<Disclosure as="nav">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div
|
||||
className={classNames("mx-auto max-w-7xl px-2 sm:px-6 lg:px-8", {
|
||||
"bg-white": open,
|
||||
})}>
|
||||
<div className="relative flex h-16 items-center justify-between">
|
||||
<div className="absolute inset-y-0 left-0 flex items-center sm:hidden">
|
||||
{/* Mobile menu button*/}
|
||||
<Disclosure.Button className="relative inline-flex items-center justify-center rounded-md p-2 text-slate-500 hover:bg-slate-200 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500">
|
||||
<span className="absolute -inset-0.5" />
|
||||
<span className="sr-only">Open main menu</span>
|
||||
{open ? (
|
||||
<XMarkIcon className="block h-6 w-6" aria-hidden="true" />
|
||||
) : (
|
||||
<Bars3Icon className="block h-6 w-6" aria-hidden="true" />
|
||||
)}
|
||||
</Disclosure.Button>
|
||||
</div>
|
||||
<div className="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
|
||||
<div className="flex flex-shrink-0 items-center">
|
||||
<NavButton text="GoShort" link="/" />
|
||||
</div>
|
||||
<div className="hidden sm:ml-6 sm:flex w-full flex-row justify-center">
|
||||
<div className="flex space-x-4">
|
||||
{navigation.map((item) => (
|
||||
<NavButton
|
||||
key={item.name}
|
||||
text={item.name}
|
||||
link={item.href}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
|
||||
{/* Profile dropdown */}
|
||||
{isAuthenticated && (
|
||||
<Menu as="div" className="relative ml-3">
|
||||
<div>
|
||||
<Menu.Button className="relative px-3 py-2 justify-items-center flex text-lg rounded-lg focus:outline-none focus:ring-none">
|
||||
<span className="absolute -inset-1.5" />
|
||||
<span className="sr-only">Open user menu</span>
|
||||
<span className="my-auto">{user?.username}</span>
|
||||
<span className="my-auto">
|
||||
<ChevronDownIcon
|
||||
className="h-4 w-4 ml-2"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</Menu.Button>
|
||||
</div>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95">
|
||||
<Menu.Items className="absolute right-0 z-10 mt-2 w-32 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<Link
|
||||
to="/lgo"
|
||||
className={classNames(
|
||||
active ? "bg-gray-100" : "",
|
||||
"block px-4 py-2 text-sm text-gray-700"
|
||||
)}>
|
||||
Logout
|
||||
</Link>
|
||||
)}
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
)}
|
||||
{isAuthenticated || <NavButton text="Login" link="lgn" />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Disclosure.Panel
|
||||
className={classNames("sm:hidden pb-2 flex", {
|
||||
"bg-white shadow": open,
|
||||
})}>
|
||||
{({ close }) => (
|
||||
<div className="space-y-1">
|
||||
{navigation.map((item) => (
|
||||
<NavLink
|
||||
onClick={() => close()}
|
||||
key={item.name}
|
||||
to={item.href}
|
||||
className={({ isActive }) =>
|
||||
classNames(
|
||||
isActive
|
||||
? "bg-blue-100 text-blue-600 border-blue-500"
|
||||
: "text-slate-500 hover:bg-slate-50 hover:text-slate-600 hover:border-slate-500",
|
||||
"block border-l-4 border-transparent px-3 py-2 text-base font-medium"
|
||||
)
|
||||
}>
|
||||
{item.name}
|
||||
</NavLink>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
)
|
||||
}
|
|
@ -68,7 +68,7 @@ const ShortItem: FunctionComponent<Short & { doDelete: () => void }> = ({
|
|||
<div className="col-span-10 md:col-span-4 lg:col-span-5 my-auto order-4 md:order-3">
|
||||
<a
|
||||
href={url}
|
||||
target="_blank"
|
||||
title={url}
|
||||
rel="noreferrer"
|
||||
className="flex flex-row font-normal leading-6 text-blue-600 hover:text-blue-500 transition-all duration-200">
|
||||
<span className="break-all">
|
||||
|
@ -87,10 +87,10 @@ const ShortItem: FunctionComponent<Short & { doDelete: () => void }> = ({
|
|||
Last viewed <time dateTime="2023-01-23T13:23Z">3h ago</time>
|
||||
</p>
|
||||
<Link to={`/sht/${name}`}>
|
||||
<div className="py-1 my-1 text-xs flex flex-row gap-1 text-slate-500 hover:text-blue-500 border-b border-transparent hover:border-blue-500 transition-colors duration-200">
|
||||
<div className="py-1 my-1 text-xs flex flex-row align-middle justify-items-center text-blue-500 hover:text-blue-600 transition-colors duration-200">
|
||||
<span className="my-auto">details</span>
|
||||
|
||||
<ChevronRightIcon className="my-auto w-3 h-3 -mr-1" />
|
||||
<ChevronRightIcon className="my-auto w-4 h-4 " />
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
@apply text-slate-700;
|
||||
@apply text-slate-700 bg-slate-50;
|
||||
font-family: -apple-system, SF Pro Text, SF UI Text, system-ui, Helvetica Neue, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { LoaderFunction, redirect, useLoaderData } from "react-router-dom"
|
||||
|
||||
import BackButton from "../components/BackButton"
|
||||
import Header from "../components/Header"
|
||||
import { protectedLoader } from "../hooks/useAuth"
|
||||
import { Short } from "../types"
|
||||
|
@ -10,6 +11,7 @@ export function Component() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<BackButton className="absolute mt-10" />
|
||||
<Header title={short.name} />
|
||||
{short.url}
|
||||
</>
|
||||
|
|
Loading…
Reference in New Issue
Block a user