rc-1
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { type ReactNode, useEffect, useState } from 'react'
|
||||
import { ScrollContext } from '../../functions/Context'
|
||||
import HeaderMessage from '../layout/HeaderMessage'
|
||||
import HeaderError from '../layout/HeaderError'
|
||||
|
||||
import ViewAnimation from '../views/Animation'
|
||||
import ViewHomepage from '../views/Homepage'
|
||||
import ViewPersonal from '../views/Personal'
|
||||
import ViewSearch from '../views/Search'
|
||||
import ViewSettings from '../views/Settings'
|
||||
import ViewText from '../views/Text'
|
||||
import ViewUpload from '../views/Upload'
|
||||
|
||||
export default function PaneContent() {
|
||||
const [mainElem, setMainElem] = useState<HTMLElement | null>(null)
|
||||
const [path, setPath] = useState(window.location.pathname)
|
||||
const [key, setKey] = useState(window.location.href)
|
||||
|
||||
// Track Path
|
||||
useEffect(() => {
|
||||
const onPop = () => {
|
||||
setPath(window.location.pathname)
|
||||
setKey(window.location.href)
|
||||
}
|
||||
window.addEventListener('popstate', onPop)
|
||||
return () => window.removeEventListener('popstate', onPop)
|
||||
}, [])
|
||||
|
||||
// Match Component
|
||||
const views = new Array<{ route: RegExp; scroll: boolean; component: (m: RegExpMatchArray) => ReactNode }>(
|
||||
{ route: /^\/art\/([0-9]+)$/, scroll: false, component: (m) => <ViewAnimation key={key} id={m[1]} /> },
|
||||
{ route: /^\/text\/([a-z-]+)$/, scroll: true, component: (m) => <ViewText key={key} id={m[1]} /> },
|
||||
{ route: /^\/personal$/, scroll: true, component: (_) => <ViewPersonal /> },
|
||||
{ route: /^\/upload$/, scroll: false, component: (_) => <ViewUpload key={key} /> },
|
||||
{ route: /^\/settings$/, scroll: true, component: (_) => <ViewSettings key={key} /> },
|
||||
{ route: /^\/search$/, scroll: true, component: (_) => <ViewSearch key={key} /> },
|
||||
{ route: /^\/$/, scroll: true, component: (_) => <ViewHomepage key={key} /> },
|
||||
)
|
||||
|
||||
const match = views.map((v) => ({ v, m: path.match(v.route) })).find(({ m }) => m !== null)
|
||||
const relevant = match ? { ...match.v, component: match.v.component(match.m!) } : null
|
||||
|
||||
// Render Content
|
||||
return (
|
||||
<ScrollContext.Provider value={mainElem}>
|
||||
<main ref={setMainElem} className={`layout-content ${relevant?.scroll ? 'layout-scrolling' : ''}`}>
|
||||
{relevant ? (
|
||||
relevant.component
|
||||
) : (
|
||||
<>
|
||||
<HeaderMessage label="System Message" />
|
||||
<HeaderError reason="The page you requested was not found." />
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
</ScrollContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { type ReactNode } from 'react'
|
||||
import './styles/PaneGlass.css'
|
||||
|
||||
interface PropsForPaneGlass {
|
||||
children?: ReactNode
|
||||
}
|
||||
|
||||
export default function PaneGlass({ children }: PropsForPaneGlass) {
|
||||
return (
|
||||
<div className="layout-glass-container">
|
||||
<div className="layout-glass-corner" />
|
||||
<div className="layout-glass-corner" />
|
||||
<div className="layout-glass-corner" />
|
||||
<div className="layout-glass-corner" />
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import type { BackendTag } from '../../functions/BackendTypes'
|
||||
import { BackendFetch } from '../../functions/Backend'
|
||||
import { routeTo } from '../../functions/Route'
|
||||
import VectorIconStar from '../../vectors/star.svg'
|
||||
import VectorIconFeed from '../../vectors/feed.svg'
|
||||
|
||||
import SidebarCategory from '../layout/SidebarCategory'
|
||||
import SidebarItemLogo from '../layout/SidebarItemLogo'
|
||||
import SidebarItemText from '../layout/SidebarItemText'
|
||||
import SidebarItemIcon from '../layout/SidebarItemIcon'
|
||||
import SidebarItemTag from '../layout/SidebarItemTag'
|
||||
import InputTags from '../inputs/Tags'
|
||||
|
||||
export default function PaneSidebar() {
|
||||
const [childrenTags, setChildrenTags] = useState([<a className="item-tag dummy">... LOADING ...</a>])
|
||||
|
||||
useEffect(() => {
|
||||
BackendFetch<BackendTag[]>('/tags/popular?limit=5').then((resp) => {
|
||||
if (!resp.success) {
|
||||
console.error('Tags Unavailable:', resp)
|
||||
setChildrenTags([
|
||||
<a className="item-tag dummy">... ERROR ...</a>,
|
||||
<a className="item-tag dummy">VIEW CONSOLE FOR DETAILS</a>,
|
||||
])
|
||||
return
|
||||
}
|
||||
setChildrenTags(
|
||||
resp.json.map((i) => <SidebarItemTag key={i.id} id={i.id} usage={i.usage} label={i.label} />),
|
||||
)
|
||||
})
|
||||
}, [])
|
||||
|
||||
function onTagChange(tags: BackendTag[]) {
|
||||
const query = tags.map((t) => `tag=${t.id}`).join('&')
|
||||
routeTo(`/search?${query}`)
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="layout-sidebar layout-scrolling">
|
||||
<SidebarCategory label="Main">
|
||||
<SidebarItemLogo />
|
||||
<InputTags label="" allowCustom={false} onChange={onTagChange} />
|
||||
</SidebarCategory>
|
||||
|
||||
<SidebarCategory label="Sections">
|
||||
<SidebarItemIcon
|
||||
icon={VectorIconFeed}
|
||||
label="Upload"
|
||||
description="Submit new animation"
|
||||
location="/upload"
|
||||
/>
|
||||
<SidebarItemIcon
|
||||
icon={VectorIconStar}
|
||||
label="Personal"
|
||||
description="From this device"
|
||||
location="/personal"
|
||||
/>
|
||||
</SidebarCategory>
|
||||
|
||||
<SidebarCategory label="Popular" header={true}>
|
||||
{childrenTags}
|
||||
</SidebarCategory>
|
||||
|
||||
<SidebarCategory label="Links" header={true}>
|
||||
<SidebarItemText label="Terms of Service" location="/text/terms-of-service" />
|
||||
<SidebarItemText label="Privacy Policy" location="/text/privacy-policy" />
|
||||
<SidebarItemText label="API Guide" location="/text/api-guide" />
|
||||
<SidebarItemText label="Settings" location="/settings" />
|
||||
</SidebarCategory>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
div.layout-glass-container {
|
||||
backdrop-filter: blur(var(--effect-glass-blur));
|
||||
margin: var(--effect-glass-corner-margin);
|
||||
background: var(--effect-glass-tint);
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
div.layout-glass-corner {
|
||||
position: absolute;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
div.layout-glass-corner:nth-child(1) {
|
||||
/* Top-left */
|
||||
top: var(--effect-glass-corner-offset);
|
||||
left: var(--effect-glass-corner-offset);
|
||||
border-top: var(--effect-glass-corner-thickness) solid var(--effect-glass-corner-color);
|
||||
border-left: var(--effect-glass-corner-thickness) solid var(--effect-glass-corner-color);
|
||||
}
|
||||
|
||||
div.layout-glass-corner:nth-child(2) {
|
||||
/* Top-right */
|
||||
top: var(--effect-glass-corner-offset);
|
||||
right: var(--effect-glass-corner-offset);
|
||||
border-top: var(--effect-glass-corner-thickness) solid var(--effect-glass-corner-color);
|
||||
border-right: var(--effect-glass-corner-thickness) solid var(--effect-glass-corner-color);
|
||||
}
|
||||
|
||||
div.layout-glass-corner:nth-child(3) {
|
||||
/* Bottom-left */
|
||||
bottom: var(--effect-glass-corner-offset);
|
||||
left: var(--effect-glass-corner-offset);
|
||||
border-bottom: var(--effect-glass-corner-thickness) solid var(--effect-glass-corner-color);
|
||||
border-left: var(--effect-glass-corner-thickness) solid var(--effect-glass-corner-color);
|
||||
}
|
||||
|
||||
div.layout-glass-corner:nth-child(4) {
|
||||
right: var(--effect-glass-corner-offset);
|
||||
/* Bottom-right */
|
||||
bottom: var(--effect-glass-corner-offset);
|
||||
border-right: var(--effect-glass-corner-thickness) solid var(--effect-glass-corner-color);
|
||||
border-bottom: var(--effect-glass-corner-thickness) solid var(--effect-glass-corner-color);
|
||||
}
|
||||
Reference in New Issue
Block a user