remove docs (again) (#1643)
This PR removes the docs site (again) which suggests that git may have been confused about new content. ### Change Type - [x] `documentation` — Changes to the documentation only
This commit is contained in:
parent
096df3209b
commit
51406d2d81
11 changed files with 0 additions and 1678 deletions
|
@ -1,44 +0,0 @@
|
||||||
import { Article, Category, Section } from '@/types/content-types'
|
|
||||||
import Link from 'next/link'
|
|
||||||
|
|
||||||
export function Breadcrumb({
|
|
||||||
section,
|
|
||||||
category,
|
|
||||||
article,
|
|
||||||
}: {
|
|
||||||
section?: Section
|
|
||||||
category?: Category
|
|
||||||
article?: Article
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div className="breadcrumb">
|
|
||||||
<Link href={`/`}>tldraw</Link>
|
|
||||||
{section && (
|
|
||||||
<>
|
|
||||||
{section.title && (
|
|
||||||
<>
|
|
||||||
{` / `}
|
|
||||||
<Link href={`/${section.id}`}>{section.title}</Link>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{category && (
|
|
||||||
<>
|
|
||||||
{category.id === 'ucg' ? null : (
|
|
||||||
<>
|
|
||||||
{` / `}
|
|
||||||
<Link href={`/${section.id}/${category.id}`}>{category.title}</Link>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{article && (
|
|
||||||
<>
|
|
||||||
{` / `}
|
|
||||||
<Link href={`/${section.id}/${category.id}/${article.id}`}>{article.title}</Link>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,139 +0,0 @@
|
||||||
import { SearchResult } from '@/types/search-types'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
||||||
import { Icon } from './Icon'
|
|
||||||
|
|
||||||
export function Search({ activeId }: { activeId: string | null }) {
|
|
||||||
const [query, setQuery] = useState('')
|
|
||||||
const [results, setResults] = useState<SearchResult[]>([])
|
|
||||||
const rResultsList = useRef<HTMLOListElement>(null)
|
|
||||||
const [isDisabled, setIsDisabled] = useState(false)
|
|
||||||
|
|
||||||
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setQuery(e.target.value)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
const sendQuery = useCallback(
|
|
||||||
throttle(async (query: string) => {
|
|
||||||
const res = await fetch(`/api/search?q=${query}&s=${activeId}`)
|
|
||||||
const json = await res.json()
|
|
||||||
setResults(json.results)
|
|
||||||
}, 150),
|
|
||||||
[activeId]
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const query = rInput.current!.value
|
|
||||||
if (query.length > 2) {
|
|
||||||
sendQuery(query)
|
|
||||||
} else {
|
|
||||||
setResults([])
|
|
||||||
}
|
|
||||||
}, [sendQuery])
|
|
||||||
|
|
||||||
const hasQuery = query.length > 0
|
|
||||||
const hasResults = query.length > 0
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
function handleKeyUp(e: KeyboardEvent) {
|
|
||||||
if (e.key === 'Escape' && hasResults) {
|
|
||||||
setResults([])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleMouseUp(e: MouseEvent) {
|
|
||||||
if (rResultsList.current && !rResultsList.current.contains(e.target as Node)) {
|
|
||||||
setResults([])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.addEventListener('mouseup', handleMouseUp)
|
|
||||||
document.body.addEventListener('keyup', handleKeyUp)
|
|
||||||
return () => {
|
|
||||||
document.body.removeEventListener('mouseup', handleMouseUp)
|
|
||||||
document.body.removeEventListener('keyup', handleKeyUp)
|
|
||||||
}
|
|
||||||
}, [hasResults])
|
|
||||||
|
|
||||||
const rInput = useRef<HTMLInputElement>(null)
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setQuery('')
|
|
||||||
setResults([])
|
|
||||||
setIsDisabled(false)
|
|
||||||
}, [router.asPath])
|
|
||||||
|
|
||||||
const handleFocus = useCallback(() => {
|
|
||||||
if (hasQuery && !hasResults) {
|
|
||||||
sendQuery(rInput.current!.value)
|
|
||||||
}
|
|
||||||
}, [sendQuery, hasQuery, hasResults])
|
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
|
||||||
(e: React.KeyboardEvent) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
setIsDisabled(true)
|
|
||||||
router.push(`/search-results?q=${rInput.current!.value}`)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[router]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="search__wrapper">
|
|
||||||
<div className="search">
|
|
||||||
<Icon className="search__icon" icon="search" />
|
|
||||||
<input
|
|
||||||
ref={rInput}
|
|
||||||
type="text"
|
|
||||||
className="search__input"
|
|
||||||
placeholder="Search..."
|
|
||||||
value={query}
|
|
||||||
onChange={handleChange}
|
|
||||||
onFocus={handleFocus}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
autoCapitalize="off"
|
|
||||||
autoComplete="off"
|
|
||||||
autoCorrect="off"
|
|
||||||
disabled={isDisabled}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{results.length > 0 && (
|
|
||||||
<div className="search__results__wrapper">
|
|
||||||
<div className="search__results">
|
|
||||||
<ol ref={rResultsList} className="search__results__list">
|
|
||||||
{results.map((result) => (
|
|
||||||
<Link key={result.id} href={result.url}>
|
|
||||||
<li className="sidebar__article search__results__article">
|
|
||||||
<h4>{result.subtitle}</h4>
|
|
||||||
<h3>{result.title}</h3>
|
|
||||||
</li>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function throttle<T extends (...args: any) => any>(
|
|
||||||
func: T,
|
|
||||||
limit: number
|
|
||||||
): (...args: Parameters<T>) => ReturnType<T> {
|
|
||||||
let inThrottle: boolean
|
|
||||||
let lastResult: ReturnType<T>
|
|
||||||
return function (this: any, ...args: any[]): ReturnType<T> {
|
|
||||||
if (!inThrottle) {
|
|
||||||
inThrottle = true
|
|
||||||
setTimeout(() => (inThrottle = false), limit)
|
|
||||||
lastResult = func(...args)
|
|
||||||
}
|
|
||||||
return lastResult
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
import {
|
|
||||||
SidebarContentArticleLink,
|
|
||||||
SidebarContentCategoryLink,
|
|
||||||
SidebarContentLink,
|
|
||||||
SidebarContentList,
|
|
||||||
SidebarContentSectionLink,
|
|
||||||
} from '@/types/content-types'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { Icon } from './Icon'
|
|
||||||
import { Search } from './Search'
|
|
||||||
import { ThemeSwitcher } from './ThemeSwitcher'
|
|
||||||
|
|
||||||
type SidebarProps = SidebarContentList
|
|
||||||
|
|
||||||
export function Sidebar({ links, sectionId, categoryId, articleId }: SidebarProps) {
|
|
||||||
const [menuOpen, setMenuOpen] = useState(false)
|
|
||||||
|
|
||||||
const activeId = articleId ?? categoryId ?? sectionId
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setMenuOpen(false)
|
|
||||||
}, [router.asPath])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="sidebar" data-open={menuOpen}>
|
|
||||||
<div className="sidebar__buttons">
|
|
||||||
<ThemeSwitcher />
|
|
||||||
<div className="sidebar__buttons__socials">
|
|
||||||
<a
|
|
||||||
href="https://twitter.com/tldraw"
|
|
||||||
className="sidebar__button icon-button"
|
|
||||||
title="twitter"
|
|
||||||
>
|
|
||||||
<Icon icon="twitter" />
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://github.com/tldraw/tldraw"
|
|
||||||
className="sidebar__button icon-button"
|
|
||||||
title="github"
|
|
||||||
>
|
|
||||||
<Icon icon="github" />
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://discord.com/invite/SBBEVCA4PG"
|
|
||||||
className="sidebar__button icon-button"
|
|
||||||
title="discord"
|
|
||||||
>
|
|
||||||
<Icon icon="discord" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Search activeId={activeId} />
|
|
||||||
<nav className="sidebar__nav">
|
|
||||||
<ul className="sidebar__list sidebar__sections__list">
|
|
||||||
{links.map((link) => (
|
|
||||||
<SidebarLink key={link.url} {...link} />
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<div className="sidebar__footer">
|
|
||||||
<a href="https://www.tldraw.com">
|
|
||||||
<div
|
|
||||||
className="sidebar__lockup"
|
|
||||||
style={{
|
|
||||||
mask: `url(/lockup.svg) center 100% / 100% no-repeat`,
|
|
||||||
WebkitMask: `url(/lockup.svg) center 100% / 100% no-repeat`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<div>tldraw © 2023</div>
|
|
||||||
</div>
|
|
||||||
<div className="sidebar__close">
|
|
||||||
<span onClick={() => setMenuOpen(false)}>Close</span>
|
|
||||||
<button className="icon-button" onClick={() => setMenuOpen(false)}>
|
|
||||||
<Icon icon="close" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* Menu */}
|
|
||||||
<button className="menu__button icon-button" onClick={() => setMenuOpen(true)}>
|
|
||||||
<Icon icon="menu" />
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function SidebarLink(props: SidebarContentLink) {
|
|
||||||
switch (props.type) {
|
|
||||||
case 'section': {
|
|
||||||
return <SidebarSection {...props} />
|
|
||||||
}
|
|
||||||
case 'article': {
|
|
||||||
return <SidebarArticle {...props} />
|
|
||||||
}
|
|
||||||
case 'category': {
|
|
||||||
return <SidebarCategory {...props} />
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function SidebarSection({ title, url, children }: SidebarContentSectionLink) {
|
|
||||||
return (
|
|
||||||
<li className="sidebar__section">
|
|
||||||
{title && (
|
|
||||||
<Link href={url}>
|
|
||||||
<div className="sidebar__link sidebar__section__title" data-active={false}>
|
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
<ul className="sidebar__list sidebar__section__list">
|
|
||||||
{children.map((link) => (
|
|
||||||
<SidebarLink key={link.url} {...link} />
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function SidebarCategory({ title, url, children }: SidebarContentCategoryLink) {
|
|
||||||
return (
|
|
||||||
<li className="sidebar__category">
|
|
||||||
<Link href={url}>
|
|
||||||
<div className="sidebar__link sidebar__category__title" data-active={false}>
|
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
<ul className="sidebar__list sidebar__category__list">
|
|
||||||
{children.map((link) => (
|
|
||||||
<SidebarLink key={link.url} {...link} />
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
<hr />
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function SidebarArticle({ title, url }: SidebarContentArticleLink) {
|
|
||||||
return (
|
|
||||||
<li className="sidebar__article">
|
|
||||||
<Link href={url}>
|
|
||||||
<div className="sidebar__link sidebar__article__title" data-active={false}>
|
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
---
|
|
||||||
title: Installation
|
|
||||||
status: published
|
|
||||||
author: steveruizok
|
|
||||||
date: 3/22/2023
|
|
||||||
order: 1
|
|
||||||
---
|
|
||||||
|
|
||||||
At the moment the `@tldraw/tldraw` package is in alpha. We also ship a canary version which is always up to date with the main branch of tldraw [repository](https://github.com/tldraw/tldraw).
|
|
||||||
|
|
||||||
## Alpha
|
|
||||||
|
|
||||||
First, install the `@tldraw/tldraw` package using `@alpha` for the latest.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn add @tldraw/tldraw@alpha
|
|
||||||
# or
|
|
||||||
npm install @tldraw/tldraw@alpha
|
|
||||||
```
|
|
||||||
|
|
||||||
## Canary
|
|
||||||
|
|
||||||
To get the very latest version, use the [latest canary release](https://www.npmjs.com/package/@tldraw/tldraw?activeTab=versions). Docs for the very latest version are also available at [canary.tldraw.dev](https://canary.tldraw.dev).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn add @tldraw/tldraw@canary
|
|
||||||
# or
|
|
||||||
npm install @tldraw/tldraw@canary
|
|
||||||
```
|
|
|
@ -1,50 +0,0 @@
|
||||||
---
|
|
||||||
title: Introduction
|
|
||||||
status: published
|
|
||||||
author: steveruizok
|
|
||||||
date: 3/22/2023
|
|
||||||
order: 0
|
|
||||||
---
|
|
||||||
|
|
||||||
Welcome to the tldraw developer docs.
|
|
||||||
|
|
||||||
Here at tldraw, we make two things: a very good multiplayer whiteboard (at [tldraw.com](https://tldraw.com)), and the [open source library](https://github.com/tldraw/tldraw) that powers it. This page provides documentation and reference for that open source library.
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import { Tldraw } from '@tldraw/tldraw'
|
|
||||||
import '@tldraw/tldraw/tldraw.css'
|
|
||||||
|
|
||||||
export default function () {
|
|
||||||
return (
|
|
||||||
<div style={{ position: 'fixed', inset: 0 }}>
|
|
||||||
<Tldraw />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can use the `<Tldraw>` React component to build on top of the default tldraw experience. It's easy to use and easy to extend with your own [custom shapes](/docs/shapes), [custom tools](/docs/tools), and [user interface](/docs/user-interface) overrides.
|
|
||||||
|
|
||||||
You can also use the [Editor API](/docs/editor) to create, update, and delete shapes, control the camera, or do just about anything else.
|
|
||||||
|
|
||||||
If you want to go even deeper, you can use the `<TldrawEditor>` component as a more minimal engine without the default tldraw shapes or user interface.
|
|
||||||
|
|
||||||
|
|
||||||
Best of all, you can easily plug tldraw into the [collaboration backend](/docs/collaboration) of your choice.
|
|
||||||
|
|
||||||
- Want to explore the code? Visit the [GitHub repo](https://github.com/tldraw/tldraw).
|
|
||||||
- Want to try it out? Visit the [examples sandbox](https://stackblitz.com/github/tldraw/tldraw/tree/examples?file=src%2F1-basic%2FBasicExample.tsx).
|
|
||||||
|
|
||||||
Otherwise, continue on to the [installation](/docs/installation) and [usage](/docs/usage) guides.
|
|
||||||
|
|
||||||
## Community
|
|
||||||
|
|
||||||
Found a bug or want to request a feature? Create an issue [here](https://github.com/tldraw/tldraw/issues). To connect with the team and other users, join us on our [Discord](https://discord.gg/JMbeb96jsh).
|
|
||||||
|
|
||||||
If you spot an issue with these docs, please use the links at the bottom of each page to suggest a change.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Our open source libraries are licensed and distributed under Apache-2.0.
|
|
||||||
|
|
||||||
Our plan is to keep these libraries permissively licensed while we develop a commercial offering for teams who want more from tldraw. If you're planning to use use tldraw in a commercial product, please reach out at [hello@tldraw.com](mailto://hello@tldraw.com).
|
|
|
@ -1,48 +0,0 @@
|
||||||
---
|
|
||||||
title: Usage
|
|
||||||
status: published
|
|
||||||
author: steveruizok
|
|
||||||
date: 3/22/2023
|
|
||||||
order: 2
|
|
||||||
---
|
|
||||||
|
|
||||||
The `<Tldraw>` component is the easiest way to get started. To use it, create a file like this one:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import { Tldraw } from '@tldraw/tldraw'
|
|
||||||
import '@tldraw/tldraw/tldraw.css'
|
|
||||||
|
|
||||||
export default function () {
|
|
||||||
return (
|
|
||||||
<div style={{ position: 'fixed', inset: 0 }} >
|
|
||||||
<Tldraw />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### CSS
|
|
||||||
|
|
||||||
In order to use the `<Tldraw>` component, you should also import the `tldraw.css` file from the `@tldraw/tldraw` package. You can alternatively import this file inside of another CSS file using the `@import` syntax.
|
|
||||||
|
|
||||||
You can overwrite these files with other CSS or copy the contents into a different file and import that instead.
|
|
||||||
|
|
||||||
### HTML
|
|
||||||
|
|
||||||
If you're using the `<Tldraw>` component in a full-screen app, you probably also want to update your `index.html`'s meta viewport element as shown below.
|
|
||||||
|
|
||||||
```html
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
||||||
```
|
|
||||||
|
|
||||||
This may not be critical to `<Tldraw>` performing correctly, however some features (such as safe area positioning) will only work correctly if these viewport options are set.
|
|
||||||
|
|
||||||
## Using in Next.js, Create React App, Vite, etc.
|
|
||||||
|
|
||||||
Check the [examples repository](https://github.com/tldraw/examples) to see examples of tldraw being used in various frameworks.
|
|
||||||
|
|
||||||
By the way, the `<Tldraw>` component can't be server-rendered. If you're using the component in a server-rendered framework (such as Next.js) then you need to import it dynamically.
|
|
||||||
|
|
||||||
## Going deeper
|
|
||||||
|
|
||||||
The `<Tldraw>` component combines two lower-level components: `<TldrawEditor>` and `<TldrawUi>`. If you want to have more granular control, you can use those lower-level components directly. See [this example](https://github.com/tldraw/tldraw/blob/main/apps/examples/src/5-exploded/ExplodedExample.tsx) for reference.
|
|
|
@ -1,20 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": "getting-started",
|
|
||||||
"title": "",
|
|
||||||
"description": "",
|
|
||||||
"categories": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "docs",
|
|
||||||
"title": "Documentation",
|
|
||||||
"description": "Developer documentation for tldraw.",
|
|
||||||
"categories": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "community",
|
|
||||||
"title": "Community",
|
|
||||||
"description": "Guides for contributing to tldraw's open source project.",
|
|
||||||
"categories": []
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -1,44 +0,0 @@
|
||||||
/** @type {import('next').NextConfig} */
|
|
||||||
const nextConfig = {
|
|
||||||
reactStrictMode: true,
|
|
||||||
experimental: {
|
|
||||||
scrollRestoration: true,
|
|
||||||
},
|
|
||||||
transpilePackages: ['@tldraw/utils'],
|
|
||||||
async redirects() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
// For reverse compatibility with old links
|
|
||||||
source: '/:sectionId/ucg/:articleId',
|
|
||||||
destination: '/:sectionId/:articleId',
|
|
||||||
permanent: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// For reverse compatibility with old links
|
|
||||||
source: '/docs/introduction',
|
|
||||||
destination: '/introduction',
|
|
||||||
permanent: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// For reverse compatibility with old links
|
|
||||||
source: '/docs/installation',
|
|
||||||
destination: '/installation',
|
|
||||||
permanent: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// For reverse compatibility with old links
|
|
||||||
source: '/docs/usage',
|
|
||||||
destination: '/usage',
|
|
||||||
permanent: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// To reflect that these are at the top level of the sidebar
|
|
||||||
source: '/getting-started/:childId',
|
|
||||||
destination: '/:childId',
|
|
||||||
permanent: true,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = nextConfig
|
|
|
@ -1,146 +0,0 @@
|
||||||
import { Breadcrumb } from '@/components/Breadcrumb'
|
|
||||||
import { Mdx } from '@/components/Mdx'
|
|
||||||
import { MetaHead } from '@/components/MetaHead'
|
|
||||||
import { Sidebar } from '@/components/Sidebar'
|
|
||||||
import { Article, Category, Section, SidebarContentList } from '@/types/content-types'
|
|
||||||
import {
|
|
||||||
getArticle,
|
|
||||||
getArticleSource,
|
|
||||||
getArticles,
|
|
||||||
getCategory,
|
|
||||||
getLinks,
|
|
||||||
getSection,
|
|
||||||
getSections,
|
|
||||||
} from '@/utils/content'
|
|
||||||
import { getSidebarContentList } from '@/utils/getSidebarContentList'
|
|
||||||
import { GetStaticPaths, GetStaticProps } from 'next'
|
|
||||||
import { MDXRemoteSerializeResult } from 'next-mdx-remote'
|
|
||||||
import { useTheme } from 'next-themes'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import ArticlePage, { ArticleProps } from './[childId]/[articleId]'
|
|
||||||
|
|
||||||
type SectionProps = {
|
|
||||||
type: 'section'
|
|
||||||
sidebar: SidebarContentList
|
|
||||||
section: Section
|
|
||||||
categories: { category: Category; articles: Article[] }[]
|
|
||||||
mdxSource: MDXRemoteSerializeResult | null
|
|
||||||
}
|
|
||||||
|
|
||||||
type Props = SectionProps | ArticleProps
|
|
||||||
|
|
||||||
export default function SectionListPage(props: Props) {
|
|
||||||
const theme = useTheme()
|
|
||||||
if (props.type === 'article') {
|
|
||||||
return <ArticlePage {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
const { sidebar, section, categories, mdxSource } = props
|
|
||||||
const ucg = categories.find((category) => category.category.id === 'ucg')!
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MetaHead title={section.title} description={section.description} />
|
|
||||||
<div className="layout">
|
|
||||||
<Sidebar {...sidebar} />
|
|
||||||
<main className={`article list ${theme.theme ?? 'light'}`}>
|
|
||||||
<Breadcrumb />
|
|
||||||
<h1>{section.title}</h1>
|
|
||||||
{mdxSource && <Mdx mdxSource={mdxSource} />}
|
|
||||||
{ucg.articles.length > 0 ? (
|
|
||||||
<>
|
|
||||||
<ul>
|
|
||||||
{ucg.articles.map((article) => {
|
|
||||||
return (
|
|
||||||
<Link key={article.id} href={`/${section.id}/${ucg.category.id}/${article.id}`}>
|
|
||||||
<li>{article.title}</li>
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
<ul>
|
|
||||||
{categories.map((category) =>
|
|
||||||
category.category.id === 'ucg' ? null : (
|
|
||||||
<Link key={category.category.id} href={`/${section.id}/${category.category.id}`}>
|
|
||||||
{category.category.id === 'ucg' ? null : <li>{category.category.title}</li>}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getStaticPaths: GetStaticPaths = async () => {
|
|
||||||
const sections = await getSections()
|
|
||||||
const paths: { params: { sectionId: string } }[] = []
|
|
||||||
|
|
||||||
for (const section of sections) {
|
|
||||||
if (section.id !== 'getting-started') {
|
|
||||||
paths.push({ params: { sectionId: section.id } })
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add paths for getting-started articles (not the section itself)
|
|
||||||
// ... because we keep those at the top level of the sidebar
|
|
||||||
for (const category of section.categories) {
|
|
||||||
if (category.id !== 'ucg') continue
|
|
||||||
for (const articleId of category.articleIds) {
|
|
||||||
paths.push({ params: { sectionId: articleId } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { paths, fallback: false }
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps<Props> = async (ctx) => {
|
|
||||||
const id = ctx.params?.sectionId?.toString()
|
|
||||||
if (!id) throw Error()
|
|
||||||
|
|
||||||
// If the path goes to an article in the getting-started section
|
|
||||||
// ... show the article page instead
|
|
||||||
// ... because we keep those ones at the top level
|
|
||||||
const sections = await getSections()
|
|
||||||
if (!sections.some((section) => section.id === id)) {
|
|
||||||
const sectionId = 'getting-started'
|
|
||||||
const categoryId = 'ucg'
|
|
||||||
const articleId = id
|
|
||||||
const sidebar = await getSidebarContentList({ sectionId, categoryId, articleId })
|
|
||||||
const section = await getSection(sectionId)
|
|
||||||
const category = await getCategory(sectionId, categoryId)
|
|
||||||
const article = await getArticle(articleId)
|
|
||||||
const links = await getLinks(articleId)
|
|
||||||
const mdxSource = await getArticleSource(articleId)
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
type: 'article',
|
|
||||||
sidebar,
|
|
||||||
section,
|
|
||||||
category,
|
|
||||||
article,
|
|
||||||
links,
|
|
||||||
mdxSource,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, show the section page
|
|
||||||
const sectionId = id
|
|
||||||
const sidebar = await getSidebarContentList({ sectionId })
|
|
||||||
const articles = await getArticles()
|
|
||||||
const section = await getSection(sectionId)
|
|
||||||
const categories = [] as { category: Category; articles: Article[] }[]
|
|
||||||
|
|
||||||
for (const category of section.categories) {
|
|
||||||
categories.push({ category, articles: category.articleIds.map((id) => articles[id]) })
|
|
||||||
}
|
|
||||||
|
|
||||||
const article = articles[sectionId + '_index'] ?? null
|
|
||||||
const mdxSource = article ? await getArticleSource(sectionId + '_index') : null
|
|
||||||
|
|
||||||
return { props: { type: 'section', sidebar, section, categories, mdxSource } }
|
|
||||||
}
|
|
|
@ -1,931 +0,0 @@
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&family=Roboto+Mono:wght@400;700&display=swap');
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--font-body: 'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
|
||||||
Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
|
||||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
|
||||||
--font-heading: 'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
|
||||||
Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
|
||||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
|
||||||
--font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', 'Roboto Mono',
|
|
||||||
'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', 'Fira Mono', 'Droid Sans Mono',
|
|
||||||
'Courier New', monospace;
|
|
||||||
|
|
||||||
--color-tint-1: rgba(144, 144, 144, 0.08);
|
|
||||||
--color-tint-2: rgba(144, 144, 144, 0.15);
|
|
||||||
--color-tint-3: rgba(144, 144, 144, 0.3);
|
|
||||||
--color-tint-4: rgba(144, 144, 144, 0.5);
|
|
||||||
--color-tint-5: rgb(144, 144, 144);
|
|
||||||
--color-tint-6: rgb(81, 81, 81);
|
|
||||||
|
|
||||||
/* Light theme */
|
|
||||||
--color-text: #2d2d2d;
|
|
||||||
--color-background: #ffffff;
|
|
||||||
--color-accent: #2f80ed;
|
|
||||||
|
|
||||||
--shadow-small: 0px 0px 16px -2px rgba(0, 0, 0, 0.12), 0px 0px 4px 0px rgba(0, 0, 0, 0.12);
|
|
||||||
--shadow-big: 0px 0px 16px -2px rgba(0, 0, 0, 0.24), 0px 0px 4px 0px rgba(0, 0, 0, 0.24);
|
|
||||||
|
|
||||||
/* Code colors */
|
|
||||||
--hl: #4e484e;
|
|
||||||
--hl-1: rgb(54, 59, 69);
|
|
||||||
--hl-2: rgb(144, 81, 188);
|
|
||||||
--hl-3: rgb(178, 66, 69);
|
|
||||||
--hl-4: rgb(69, 141, 155);
|
|
||||||
--hl-5: rgb(100, 144, 65);
|
|
||||||
--hl-6: rgb(179, 108, 50);
|
|
||||||
--hl-7: rgb(73, 131, 216);
|
|
||||||
--hl-8: rgb(180, 133, 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme='dark'] {
|
|
||||||
/* Dark theme */
|
|
||||||
--color-text: #fff;
|
|
||||||
--color-background: #1d1d1d;
|
|
||||||
--color-accent: #f3c14b;
|
|
||||||
|
|
||||||
--color-tint-6: rgb(186, 186, 186);
|
|
||||||
|
|
||||||
--shadow-small: 0px 0px 16px -2px rgba(0, 0, 0, 0.52), 0px 0px 4px 0px rgba(0, 0, 0, 0.62);
|
|
||||||
--shadow-big: 0px 0px 16px -2px rgba(0, 0, 0, 0.54), 0px 0px 4px 0px rgba(0, 0, 0, 0.54);
|
|
||||||
|
|
||||||
/* Code colors */
|
|
||||||
--hl: #c8c5f1;
|
|
||||||
--hl-1: #5c6370;
|
|
||||||
--hl-2: #c678dd;
|
|
||||||
--hl-3: #e06c75;
|
|
||||||
--hl-4: #56b6c2;
|
|
||||||
--hl-5: #98c379;
|
|
||||||
--hl-6: #d19a66;
|
|
||||||
--hl-7: #61aeee;
|
|
||||||
--hl-8: #e6c07b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--color-text: #ffffff;
|
|
||||||
--color-background: #1d1d1d;
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
/* @media (prefers-color-scheme: dark) {
|
|
||||||
html {
|
|
||||||
color-scheme: dark;
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
background-color: var(--color-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: var(--font-body);
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-weight: 400;
|
|
||||||
color: var(--color-text);
|
|
||||||
background-color: var(--color-background);
|
|
||||||
transition: background-color 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 212px 1fr;
|
|
||||||
grid-template-rows: 1fr;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 984px;
|
|
||||||
column-gap: 28px;
|
|
||||||
row-gap: 0px;
|
|
||||||
margin: 0px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-button {
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
height: 40px;
|
|
||||||
width: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
--bg: transparent;
|
|
||||||
color: var(--color-text);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-button::after {
|
|
||||||
position: absolute;
|
|
||||||
display: block;
|
|
||||||
content: '';
|
|
||||||
inset: 4px;
|
|
||||||
background-color: var(--bg);
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: background-color 0.1s ease-in-out;
|
|
||||||
transition-delay: 0s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (hover: hover) {
|
|
||||||
.icon-button:hover {
|
|
||||||
--bg: var(--color-tint-1);
|
|
||||||
color: var(--color-accent);
|
|
||||||
transition: background-color 0.2s ease-in-out;
|
|
||||||
transition-delay: 0.1s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
background-color: currentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lockup {
|
|
||||||
position: relative;
|
|
||||||
width: calc(71px * 4);
|
|
||||||
height: calc(18px * 4);
|
|
||||||
background: currentColor;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.lockup {
|
|
||||||
width: calc(71px * 3);
|
|
||||||
height: calc(18px * 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.lockup + h2 {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.article {
|
|
||||||
padding: 56px 16px 120px 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
max-width: 100%;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article__details {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin: 40px 0px;
|
|
||||||
gap: 16px;
|
|
||||||
max-width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article__details__edit {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
font-size: 14px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article__details__timestamp {
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--color-tint-5);
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prev / Next Links */
|
|
||||||
|
|
||||||
.article__links {
|
|
||||||
width: 100%;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: space-between;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article__links .icon {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article__links__link {
|
|
||||||
display: block;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
border: 1px solid var(--color-accent);
|
|
||||||
padding: 10px 16px;
|
|
||||||
border-radius: 4px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article__links__prev {
|
|
||||||
grid-column: 1;
|
|
||||||
justify-self: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article__links__next {
|
|
||||||
grid-column: 2;
|
|
||||||
justify-self: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Common Styles */
|
|
||||||
|
|
||||||
.article > * {
|
|
||||||
content-visibility: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article > h2,
|
|
||||||
.article > h3,
|
|
||||||
.article > h4,
|
|
||||||
.article > h5,
|
|
||||||
.article > h6 {
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article > h2 > a,
|
|
||||||
.article > h3 > a,
|
|
||||||
.article > h4 > a,
|
|
||||||
.article > h5 > a,
|
|
||||||
.article > h6 > a {
|
|
||||||
color: var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article h1 {
|
|
||||||
font-size: 2.5em;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
line-height: 1.15;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article h2 {
|
|
||||||
margin: 48px 0px 40px 0px;
|
|
||||||
font-size: 1.8em;
|
|
||||||
/* margin: 24px 0px 40px 0px; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.article h5 {
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--color-tint-5);
|
|
||||||
letter-spacing: 0.1em;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article details {
|
|
||||||
margin: 32px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article details > summary {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article details > summary::before {
|
|
||||||
font-size: 10px;
|
|
||||||
content: '▶';
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article details[open] > summary::before {
|
|
||||||
content: '▼';
|
|
||||||
}
|
|
||||||
|
|
||||||
.article p.article__small {
|
|
||||||
margin: 8px 0px 32px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article p.article__small small {
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--color-tint-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article a {
|
|
||||||
color: var(--color-accent);
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (hover: hover) {
|
|
||||||
.article a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.article > p {
|
|
||||||
margin: 32px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article > blockquote {
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 32px 0px;
|
|
||||||
padding-left: 16px;
|
|
||||||
border-left: 2px solid var(--color-tint-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article pre {
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 32px 0px;
|
|
||||||
padding: 16px;
|
|
||||||
background-color: var(--color-tint-1);
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 13px;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article code {
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article p > code,
|
|
||||||
.article td > code {
|
|
||||||
background-color: var(--color-tint-1);
|
|
||||||
font-size: 15px;
|
|
||||||
padding: 2px 4px;
|
|
||||||
margin: 0px -2px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article pre > code {
|
|
||||||
font-size: 14px;
|
|
||||||
width: 100%;
|
|
||||||
tab-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article ol {
|
|
||||||
margin: 24px 0px;
|
|
||||||
padding-left: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article ul {
|
|
||||||
margin: 24px 0px;
|
|
||||||
padding-left: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article li {
|
|
||||||
margin: 8px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article hr {
|
|
||||||
margin: 52px 0px;
|
|
||||||
padding: 0px 4px;
|
|
||||||
height: 1px;
|
|
||||||
width: calc(100% - 8px);
|
|
||||||
background-color: var(--color-tint-2);
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article table {
|
|
||||||
margin: 32px 0px;
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
border: 1px solid var(--color-tint-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article table th {
|
|
||||||
padding: 8px 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 12px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--color-tint-5);
|
|
||||||
letter-spacing: 0.1em;
|
|
||||||
border-bottom: 1px solid var(--color-tint-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article td:nth-of-type(1) {
|
|
||||||
width: 25%;
|
|
||||||
max-width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article table td {
|
|
||||||
padding: 8px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article table td:nth-of-type(1) {
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
.artcle__image {
|
|
||||||
display: block;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 24px 0px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.artcle__image > img {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article__caption {
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 14px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.breadcrumb {
|
|
||||||
font-size: 12px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.1em;
|
|
||||||
color: var(--color-tint-6);
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.breadcrumb a {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------- Sidebar -------------------- */
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
position: relative;
|
|
||||||
position: sticky;
|
|
||||||
padding: 8px;
|
|
||||||
align-self: start;
|
|
||||||
top: 0px;
|
|
||||||
max-height: calc(100vh);
|
|
||||||
z-index: 1000;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding-top: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar .sidebar__list h4 {
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar hr {
|
|
||||||
margin: 11px 4px;
|
|
||||||
padding: 0px 4px;
|
|
||||||
height: 1px;
|
|
||||||
width: calc(100% - 8px);
|
|
||||||
background-color: var(--color-tint-2);
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__list {
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__link {
|
|
||||||
position: relative;
|
|
||||||
height: 40px;
|
|
||||||
padding: 0px 12px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
color: var(--color-text);
|
|
||||||
--bg: transparent;
|
|
||||||
margin-top: -4px;
|
|
||||||
margin-bottom: -4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__link:nth-of-type(1) {
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__link:nth-last-of-type(1) {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__link[data-active='true'] {
|
|
||||||
--bg: var(--color-tint-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__link::after {
|
|
||||||
position: absolute;
|
|
||||||
display: block;
|
|
||||||
content: '';
|
|
||||||
inset: 4px;
|
|
||||||
background-color: var(--bg);
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: background-color 0.2s ease-in-out;
|
|
||||||
transition-delay: 0s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__section__title {
|
|
||||||
font-size: 12px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.1em;
|
|
||||||
color: var(--color-tint-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__sections__list > *:nth-of-type(1) {
|
|
||||||
padding-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__sections__list > *:nth-last-of-type(n + 2) .sidebar__list {
|
|
||||||
padding-bottom: 12px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
border-bottom: 1px solid var(--color-tint-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (hover: hover) {
|
|
||||||
.sidebar__link:hover {
|
|
||||||
color: var(--color-accent);
|
|
||||||
transition: background-color 0.12s ease-in-out;
|
|
||||||
transition-delay: 0.1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__link:not([data-active='true']):hover {
|
|
||||||
--bg: var(--color-tint-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__footer {
|
|
||||||
padding: 12px;
|
|
||||||
padding-top: 44px;
|
|
||||||
font-size: 13px;
|
|
||||||
color: var(--color-tint-3);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
transition: color 0.2s ease-in-out;
|
|
||||||
transition-delay: 0s;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (hover: hover) {
|
|
||||||
.sidebar__footer:hover {
|
|
||||||
color: var(--color-text);
|
|
||||||
transition: color 0.3s ease-in-out;
|
|
||||||
transition-delay: 0.2s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__lockup {
|
|
||||||
position: relative;
|
|
||||||
width: calc(71px * 1.3);
|
|
||||||
height: calc(18px * 1.3);
|
|
||||||
background: currentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__buttons {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__buttons__socials {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__close {
|
|
||||||
display: none;
|
|
||||||
width: 100%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
color: var(--color-text);
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0px;
|
|
||||||
left: 0px;
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu__button {
|
|
||||||
display: none;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 500;
|
|
||||||
bottom: 8px;
|
|
||||||
right: 8px;
|
|
||||||
background-color: var(--color-text);
|
|
||||||
border-radius: 6px;
|
|
||||||
color: var(--color-background);
|
|
||||||
box-shadow: var(--shadow-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------- Search --------------------- */
|
|
||||||
|
|
||||||
.search__wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search {
|
|
||||||
position: relative;
|
|
||||||
z-index: 200;
|
|
||||||
height: 40px;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__icon {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
left: 12px;
|
|
||||||
z-index: 2;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__input {
|
|
||||||
position: relative;
|
|
||||||
padding-left: 28px;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid var(--color-tint-3);
|
|
||||||
background-color: var(--color-background);
|
|
||||||
font-family: var(--font-body);
|
|
||||||
font-size: 14px;
|
|
||||||
background-color: none;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__input:disabled {
|
|
||||||
background-color: var(--color-tint-1);
|
|
||||||
color: var(--color-tint-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__input::placeholder {
|
|
||||||
color: var(--color-tint-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__input:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__results__wrapper {
|
|
||||||
position: relative;
|
|
||||||
height: 0px;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__results {
|
|
||||||
padding: 0px;
|
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
background-color: var(--color-background);
|
|
||||||
width: 320px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: var(--shadow-big);
|
|
||||||
max-height: 80vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__results__list {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 4px;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__results__article {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 16px;
|
|
||||||
padding: 12px;
|
|
||||||
height: fit-content;
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__results__article h4 {
|
|
||||||
font-size: 11px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--color-tint-5);
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__results__article h3 {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (hover: hover) {
|
|
||||||
.sidebar__article:hover {
|
|
||||||
color: var(--color-accent);
|
|
||||||
transition: background-color 0.12s ease-in-out;
|
|
||||||
transition-delay: 0.1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__article:not([data-active='true']):hover {
|
|
||||||
--bg: var(--color-tint-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------- API docs --------------------- */
|
|
||||||
.parametersTable {
|
|
||||||
table-layout: fixed;
|
|
||||||
}
|
|
||||||
.parametersTable > thead th:nth-child(1) {
|
|
||||||
width: 25%;
|
|
||||||
}
|
|
||||||
.parametersTable > thead th:nth-child(2) {
|
|
||||||
width: 75%;
|
|
||||||
}
|
|
||||||
.parametersTable pre {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
.parametersTable-row > td {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
.parametersTable-row:not(:last-child) > td {
|
|
||||||
border-bottom: 1px solid var(--color-tint-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------- Mobile --------------------- */
|
|
||||||
|
|
||||||
@media (max-width: 760px) {
|
|
||||||
.layout {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
grid-template-rows: 1fr;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 984px;
|
|
||||||
column-gap: 28px;
|
|
||||||
row-gap: 0px;
|
|
||||||
margin: 0px auto;
|
|
||||||
padding-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu__button {
|
|
||||||
display: flex;
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
display: none;
|
|
||||||
background-color: var(--color-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar[data-open='true'] {
|
|
||||||
display: initial;
|
|
||||||
position: fixed;
|
|
||||||
inset: 0px;
|
|
||||||
z-index: 10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar[data-open='true'] > .sidebar__close {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar__article {
|
|
||||||
height: 44px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-button {
|
|
||||||
height: 44px;
|
|
||||||
width: 44px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search {
|
|
||||||
height: 44px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__input {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__results__article {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__results {
|
|
||||||
position: static;
|
|
||||||
box-shadow: none;
|
|
||||||
padding: 0px;
|
|
||||||
padding-top: 8px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__results__list {
|
|
||||||
padding: 0px;
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search__results__wrapper {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article {
|
|
||||||
padding-top: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article h1 {
|
|
||||||
font-size: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article__links {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
grid-auto-rows: 44px;
|
|
||||||
gap: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article__links__prev {
|
|
||||||
border: none;
|
|
||||||
grid-row: 2;
|
|
||||||
grid-column: 2;
|
|
||||||
justify-self: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article__links__next {
|
|
||||||
grid-row: 1;
|
|
||||||
grid-column: 2;
|
|
||||||
justify-self: flex-end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------- Code Themes ------------------ */
|
|
||||||
|
|
||||||
.hljs {
|
|
||||||
color: var(--hl);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-comment,
|
|
||||||
.hljs-quote {
|
|
||||||
color: var(--hl-1);
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-doctag,
|
|
||||||
.hljs-keyword,
|
|
||||||
.hljs-formula {
|
|
||||||
color: var(--hl-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-section,
|
|
||||||
.hljs-name,
|
|
||||||
.hljs-selector-tag,
|
|
||||||
.hljs-deletion,
|
|
||||||
.hljs-subst {
|
|
||||||
color: var(--hl-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-literal {
|
|
||||||
color: var(--hl-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-string,
|
|
||||||
.hljs-regexp,
|
|
||||||
.hljs-addition,
|
|
||||||
.hljs-attribute,
|
|
||||||
.hljs-meta .hljs-string {
|
|
||||||
color: var(--hl-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-attr,
|
|
||||||
.hljs-variable,
|
|
||||||
.hljs-template-variable,
|
|
||||||
.hljs-type,
|
|
||||||
.hljs-selector-class,
|
|
||||||
.hljs-selector-attr,
|
|
||||||
.hljs-selector-pseudo,
|
|
||||||
.hljs-number {
|
|
||||||
color: var(--hl-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-symbol,
|
|
||||||
.hljs-bullet,
|
|
||||||
.hljs-link,
|
|
||||||
.hljs-meta,
|
|
||||||
.hljs-selector-id,
|
|
||||||
.hljs-title {
|
|
||||||
color: var(--hl-7);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-built_in,
|
|
||||||
.hljs-title.class_,
|
|
||||||
.hljs-class .hljs-title {
|
|
||||||
color: var(--hl-8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-emphasis {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-link {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
import { SidebarContentLink, SidebarContentList } from '../types/content-types'
|
|
||||||
import { getArticles, getSections } from './content'
|
|
||||||
|
|
||||||
export async function getSidebarContentList({
|
|
||||||
sectionId,
|
|
||||||
categoryId,
|
|
||||||
articleId,
|
|
||||||
}: {
|
|
||||||
sectionId?: string
|
|
||||||
categoryId?: string
|
|
||||||
articleId?: string
|
|
||||||
}): Promise<SidebarContentList> {
|
|
||||||
const links: SidebarContentLink[] = []
|
|
||||||
|
|
||||||
const articles = await getArticles()
|
|
||||||
|
|
||||||
for (const section of await getSections()) {
|
|
||||||
const children: SidebarContentLink[] = []
|
|
||||||
|
|
||||||
if (section.id === 'gen') {
|
|
||||||
links.push({ type: 'article', title: 'API Reference', url: '/gen' })
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the article is in the getting-started section
|
|
||||||
// ... we place it at the top level of the sidebar
|
|
||||||
// ... so let's simplify its URL to reflect that
|
|
||||||
const sectionUrl = section.id === 'getting-started' ? '' : `/${section.id}`
|
|
||||||
|
|
||||||
for (const category of section.categories) {
|
|
||||||
if (category.id === 'ucg') {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
children.push({
|
|
||||||
type: 'category',
|
|
||||||
title: category.title,
|
|
||||||
url: `${sectionUrl}/${category.id}`,
|
|
||||||
children: category.articleIds.map((articleId) => {
|
|
||||||
const article = articles[articleId]
|
|
||||||
return {
|
|
||||||
type: 'article' as const,
|
|
||||||
title: article.title,
|
|
||||||
url: `${sectionUrl}/${category.id}/${articleId}`,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const category of section.categories) {
|
|
||||||
if (category.id === 'ucg') {
|
|
||||||
children.push(
|
|
||||||
...category.articleIds.map((articleId) => {
|
|
||||||
const article = articles[articleId]
|
|
||||||
return {
|
|
||||||
type: 'article' as const,
|
|
||||||
title: article.title,
|
|
||||||
url: `${sectionUrl}/${articleId}`,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
links.push({ type: 'section', title: section.title, url: sectionUrl, children })
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
sectionId: sectionId ?? null,
|
|
||||||
categoryId: categoryId ?? null,
|
|
||||||
articleId: articleId ?? null,
|
|
||||||
links,
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue