b373abf605
Adds descriptions to examples. ![Kapture 2023-12-22 at 17 08 32](https://github.com/tldraw/tldraw/assets/1489520/d78657cf-b3c3-4160-b58b-7c08ed27823d) They show as a list on the index page, and on individual examples they show in a three-js style sidebar. For now, this is disabled completely on mobile. Examples can still be opened in 'standalone' mode to get rid of the sidebar. Note: the 'view code' link won't work until after these changes are merged. There's a small impact on authoring examples: each one needs to live in a folder with a README.md. At a minimum, the readme needs to look like this: ```md --- title: My Example component: ./MyExample.tsx --- Here is a 1-liner about my example ``` Optionally, you can: - Add `hide: true` to the frontmatter to remove the example from the list (you can skip the description this way) - Add `order: 3` to control the order in which the example appears. They're alphabetical otherwise - Add some more description or links to docs below a `---`. This won't show in the listing, but will be visible on GitHub and on the example page itself. As a follow-up, I'd like to add an 'Open in CodeSandbox' link to each example. These won't work until we've made a release with these examples (as our special examples codesandbox is tied to our release process) but the code is there & ready to go! Have a play, let me know what you think! ### Change Type - [x] `documentation` — Changes to the documentation only[^2] --------- Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
79 lines
2.1 KiB
TypeScript
79 lines
2.1 KiB
TypeScript
import { assert, assertExists } from '@tldraw/tldraw'
|
|
import { useEffect, useRef } from 'react'
|
|
import { Link } from 'react-router-dom'
|
|
import ExamplesTldrawLogo from './components/ExamplesTldrawLogo'
|
|
import { ListLink } from './components/ListLink'
|
|
import { Example, examples } from './examples'
|
|
|
|
export function ExamplePage({
|
|
example,
|
|
children,
|
|
}: {
|
|
example: Example
|
|
children: React.ReactNode
|
|
}) {
|
|
const scrollElRef = useRef<HTMLUListElement>(null)
|
|
const activeElRef = useRef<HTMLLIElement>(null)
|
|
const isFirstScroll = useRef(true)
|
|
|
|
useEffect(() => {
|
|
const frame = requestAnimationFrame(() => {
|
|
if (activeElRef.current) {
|
|
const scrollEl = assertExists(scrollElRef.current)
|
|
const activeEl = activeElRef.current
|
|
assert(activeEl.offsetParent === scrollEl)
|
|
|
|
const isScrolledIntoView =
|
|
activeEl.offsetTop >= scrollEl.scrollTop &&
|
|
activeEl.offsetTop + activeEl.offsetHeight <= scrollEl.scrollTop + scrollEl.offsetHeight
|
|
|
|
if (!isScrolledIntoView) {
|
|
activeEl.scrollIntoView({
|
|
behavior: isFirstScroll.current ? 'auto' : 'smooth',
|
|
block: isFirstScroll.current ? 'start' : 'center',
|
|
})
|
|
}
|
|
isFirstScroll.current = false
|
|
}
|
|
})
|
|
return () => cancelAnimationFrame(frame)
|
|
}, [example])
|
|
|
|
return (
|
|
<div className="example">
|
|
<div className="example__info">
|
|
<Link className="example__logo" to="/">
|
|
<ExamplesTldrawLogo /> examples
|
|
</Link>
|
|
<ul className="example__info__list scroll-light" ref={scrollElRef}>
|
|
{examples
|
|
.filter((e) => !e.hide)
|
|
.filter((e) => e.order !== null)
|
|
.map((e) => (
|
|
<ListLink
|
|
key={e.path}
|
|
ref={e.path === example.path ? activeElRef : undefined}
|
|
example={e}
|
|
isActive={e.path === example.path}
|
|
/>
|
|
))}
|
|
<li>
|
|
<hr />
|
|
</li>
|
|
{examples
|
|
.filter((e) => !e.hide)
|
|
.filter((e) => e.order === null)
|
|
.map((e) => (
|
|
<ListLink
|
|
key={e.path}
|
|
ref={e.path === example.path ? activeElRef : undefined}
|
|
example={e}
|
|
isActive={e.path === example.path}
|
|
/>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
<div className="example__content">{children}</div>
|
|
</div>
|
|
)
|
|
}
|