forge/examples/spa/app.tsx
2025-12-27 21:14:58 -08:00

181 lines
3.9 KiB
TypeScript

import { define } from '../../src'
import { ButtonExamplesContent } from '../button'
import { ProfileExamplesContent } from '../profile'
import { NavigationExamplesContent } from '../navigation'
export const Main = define('SpaMain', {
base: 'div',
padding: '40px 20px',
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
background: '#f3f4f6',
})
export const Container = define('SpaContainer', {
base: 'div',
maxWidth: 1200,
margin: '0 auto'
})
// Simple client-side router
const Link = define('Link', {
base: 'a',
color: '#3b82f6',
textDecoration: 'none',
states: {
hover: {
textDecoration: 'underline'
}
},
render({ props, parts: { Root } }) {
const handleClick = (e: Event) => {
e.preventDefault()
window.history.pushState({}, '', props.href)
window.dispatchEvent(new Event('routechange'))
}
return (
<Root {...props} onclick={handleClick}>
{props.children}
</Root>
)
}
})
const Nav = define('Nav', {
base: 'nav',
display: 'flex',
gap: 20,
marginBottom: 40,
padding: 20,
background: 'white',
borderRadius: 8,
boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
})
const P = define('P', {
color: '#6b7280',
fontSize: 18,
marginBottom: 48,
})
const ExamplesGrid = define('ExamplesGrid', {
display: 'grid',
gap: 20,
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))'
})
const ExampleCard = define('ExampleCard', {
base: 'a',
background: 'white',
padding: 24,
borderRadius: 12,
boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
textDecoration: 'none',
transition: 'all 0.2s ease',
display: 'block',
states: {
hover: {
transform: 'translateY(-2px)',
boxShadow: '0 4px 12px rgba(0,0,0,0.15)'
}
},
parts: {
H2: {
color: '#111827',
margin: '0 0 8px 0',
fontSize: 20,
},
P: {
color: '#6b7280',
margin: 0,
fontSize: 14,
}
},
render({ props: { title, desc, ...props }, parts: { Root, H2, P } }) {
const handleClick = (e: Event) => {
e.preventDefault()
window.history.pushState({}, '', props.href)
window.dispatchEvent(new Event('routechange'))
}
return (
<Root {...props} onclick={handleClick}>
<H2>{title}</H2>
<P>{desc}</P>
</Root>
)
}
})
const HomePage = () => (
<>
<P>Explore component examples built with Forge - Client-side SPA version</P>
<ExamplesGrid>
<ExampleCard href="/spa/profile"
title="Profile Card"
desc="User profile component with variants for size, theme, and verified status"
/>
<ExampleCard href="/spa/buttons"
title="Buttons"
desc="Button component with intent, size, and disabled variants"
/>
<ExampleCard href="/spa/navigation"
title="Navigation"
desc="Navigation patterns including tabs, pills, vertical nav, and breadcrumbs"
/>
</ExamplesGrid>
</>
)
const ProfilePage = () => <ProfileExamplesContent />
const ButtonsPage = () => <ButtonExamplesContent />
const NavigationPage = () => <NavigationExamplesContent />
export function route(path: string) {
switch (path) {
case '/spa':
case '/spa/':
return <HomePage />
case '/spa/profile':
return <ProfilePage />
case '/spa/buttons':
return <ButtonsPage />
case '/spa/navigation':
return <NavigationPage />
default:
return <P>404 Not Found</P>
}
}
export function App() {
return (
<Main>
<Container>
<Nav>
<a href="/" style="color: #3b82f6; text-decoration: none;">Home</a>
<Link href="/spa">SPA Examples</Link>
<Link href="/spa/profile">Profile</Link>
<Link href="/spa/buttons">Buttons</Link>
<Link href="/spa/navigation">Navigation</Link>
</Nav>
<div id="content">
{route(window.location.pathname)}
</div>
</Container>
</Main>
)
}