Grouping examples into categories (#2585)
This PR adds collapsible groups to the examples app. it's not finished, but I'd like a review before continuing as I've made a few decisions I'd like feedback on. I'd like to make a separate issue for abstracting the accordion component, as I wasn't sure how I would do it and I thought it would be best to prioritise the functionality first. Especially considering there are more pressing issues to be getting on with. ### Change Type - [ ] `patch` — Bug fix - [ ] `minor` — New feature - [ ] `major` — Breaking change - [ ] `dependencies` — Changes to package dependencies[^1] - [x] `documentation` — Changes to the documentation only[^2] - [ ] `tests` — Changes to any test code only[^2] - [ ] `internal` — Any other changes that don't affect the published package[^2] - [ ] I don't know [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Release Notes - Add collapsible categories to the examples app --------- Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
parent
3a3248a636
commit
f25f92a46d
45 changed files with 326 additions and 111 deletions
|
@ -35,6 +35,7 @@
|
|||
"dependencies": {
|
||||
"@babel/plugin-proposal-decorators": "^7.21.0",
|
||||
"@playwright/test": "^1.38.1",
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
"@tldraw/assets": "workspace:*",
|
||||
"@tldraw/tldraw": "workspace:*",
|
||||
"@vercel/analytics": "^1.1.1",
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import * as Accordion from '@radix-ui/react-accordion'
|
||||
import { assert, assertExists } from '@tldraw/tldraw'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { ExamplesLink } from './components/ExamplesLink'
|
||||
import ExamplesTldrawLogo from './components/ExamplesTldrawLogo'
|
||||
import { ListLink } from './components/ListLink'
|
||||
import { Chevron } from './components/Icons'
|
||||
import { Example, examples } from './examples'
|
||||
|
||||
export function ExamplePage({
|
||||
|
@ -12,7 +14,7 @@ export function ExamplePage({
|
|||
example: Example
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const scrollElRef = useRef<HTMLUListElement>(null)
|
||||
const scrollElRef = useRef<HTMLDivElement>(null)
|
||||
const activeElRef = useRef<HTMLLIElement>(null)
|
||||
const isFirstScroll = useRef(true)
|
||||
|
||||
|
@ -39,6 +41,8 @@ export function ExamplePage({
|
|||
return () => cancelAnimationFrame(frame)
|
||||
}, [example])
|
||||
|
||||
const categories = examples.map((e) => e.id)
|
||||
|
||||
return (
|
||||
<div className="example">
|
||||
<div className="example__info">
|
||||
|
@ -70,33 +74,38 @@ export function ExamplePage({
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
<Accordion.Root
|
||||
type="multiple"
|
||||
defaultValue={categories}
|
||||
className="example__info__list scroll-light"
|
||||
ref={scrollElRef}
|
||||
>
|
||||
{categories.map((currentCategory) => (
|
||||
<Accordion.Item key={currentCategory} value={currentCategory}>
|
||||
<Accordion.Trigger className="accordion__trigger">
|
||||
<div className="examples__list__item accordion__trigger__container">
|
||||
<h3 className="accordion__trigger__heading">{currentCategory}</h3>
|
||||
<Chevron />
|
||||
</div>
|
||||
</Accordion.Trigger>
|
||||
<Accordion.Content className="accordion__content">
|
||||
<span className="accordion__content__separator"></span>
|
||||
<div className="accordion__content__examples">
|
||||
{examples
|
||||
.find((category) => category.id === currentCategory)
|
||||
?.value.map((sidebarExample) => (
|
||||
<ExamplesLink
|
||||
key={sidebarExample.path}
|
||||
example={sidebarExample}
|
||||
isActive={sidebarExample.path === example.path}
|
||||
ref={sidebarExample.path === example.path ? activeElRef : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Accordion.Content>
|
||||
</Accordion.Item>
|
||||
))}
|
||||
</Accordion.Root>
|
||||
<div className="example__info__list__link">
|
||||
<a
|
||||
className="link__button link__button--grey"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ExamplesLink } from './components/ExamplesLink'
|
||||
import ExamplesTldrawLogo from './components/ExamplesTldrawLogo'
|
||||
import { ListLink } from './components/ListLink'
|
||||
import { examples } from './examples'
|
||||
|
||||
export function HomePage() {
|
||||
|
@ -14,22 +14,11 @@ export function HomePage() {
|
|||
</p>
|
||||
</div>
|
||||
<ul className="examples__list">
|
||||
{examples
|
||||
.filter((example) => !example.hide)
|
||||
.filter((example) => example.order !== null)
|
||||
.map((example) => (
|
||||
<ListLink key={example.path} example={example} showDescriptionWhenInactive />
|
||||
))}
|
||||
{examples.map((e) =>
|
||||
e.value.map((e) => <ExamplesLink key={e.path} example={e} showDescriptionWhenInactive />)
|
||||
)}
|
||||
</ul>
|
||||
<hr />
|
||||
<ul className="examples__list">
|
||||
{examples
|
||||
.filter((example) => !example.hide)
|
||||
.filter((example) => example.order === null)
|
||||
.map((example) => (
|
||||
<ListLink key={example.path} example={example} showDescriptionWhenInactive />
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import { useMergedRefs } from '../hooks/useMergedRefs'
|
|||
import { StandaloneIcon } from './Icons'
|
||||
import { Markdown } from './Markdown'
|
||||
|
||||
export const ListLink = forwardRef(function ListLink(
|
||||
export const ExamplesLink = forwardRef(function ListLink(
|
||||
{
|
||||
example,
|
||||
isActive,
|
||||
|
@ -40,7 +40,7 @@ export const ListLink = forwardRef(function ListLink(
|
|||
|
||||
const mainDetails = (
|
||||
<>
|
||||
<h3 id={id}>
|
||||
<h3 className="examples__list__item__heading" id={id}>
|
||||
{example.title}
|
||||
{isActive && (
|
||||
<Link
|
||||
|
@ -81,7 +81,7 @@ export const ListLink = forwardRef(function ListLink(
|
|||
)
|
||||
|
||||
return (
|
||||
<li
|
||||
<span
|
||||
ref={useMergedRefs(ref, containerRef)}
|
||||
className={classNames('examples__list__item', isActive && 'examples__list__item__active')}
|
||||
>
|
||||
|
@ -90,6 +90,6 @@ export const ListLink = forwardRef(function ListLink(
|
|||
)}
|
||||
{mainDetails}
|
||||
{extraDetails}
|
||||
</li>
|
||||
</span>
|
||||
)
|
||||
})
|
|
@ -11,3 +11,24 @@ export function StandaloneIcon(props: React.SVGProps<SVGSVGElement>) {
|
|||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function Chevron() {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="accordion__trigger__chevron"
|
||||
>
|
||||
<path
|
||||
d="M4 6L8 10L12 6"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,22 +7,32 @@ export type Example = {
|
|||
path: string
|
||||
codeUrl: string
|
||||
hide: boolean
|
||||
order: number | null
|
||||
category: Category
|
||||
priority: number
|
||||
componentFile: string
|
||||
loadComponent: () => Promise<ComponentType>
|
||||
}
|
||||
|
||||
export const examples = (
|
||||
Object.values(import.meta.glob('./examples/*/README.md', { eager: true })) as Example[]
|
||||
).sort((a, b) => {
|
||||
// sort by order then title:
|
||||
if (a.order === b.order) {
|
||||
return a.title.localeCompare(b.title)
|
||||
} else if (a.order === null) {
|
||||
return 1
|
||||
} else if (b.order === null) {
|
||||
return -1
|
||||
} else {
|
||||
return a.order - b.order
|
||||
}
|
||||
})
|
||||
type Category = 'basic' | 'editor' | 'ui' | 'collaboration' | 'data/assets' | 'shapes/tools'
|
||||
|
||||
const getExamplesForCategory = (category: Category) =>
|
||||
(Object.values(import.meta.glob('./examples/*/README.md', { eager: true })) as Example[])
|
||||
.filter((e) => e.category === category)
|
||||
.sort((a, b) => {
|
||||
if (a.priority === b.priority) return a.title.localeCompare(b.title)
|
||||
return a.priority - b.priority
|
||||
})
|
||||
|
||||
const categories: Record<Category, string> = {
|
||||
basic: 'Getting Started',
|
||||
ui: 'UI/Theming',
|
||||
'shapes/tools': 'Shapes & Tools',
|
||||
'data/assets': 'Data & Assets',
|
||||
editor: 'Editor API',
|
||||
collaboration: 'Collaboration',
|
||||
}
|
||||
|
||||
export const examples = Object.entries(categories).map(([category, title]) => ({
|
||||
id: title,
|
||||
value: getExamplesForCategory(category as Category),
|
||||
}))
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
---
|
||||
title: Editor API
|
||||
component: ./APIExample.tsx
|
||||
order: 2
|
||||
category: editor
|
||||
priority: 1
|
||||
---
|
||||
|
||||
Manipulate the contents of the canvas using the editor API.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Asset props
|
||||
component: ./AssetPropsExample.tsx
|
||||
category: data/assets
|
||||
priority: 1
|
||||
---
|
||||
|
||||
Control the assets (images, videos, etc.) that can be added to the canvas.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Canvas events
|
||||
component: ./CanvasEventsExample.tsx
|
||||
category: editor
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Listen to events from tldraw's canvas.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Changing default colors
|
||||
component: ./ChangingDefaultColorsExample.tsx
|
||||
category: ui
|
||||
priority: 1
|
||||
---
|
||||
|
||||
Change the tldraw theme colors.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Context Toolbar
|
||||
component: ./ContextToolbar.tsx
|
||||
category: UI
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Show a contextual toolbar above the shapes when they are selected.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Canvas components
|
||||
component: ./CustomComponentsExample.tsx
|
||||
category: ui
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Replace tldraw's on-canvas UI with your own.
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
---
|
||||
title: Custom shapes / tools
|
||||
component: ./CustomConfigExample.tsx
|
||||
order: 3
|
||||
category: shapes/tools
|
||||
priority: 1
|
||||
---
|
||||
|
||||
Create custom shapes / tools
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Custom styles
|
||||
component: ./CustomStylesExample.tsx
|
||||
category: shapes/tools
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Styles are special properties that can be set on many shapes at once.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Custom UI
|
||||
component: ./CustomUiExample.tsx
|
||||
category: ui
|
||||
priority: 1
|
||||
---
|
||||
|
||||
Replace tldraw's UI with your own.
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
---
|
||||
title: Basic
|
||||
component: ./BasicExample.tsx
|
||||
order: 1
|
||||
category: basic
|
||||
priority: 1
|
||||
---
|
||||
|
||||
The easiest way to get started with tldraw.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Error boundary
|
||||
component: ./ErrorBoundaryExample.tsx
|
||||
category: ui
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Catch errors in shapes.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Sublibraries
|
||||
component: ./ExplodedExample.tsx
|
||||
category: editor
|
||||
priority: 3
|
||||
---
|
||||
|
||||
Tldraw is built from several sublibraries - like the editor, default shapes & tools, and UI.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: External content sources
|
||||
component: ./ExternalContentSourcesExample.tsx
|
||||
category: data/assets
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Control what happens when the user pastes content into the editor.
|
||||
|
|
|
@ -2,4 +2,6 @@
|
|||
title: Floaty window
|
||||
hide: true
|
||||
component: ./FloatyExample.tsx
|
||||
category: UI
|
||||
priority: 3
|
||||
---
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Force mobile breakpoint
|
||||
component: ./ForceBreakpointExample
|
||||
category: basic
|
||||
priority: 3
|
||||
---
|
||||
|
||||
Force the editor UI to render as if it were on a mobile device.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Hide UI
|
||||
component: ./HideUiExample.tsx
|
||||
category: basic
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Hide tldraw's UI with the `hideUi` prop.
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
---
|
||||
title: Hosted images
|
||||
component: ./HostedImagesExample.tsx
|
||||
category: data/assets
|
||||
priority: 2
|
||||
hide: true
|
||||
---
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Keyboard Shortcuts
|
||||
component: ./KeyboardShortcuts.tsx
|
||||
category: ui
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Override default keyboard shortcuts.
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
---
|
||||
title: Local images
|
||||
component: ./LocalImagesExample.tsx
|
||||
hide: false
|
||||
category: data/assets
|
||||
priority: 2
|
||||
---
|
||||
|
||||
How to use local images in the built-in `ImageShape` shape.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Shape Meta (on change)
|
||||
component: ./OnChangeShapeMetaExample.tsx
|
||||
category: data/assets
|
||||
priority: 3
|
||||
---
|
||||
|
||||
Add custom metadata to shapes when they're changed.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Shape Meta (on create)
|
||||
component: ./OnCreateShapeMetaExample.tsx
|
||||
category: data/assets
|
||||
priority: 3
|
||||
---
|
||||
|
||||
Add custom metadata to shapes when they're created.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Multiple editors
|
||||
component: ./MultipleExample.tsx
|
||||
category: basic
|
||||
priority: 3
|
||||
---
|
||||
|
||||
Use multiple <Tldraw/> components on the same page.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Minimal
|
||||
component: ./OnlyEditor.tsx
|
||||
category: editor
|
||||
priority: 3
|
||||
---
|
||||
|
||||
Use the `<TldrawEditor/>` component to render a bare-bones editor with minimal built-in shapes and tools.
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
---
|
||||
title: Persistence
|
||||
component: ./PersistenceExample.tsx
|
||||
order: 5
|
||||
category: collaboration
|
||||
priority: 1
|
||||
---
|
||||
|
||||
Save the contents of the editor
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Readonly
|
||||
component: ./ReadOnlyExample
|
||||
category: basic
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Use the editor in readonly mode.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Custom tool (screenshot)
|
||||
component: ./ScreenshotToolExample.tsx
|
||||
category: shapes/tools
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Draw a box on the canvas to capture a screenshot of that area.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Scrolling container
|
||||
component: ./ScrollExample.tsx
|
||||
category: basic
|
||||
priority: 1
|
||||
---
|
||||
|
||||
Use the editor inside a scrollable container.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Shape meta
|
||||
component: ./ShapeMetaExample.tsx
|
||||
category: data/assets
|
||||
priority: 3
|
||||
---
|
||||
|
||||
Add a label to shapes with the meta property.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Snapshots
|
||||
component: ./SnapshotExample.tsx
|
||||
category: editor
|
||||
priority: 1
|
||||
---
|
||||
|
||||
Load a snapshot of the editor's contents.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Speech bubble
|
||||
component: ./CustomShapeWithHandles.tsx
|
||||
category: shapes/tools
|
||||
priority: 2
|
||||
---
|
||||
|
||||
A custom shape with handles
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Store events
|
||||
component: ./StoreEventsExample.tsx
|
||||
category: editor
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Listen to changes from tldraw's store.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Things on the canvas
|
||||
component: ./OnTheCanvas.tsx
|
||||
category: ui
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Add custom components to the editor
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: UI events
|
||||
component: ./UiEventsExample.tsx
|
||||
category: editor
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Listen to events from tldraw's UI.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: User Presence
|
||||
component: ./UserPresenceExample.tsx
|
||||
category: collaboration
|
||||
priority: 2
|
||||
---
|
||||
|
||||
Show other users editing the same document.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: UI zones
|
||||
component: ./ZonesExample.tsx
|
||||
category: ui
|
||||
priority: 1
|
||||
---
|
||||
|
||||
Inject custom components into tldraw's UI.
|
||||
|
|
|
@ -29,32 +29,33 @@ const router = createBrowserRouter([
|
|||
path: 'end-to-end',
|
||||
element: <EndToEnd />,
|
||||
},
|
||||
...examples.flatMap((example) => [
|
||||
{
|
||||
path: example.path,
|
||||
lazy: async () => {
|
||||
const Component = await example.loadComponent()
|
||||
return {
|
||||
element: (
|
||||
<ExamplePage example={example}>
|
||||
<Component />
|
||||
</ExamplePage>
|
||||
),
|
||||
}
|
||||
...examples.flatMap((exampleArray) =>
|
||||
exampleArray.value.flatMap((example) => [
|
||||
{
|
||||
path: example.path,
|
||||
lazy: async () => {
|
||||
const Component = await example.loadComponent()
|
||||
return {
|
||||
element: (
|
||||
<ExamplePage example={example}>
|
||||
<Component />
|
||||
</ExamplePage>
|
||||
),
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: `${example.path}/full`,
|
||||
lazy: async () => {
|
||||
const Component = await example.loadComponent()
|
||||
return {
|
||||
element: <Component />,
|
||||
}
|
||||
{
|
||||
path: `${example.path}/full`,
|
||||
lazy: async () => {
|
||||
const Component = await example.loadComponent()
|
||||
return {
|
||||
element: <Component />,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
]),
|
||||
])
|
||||
),
|
||||
])
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const rootElement = document.getElementById('root')!
|
||||
const root = createRoot(rootElement!)
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap');
|
||||
|
||||
:root {
|
||||
--gray-light: #f5f5f5;
|
||||
--gray-dark: #e8e8e8;
|
||||
--black-transparent-light: rgba(0, 0, 0, 0.3);
|
||||
--black-transparent-dark: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
|
@ -39,7 +46,7 @@ html,
|
|||
|
||||
.examples hr {
|
||||
border: none;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
border-top: 1px solid var(--gray-dark);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
@ -72,8 +79,6 @@ html,
|
|||
grid-template-columns: repeat(1, 1fr);
|
||||
padding: 0;
|
||||
margin: 0 -1.5rem 0 -1.5rem;
|
||||
list-style: none;
|
||||
gap: 0rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
|
@ -88,12 +93,57 @@ html,
|
|||
}
|
||||
}
|
||||
|
||||
.accordion__trigger {
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.accordion__trigger__chevron {
|
||||
color:var(--black-transparent-light);
|
||||
transition: transform 300ms;
|
||||
}
|
||||
|
||||
.accordion__trigger[data-state='closed'] > div > .accordion__trigger__chevron {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
|
||||
.accordion__trigger__heading {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--black-transparent-dark);
|
||||
margin-top: 0.25rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.accordion__content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.accordion__content__separator {
|
||||
border-right: 1px solid var(--gray-dark);
|
||||
opacity: 0.5;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
.accordion__content__examples {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.examples__list__item {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding: 1.5rem;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.examples__list__item__link {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
@ -105,8 +155,8 @@ html,
|
|||
padding: 1.5rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.examples__list__item::before {
|
||||
|
@ -114,7 +164,7 @@ html,
|
|||
position: absolute;
|
||||
inset: 0.5rem;
|
||||
border-radius: 6px;
|
||||
background: #f5f5f5;
|
||||
background: var(--gray-light);
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.12s ease-in-out;
|
||||
|
@ -128,10 +178,10 @@ html,
|
|||
}
|
||||
}
|
||||
|
||||
.examples__list__item h3 {
|
||||
.examples__list__item__heading {
|
||||
all: unset;
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
font-size: 14px;
|
||||
margin-top: 0.25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -200,11 +250,10 @@ html,
|
|||
align-items: stretch;
|
||||
}
|
||||
.example__info {
|
||||
width: 25vw;
|
||||
max-width: 300px;
|
||||
width: 300px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
border-right: 1px solid var(--gray-dark);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
@ -246,6 +295,7 @@ html,
|
|||
position: relative;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.example__info__list .examples__list__item {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
@ -253,9 +303,7 @@ html,
|
|||
inset-block: 0.25rem;
|
||||
inset-inline: 0.5rem;
|
||||
}
|
||||
.example__info__list h3 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.example__content {
|
||||
flex: 1 1 auto;
|
||||
position: relative;
|
||||
|
@ -297,15 +345,23 @@ html,
|
|||
background-color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.scroll-light::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
background-color: var(--black-transparent-light);
|
||||
}
|
||||
|
||||
.example__info__list hr {
|
||||
border: none;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
border-top: 1px solid var(--gray-dark);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.accordion__trigger__container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: var(--black-transparent-dark);
|
||||
}
|
||||
|
||||
.example__info__list__link {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
@ -51,7 +51,8 @@ function exampleReadmePlugin(): PluginOption {
|
|||
|
||||
const result = [
|
||||
`export const title = ${JSON.stringify(frontmatter.title)};`,
|
||||
`export const order = ${JSON.stringify(frontmatter.order)};`,
|
||||
`export const priority = ${JSON.stringify(frontmatter.priority)};`,
|
||||
`export const category = ${JSON.stringify(frontmatter.category)};`,
|
||||
`export const hide = ${JSON.stringify(frontmatter.hide)};`,
|
||||
`export const description = ${JSON.stringify(description)};`,
|
||||
`export const details = ${JSON.stringify(details)};`,
|
||||
|
@ -82,9 +83,14 @@ function parseFrontMatter(data: unknown, fileName: string) {
|
|||
throw new Error(`Frontmatter key 'component' must be string in ${fileName}`)
|
||||
}
|
||||
|
||||
const order = 'order' in data ? data.order : null
|
||||
if (order !== null && typeof order !== 'number') {
|
||||
throw new Error(`Frontmatter key 'order' must be number in ${fileName}`)
|
||||
const priority = 'priority' in data ? data.priority : null
|
||||
if (typeof priority !== 'number') {
|
||||
throw new Error(`Frontmatter key 'priority' must be number in ${fileName}`)
|
||||
}
|
||||
|
||||
const category = 'category' in data ? data.category : null
|
||||
if (typeof category !== 'string') {
|
||||
throw new Error(`Frontmatter key 'category' must be string in ${fileName}`)
|
||||
}
|
||||
|
||||
const hide = 'hide' in data ? data.hide : false
|
||||
|
@ -95,7 +101,8 @@ function parseFrontMatter(data: unknown, fileName: string) {
|
|||
return {
|
||||
title: data.title,
|
||||
component: data.component,
|
||||
order,
|
||||
priority,
|
||||
category,
|
||||
hide,
|
||||
}
|
||||
}
|
||||
|
|
56
yarn.lock
56
yarn.lock
|
@ -4604,6 +4604,34 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-accordion@npm:^1.1.2":
|
||||
version: 1.1.2
|
||||
resolution: "@radix-ui/react-accordion@npm:1.1.2"
|
||||
dependencies:
|
||||
"@babel/runtime": "npm:^7.13.10"
|
||||
"@radix-ui/primitive": "npm:1.0.1"
|
||||
"@radix-ui/react-collapsible": "npm:1.0.3"
|
||||
"@radix-ui/react-collection": "npm:1.0.3"
|
||||
"@radix-ui/react-compose-refs": "npm:1.0.1"
|
||||
"@radix-ui/react-context": "npm:1.0.1"
|
||||
"@radix-ui/react-direction": "npm:1.0.1"
|
||||
"@radix-ui/react-id": "npm:1.0.1"
|
||||
"@radix-ui/react-primitive": "npm:1.0.3"
|
||||
"@radix-ui/react-use-controllable-state": "npm:1.0.1"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
"@types/react-dom": "*"
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
"@types/react-dom":
|
||||
optional: true
|
||||
checksum: 3c2b8fc686b3c6bc6f81e567c1d3933b8ffb35c060eeee113237ee69121b5e4d7c48bb354dbd2626bd101c1f6a1b6612e8cde2de8f72519732fb6c1a1d4cac28
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-alert-dialog@npm:^1.0.0":
|
||||
version: 1.0.5
|
||||
resolution: "@radix-ui/react-alert-dialog@npm:1.0.5"
|
||||
|
@ -4649,6 +4677,33 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-collapsible@npm:1.0.3":
|
||||
version: 1.0.3
|
||||
resolution: "@radix-ui/react-collapsible@npm:1.0.3"
|
||||
dependencies:
|
||||
"@babel/runtime": "npm:^7.13.10"
|
||||
"@radix-ui/primitive": "npm:1.0.1"
|
||||
"@radix-ui/react-compose-refs": "npm:1.0.1"
|
||||
"@radix-ui/react-context": "npm:1.0.1"
|
||||
"@radix-ui/react-id": "npm:1.0.1"
|
||||
"@radix-ui/react-presence": "npm:1.0.1"
|
||||
"@radix-ui/react-primitive": "npm:1.0.3"
|
||||
"@radix-ui/react-use-controllable-state": "npm:1.0.1"
|
||||
"@radix-ui/react-use-layout-effect": "npm:1.0.1"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
"@types/react-dom": "*"
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
"@types/react-dom":
|
||||
optional: true
|
||||
checksum: e9c90f9c9f4bcf8beac7d57cf09d5bf3eb99f868b17bd53025b7c81ffcf500efbba9cd92d137661efd7d191f29609e986e3b0577b11a6000e6b513e4403ebb09
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-collection@npm:1.0.3":
|
||||
version: 1.0.3
|
||||
resolution: "@radix-ui/react-collection@npm:1.0.3"
|
||||
|
@ -13018,6 +13073,7 @@ __metadata:
|
|||
dependencies:
|
||||
"@babel/plugin-proposal-decorators": "npm:^7.21.0"
|
||||
"@playwright/test": "npm:^1.38.1"
|
||||
"@radix-ui/react-accordion": "npm:^1.1.2"
|
||||
"@tldraw/assets": "workspace:*"
|
||||
"@tldraw/tldraw": "workspace:*"
|
||||
"@vercel/analytics": "npm:^1.1.1"
|
||||
|
|
Loading…
Reference in a new issue