wip navigation example

This commit is contained in:
Chris Wanstrath 2025-12-26 20:39:07 -08:00
parent 2fac626e46
commit 097dae4f2a
3 changed files with 353 additions and 3 deletions

View File

@ -70,6 +70,11 @@ export const IndexPage = () => (
<h2>Buttons</h2> <h2>Buttons</h2>
<p>Button component with intent, size, and disabled variants</p> <p>Button component with intent, size, and disabled variants</p>
</a> </a>
<a href="/navigation" class="example-card">
<h2>Navigation</h2>
<p>Navigation patterns including tabs, pills, vertical nav, and breadcrumbs</p>
</a>
</div> </div>
</div> </div>
</body> </body>

335
examples/navigation.tsx Normal file
View File

@ -0,0 +1,335 @@
import { define } from '../src'
import { Layout, ExampleSection } from './helpers'
const Tabs = define('Tabs', {
layout: {
display: 'flex',
gap: 0,
},
look: {
borderBottom: '2px solid #e5e7eb',
},
parts: {
Tab: {
base: 'button',
layout: {
padding: '12px 24px',
position: 'relative',
marginBottom: -2,
},
look: {
background: 'transparent',
border: 'none',
borderBottom: '2px solid transparent',
color: '#6b7280',
fontSize: 14,
fontWeight: 500,
cursor: 'pointer',
transition: 'all 0.2s ease',
},
states: {
':hover': {
look: {
color: '#111827',
}
}
}
}
},
variants: {
active: {
parts: {
Tab: {
look: {
color: '#3b82f6',
borderBottom: '2px solid #3b82f6',
},
}
}
}
},
render({ props, parts: { Root, Tab } }) {
return (
<Root>
{props.items?.map((item: any) => (
<Tab
key={item.id}
active={item.active}
onClick={() => console.log('Tab clicked:', item.label)}
>
{item.label}
</Tab>
))}
</Root>
)
}
})
const Pills = define('Pills', {
layout: {
display: 'flex',
gap: 8,
flexWrap: 'wrap',
},
parts: {
Pill: {
base: 'button',
layout: {
padding: '8px 16px',
},
look: {
background: '#f3f4f6',
border: 'none',
borderRadius: 20,
color: '#6b7280',
fontSize: 14,
fontWeight: 500,
cursor: 'pointer',
transition: 'all 0.2s ease',
},
states: {
':hover': {
look: {
background: '#e5e7eb',
color: '#111827',
}
}
}
}
},
variants: {
active: {
parts: {
Pill: {
look: {
background: '#3b82f6',
color: 'white',
},
states: {
':hover': {
look: {
background: '#2563eb',
color: 'white',
}
}
}
}
}
}
},
render({ props, parts: { Root, Pill } }) {
return (
<Root>
{props.items?.map((item: any) => (
<Pill
key={item.id}
active={item.active}
onclick={() => console.log('Pill clicked:', item.label)}
>
{item.label}
</Pill>
))}
</Root>
)
}
})
const VerticalNav = define('VerticalNav', {
layout: {
display: 'flex',
flexDirection: 'column',
gap: 4,
width: 240,
},
parts: {
NavItem: {
base: 'a',
layout: {
padding: '12px 16px',
display: 'flex',
alignItems: 'center',
gap: 12,
},
look: {
background: 'transparent',
borderRadius: 8,
color: '#6b7280',
fontSize: 14,
fontWeight: 500,
textDecoration: 'none',
cursor: 'pointer',
transition: 'all 0.2s ease',
},
states: {
':hover': {
look: {
background: '#f3f4f6',
color: '#111827',
}
}
}
},
Icon: {
layout: {
width: 20,
height: 20,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
look: {
fontSize: 18,
}
}
},
variants: {
active: {
parts: {
NavItem: {
look: {
background: '#eff6ff',
color: '#3b82f6',
},
states: {
':hover': {
look: {
background: '#dbeafe',
color: '#2563eb',
}
}
}
}
}
}
},
render({ props, parts: { Root, NavItem, Icon } }) {
return (
<Root>
{props.items?.map((item: any) => (
<NavItem
key={item.id}
active={item.active}
href={item.href || '#'}
>
{item.icon && <Icon>{item.icon}</Icon>}
{item.label}
</NavItem>
))}
</Root>
)
}
})
const Breadcrumbs = define('Breadcrumbs', {
layout: {
display: 'flex',
alignItems: 'center',
gap: 8,
flexWrap: 'wrap',
},
parts: {
Item: {
base: 'a',
look: {
color: '#6b7280',
fontSize: 14,
textDecoration: 'none',
transition: 'color 0.2s ease',
},
states: {
':hover': {
look: {
color: '#3b82f6',
}
}
}
},
Separator: {
look: {
color: '#d1d5db',
fontSize: 14,
userSelect: 'none',
}
},
Current: {
look: {
color: '#111827',
fontSize: 14,
fontWeight: 500,
}
}
},
render({ props, parts: { Root, Item, Separator, Current } }) {
return (
<Root>
{props.items?.map((item: any, index: number) => (
<>
{index === props.items.length - 1 ? (
<Current key={item.id}>{item.label}</Current>
) : (
<>
<Item key={item.id} href={item.href || '#'}>
{item.label}
</Item>
<Separator>/</Separator>
</>
)}
</>
))}
</Root>
)
}
})
export const NavigationExamplesPage = () => (
<Layout title="Forge Navigation Examples">
<ExampleSection title="Tabs">
<Tabs items={[
{ id: 1, label: 'Overview', active: true },
{ id: 2, label: 'Analytics', active: false },
{ id: 3, label: 'Reports', active: false },
{ id: 4, label: 'Settings', active: false },
]} />
</ExampleSection>
<ExampleSection title="Pills">
<Pills items={[
{ id: 1, label: 'All', active: true },
{ id: 2, label: 'Active', active: false },
{ id: 3, label: 'Pending', active: false },
{ id: 4, label: 'Archived', active: false },
]} />
</ExampleSection>
<ExampleSection title="Vertical Navigation">
<VerticalNav items={[
{ id: 1, label: 'Dashboard', icon: '📊', active: true, href: '#' },
{ id: 2, label: 'Projects', icon: '📁', active: false, href: '#' },
{ id: 3, label: 'Team', icon: '👥', active: false, href: '#' },
{ id: 4, label: 'Calendar', icon: '📅', active: false, href: '#' },
{ id: 5, label: 'Documents', icon: '📄', active: false, href: '#' },
{ id: 6, label: 'Settings', icon: '⚙️', active: false, href: '#' },
]} />
</ExampleSection>
<ExampleSection title="Breadcrumbs">
<Breadcrumbs items={[
{ id: 1, label: 'Home', href: '#' },
{ id: 2, label: 'Projects', href: '#' },
{ id: 3, label: 'Website Redesign', href: '#' },
{ id: 4, label: 'Design Assets' },
]} />
</ExampleSection>
</Layout>
)

View File

@ -2,21 +2,31 @@ import { Hono } from 'hono'
import { IndexPage } from './examples/index' import { IndexPage } from './examples/index'
import { ProfileExamplesPage } from './examples/profile' import { ProfileExamplesPage } from './examples/profile'
import { ButtonExamplesPage } from './examples/button' import { ButtonExamplesPage } from './examples/button'
import { NavigationExamplesPage } from './examples/navigation'
import { styles, stylesToCSS } from './src'
const app = new Hono() const app = new Hono()
app.get('/', (c) => { app.get('/', c => {
return c.html(<IndexPage />) return c.html(<IndexPage />)
}) })
app.get('/profile', (c) => { app.get('/profile', c => {
return c.html(<ProfileExamplesPage />) return c.html(<ProfileExamplesPage />)
}) })
app.get('/buttons', (c) => { app.get('/buttons', c => {
return c.html(<ButtonExamplesPage />) return c.html(<ButtonExamplesPage />)
}) })
app.get('/navigation', c => {
return c.html(<NavigationExamplesPage />)
})
app.get('/styles', c => {
return c.text(stylesToCSS(styles))
})
export default { export default {
port: 3300, port: 3300,
fetch: app.fetch, fetch: app.fetch,