Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
422 changes: 422 additions & 0 deletions .storybook/modules/toc.module.css

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"tags input",
"timer",
"toast",
"toc",
"toggle group",
"tooltip",
"tree view",
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/anatomy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export { tabsAnatomy } from './tabs/tabs.anatomy'
export { tagsInputAnatomy } from './tags-input/tags-input.anatomy'
export { timerAnatomy } from './timer/timer.anatomy'
export { toastAnatomy } from './toast/toast.anatomy'
export { tocAnatomy } from './toc/toc.anatomy'
export { toggleAnatomy } from './toggle/toggle.anatomy'
export { toggleGroupAnatomy } from './toggle-group/toggle-group.anatomy'
export { tooltipAnatomy } from './tooltip/tooltip.anatomy'
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export * from './tabs'
export * from './tags-input'
export * from './timer'
export * from './toast'
export * from './toc'
export * from './toggle'
export * from './toggle-group'
export * from './tooltip'
Expand Down
43 changes: 43 additions & 0 deletions packages/react/src/components/toc/examples/basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Toc } from '@ark-ui/react/toc'
import styles from 'styles/toc.module.css'
import { loremIpsum } from 'lorem-ipsum'

const items = [
{ value: 'introduction', depth: 5, label: 'Introduction' },
{ value: 'getting-started', depth: 2, label: 'Getting Started' },
{ value: 'installation', depth: 2, label: 'Installation' },
{ value: 'usage', depth: 2, label: 'Usage' },
{ value: 'configuration', depth: 2, label: 'Configuration' },
{ value: 'migration', depth: 2, label: 'Migration' },
{ value: 'faq', depth: 2, label: 'FAQ' },
{ value: 'troubleshooting', depth: 2, label: 'Troubleshooting' },
{ value: 'api-reference', depth: 2, label: 'API Reference' },
{ value: 'conclusion', depth: 2, label: 'Conclusion' },
]

const paragraphs = loremIpsum({ count: 6, units: 'paragraphs' })

export const Basic = () => (
<Toc.Root className={styles.Root} items={items}>
<Toc.Content className={styles.Content}>
{items.map((item) => (
<section key={item.value}>
<h2 id={item.value}>{item.label}</h2>
<p>{paragraphs}</p>
</section>
))}
</Toc.Content>
<Toc.Nav className={styles.Nav}>
<Toc.Title className={styles.Title}>On this page</Toc.Title>
<Toc.List className={styles.List}>
{items.map((item) => (
<Toc.Item className={styles.Item} key={item.value} item={item}>
<Toc.Link className={styles.Link} href={`#${item.value}`}>
{item.label}
</Toc.Link>
</Toc.Item>
))}
</Toc.List>
</Toc.Nav>
</Toc.Root>
)
61 changes: 61 additions & 0 deletions packages/react/src/components/toc/examples/grouped.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Toc } from '@ark-ui/react/toc'
import styles from 'styles/toc.module.css'
import { loremIpsum } from 'lorem-ipsum'

const groups = [
{
label: 'Getting Started',
items: [
{ value: 'overview', depth: 2, label: 'Overview' },
{ value: 'installation', depth: 2, label: 'Installation' },
],
},
{
label: 'Advanced',
items: [
{ value: 'configuration', depth: 2, label: 'Configuration' },
{ value: 'plugins', depth: 2, label: 'Plugins' },
],
},
{
label: 'Reference',
items: [
{ value: 'api', depth: 2, label: 'API' },
{ value: 'changelog', depth: 2, label: 'Changelog' },
],
},
]

const paragraphs = loremIpsum({ count: 5, units: 'paragraphs' })

const allItems = groups.flatMap((g) => g.items)

export const Grouped = () => (
<Toc.Root className={styles.Root} items={allItems}>
<Toc.Content className={styles.Content}>
{allItems.map((item) => (
<section key={item.value}>
<h2 id={item.value}>{item.label}</h2>
<p>{paragraphs}</p>
</section>
))}
</Toc.Content>

<Toc.Nav className={styles.Nav}>
{groups.map((group) => (
<div key={group.label} className={styles.Group}>
<span className={styles.GroupLabel}>{group.label}</span>
<Toc.List className={styles.List}>
{group.items.map((item) => (
<Toc.Item className={styles.Item} key={item.value} item={item}>
<Toc.Link className={styles.Link} href={`#${item.value}`}>
{item.label}
</Toc.Link>
</Toc.Item>
))}
</Toc.List>
</div>
))}
</Toc.Nav>
</Toc.Root>
)
45 changes: 45 additions & 0 deletions packages/react/src/components/toc/examples/nested.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Toc } from '@ark-ui/react/toc'
import styles from 'styles/toc.module.css'
import { loremIpsum } from 'lorem-ipsum'

const paragraphs = loremIpsum({ count: 5, units: 'paragraphs' })

const items = [
{ value: 'introduction', depth: 2, label: 'Introduction' },
{ value: 'getting-started', depth: 2, label: 'Getting Started' },
{ value: 'installation', depth: 3, label: 'Installation' },
{ value: 'configuration', depth: 3, label: 'Configuration' },
{ value: 'api-reference', depth: 2, label: 'API Reference' },
{ value: 'hooks', depth: 3, label: 'Hooks' },
{ value: 'components', depth: 3, label: 'Components' },
{ value: 'examples', depth: 2, label: 'Examples' },
]

export const Nested = () => (
<Toc.Root className={styles.Root} items={items}>
<Toc.Content className={styles.Content}>
{items.map((item) => {
const Heading = item.depth === 2 ? 'h2' : 'h3'
return (
<section key={item.value}>
<Heading id={item.value}>{item.label}</Heading>
<p>{paragraphs}</p>
</section>
)
})}
</Toc.Content>

<Toc.Nav className={styles.Nav}>
<Toc.Title className={styles.Title}>On this page</Toc.Title>
<Toc.List className={styles.List}>
{items.map((item) => (
<Toc.Item className={item.depth > 2 ? styles.ItemNested : styles.Item} key={item.value} item={item}>
<Toc.Link className={styles.Link} href={`#${item.value}`}>
{item.label}
</Toc.Link>
</Toc.Item>
))}
</Toc.List>
</Toc.Nav>
</Toc.Root>
)
50 changes: 50 additions & 0 deletions packages/react/src/components/toc/examples/root-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Toc, useToc } from '@ark-ui/react/toc'
import styles from 'styles/toc.module.css'
import { loremIpsum } from 'lorem-ipsum'

const items = [
{ value: 'introduction', depth: 2, label: 'Introduction' },
{ value: 'getting-started', depth: 2, label: 'Getting Started' },
{ value: 'installation', depth: 2, label: 'Installation' },
{ value: 'usage', depth: 2, label: 'Usage' },
{ value: 'configuration', depth: 2, label: 'Configuration' },
{ value: 'migration', depth: 2, label: 'Migration' },
{ value: 'faq', depth: 2, label: 'FAQ' },
{ value: 'troubleshooting', depth: 2, label: 'Troubleshooting' },
{ value: 'api-reference', depth: 2, label: 'API Reference' },
{ value: 'conclusion', depth: 2, label: 'Conclusion' },
]

const paragraphs = loremIpsum({ count: 6, units: 'paragraphs' })

export const RootProvider = () => {
const toc = useToc({ items })

return (
<Toc.RootProvider className={styles.Root} value={toc}>
<Toc.Content className={styles.Content}>
{items.map((item) => (
<section key={item.value}>
<h2 id={item.value}>{item.label}</h2>
<p>{paragraphs}</p>
</section>
))}
</Toc.Content>

<Toc.Nav className={styles.Nav}>
<p className={styles.ActiveIds}>Active: {toc.activeIds.length > 0 ? toc.activeIds.join(', ') : '—'}</p>
<Toc.Title className={styles.Title}>On this page</Toc.Title>
<Toc.List className={styles.List}>
<Toc.Indicator className={styles.Indicator} />
{items.map((item) => (
<Toc.Item className={styles.Item} key={item.value} item={item}>
<Toc.Link className={styles.Link} href={`#${item.value}`}>
{item.label}
</Toc.Link>
</Toc.Item>
))}
</Toc.List>
</Toc.Nav>
</Toc.RootProvider>
)
}
108 changes: 108 additions & 0 deletions packages/react/src/components/toc/examples/with-collapsible.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { Collapsible } from '@ark-ui/react/collapsible'
import { Toc } from '@ark-ui/react/toc'
import { loremIpsum } from 'lorem-ipsum'
import { ChevronRightIcon } from 'lucide-react'
import CollapsibleStyles from 'styles/Collapsible.module.css'
import styles from 'styles/toc.module.css'

const p = loremIpsum({ count: 7, units: 'paragraphs' })

const items = [
{ value: 'introduction', depth: 2, label: 'Introduction' },
{ value: 'installation', depth: 2, label: 'Installation' },
{ value: 'usage', depth: 2, label: 'Usage' },
{ value: 'api-reference', depth: 2, label: 'API Reference' },
{ value: 'examples', depth: 2, label: 'Examples' },
]

const RADIUS = 14
const CIRCUMFERENCE = 2 * Math.PI * RADIUS

export const WithCollapsible = () => (
<Toc.Root className={`${styles.Root} ${styles.RootStacked}`} items={items}>
<Toc.Content className={styles.Content}>
{items.map((item) => (
<section key={item.value}>
<h2 id={item.value}>{item.label}</h2>
<p>{p}</p>
</section>
))}
</Toc.Content>

<Toc.Nav className={styles.Nav}>
<Collapsible.Root className={CollapsibleStyles.Root} style={{ width: '100%' }}>
<Toc.Context>
{({ activeItems }) => {
const activeIndex = activeItems[0] ? items.findIndex((i) => i.value === activeItems[0].value) : -1
const activeLabel = activeIndex >= 0 ? items[activeIndex].label : undefined
const progress = activeIndex >= 0 ? (activeIndex + 1) / items.length : 0
const dashArray = `${progress * CIRCUMFERENCE} ${CIRCUMFERENCE}`

return (
<Collapsible.Trigger className={CollapsibleStyles.Trigger}>
<span className={styles.TriggerContent}>
<span className={styles.ProgressRing}>
<svg width="28" height="28" viewBox="0 0 36 36" aria-hidden="true">
<circle
cx="18"
cy="18"
r={RADIUS}
fill="none"
stroke="currentColor"
strokeOpacity="0.2"
strokeWidth="2.5"
/>
<circle
cx="18"
cy="18"
r={RADIUS}
fill="none"
stroke="var(--demo-coral-solid)"
strokeWidth="2.5"
strokeDasharray={dashArray}
strokeLinecap="round"
transform="rotate(-90 18 18)"
style={{ transition: 'stroke-dasharray 0.4s ease-out' }}
/>
<text
key={activeIndex}
x="18"
y="18"
textAnchor="middle"
dominantBaseline="central"
fontSize="10"
fontWeight="600"
fill="currentColor"
className={styles.ProgressIndexText}
>
{activeIndex >= 0 ? activeIndex + 1 : '—'}
</text>
</svg>
</span>
<span key={activeLabel ?? ''} className={styles.TriggerLabel}>
{activeLabel ?? 'On this page'}
</span>
</span>
<Collapsible.Indicator className={CollapsibleStyles.Indicator}>
<ChevronRightIcon />
</Collapsible.Indicator>
</Collapsible.Trigger>
)
}}
</Toc.Context>
<Collapsible.Content className={CollapsibleStyles.Content}>
<Toc.List className={styles.List}>
{items.map((item, index) => (
<Toc.Item className={styles.Item} key={item.value} item={item}>
<Toc.Link className={styles.LinkNumbered} href={`#${item.value}`}>
<span className={styles.Number}>{String(index + 1).padStart(2, '0')}</span>
{item.label}
</Toc.Link>
</Toc.Item>
))}
</Toc.List>
</Collapsible.Content>
</Collapsible.Root>
</Toc.Nav>
</Toc.Root>
)
Loading
Loading