提交 e2102941 编写于 作者: C codecalm

docs improvements

上级 3ff30bc6
// import Link from "next/link";
// import { useRouter } from "next/router";
// import { getCategory, getDocsMenu, getPrevNext } from "@/lib/docs";
import DocsMenu from '@/components/DocsMenu';
// import Icon from "@/components/Icon";
export const metadata = {
title: 'Documentation',
template: '%s - Documentation',
......@@ -13,23 +5,10 @@ export const metadata = {
};
export default function DocsLayout({ children /*, meta = {}, pageProps*/ }) {
// const docsMenu = getDocsMenu(),
// router = useRouter(),
// category = getCategory(router.pathname),
// [prev, next] = getPrevNext(router.pathname)
return (
<div className="border-bottom border-top">
<div className="container">
<div className="row g-0">
<div className="md:col-auto docs-side">
{/*<input type="search" className="form-control w-100 mb-5" placeholder="Search&hellip;" />*/}
<DocsMenu />
</div>
<div className="md:col">
<div className="py-6 md:pl-6">{children}</div>
</div>
</div>
{children}
</div>
</div>
);
......
......@@ -4,10 +4,13 @@ import { notFound } from 'next/navigation';
import { allDocs } from 'contentlayer/generated';
import { name } from '@/config/site';
import { getTableOfContents } from '@/lib/toc';
import Mdx from '@/components/MDX';
import TablerSponsorsBanner from '@/components/TablerSponsorsBanner';
import Link from 'next/link';
import TOC from '@/components/TOC';
import DocsMenu from '@/components/DocsMenu';
import Icon from '@/components/Icon';
interface DocPageProps {
params: {
......@@ -59,100 +62,62 @@ export default async function DocPage({ params }: DocPageProps) {
notFound();
}
// const toc = await getTableOfContents(doc.body.raw)
const toc = await getTableOfContents(doc.body.raw);
return (
<>
<nav aria-label="breadcrumbs" className="breadcrumb mb-6">
<ul className="breadcrumb-list">
<li className="breadcrumb-item">
<Link href="/" className="breadcrumb-link">
Home
</Link>
</li>
<li className="breadcrumb-item">
<Link href="/docs" className="breadcrumb-link">
Documentation
</Link>
</li>
<li className="breadcrumb-item">
<Link href={`/docs/${params.slug.join('/')}`} className="breadcrumb-link">
{doc.title}
</Link>
</li>
</ul>
</nav>
<div className="markdown">
{/* {category && (
<div className="row g-0">
<div className="md:col-auto docs-side">
{/*<input type="search" className="form-control w-100 mb-5" placeholder="Search&hellip;" />*/}
<DocsMenu />
</div>
<div className="md:col">
<div className="py-6 md:pl-6">
<nav aria-label="breadcrumbs" className="breadcrumb mb-6">
<ul className="breadcrumb-list">
<li className="breadcrumb-item">
<Link href="/" className="breadcrumb-link">
Home
</Link>
</li>
<li className="breadcrumb-item">
<Link href="/docs" className="breadcrumb-link">
Documentation
</Link>
</li>
<li className="breadcrumb-item">
<Link href={`/docs/${params.slug.join('/')}`} className="breadcrumb-link">
{doc.title}
</Link>
</li>
</ul>
</nav>
<div className="markdown">
{/* {category && (
<div className="h-subheader text-primary">{category}</div>
)} */}
{doc.title && <h1>{doc.title}</h1>}
{doc.description && <p className="lead">{doc.description}</p>}
{doc.title && <h1>{doc.title}</h1>}
{doc.description && <p className="lead">{doc.description}</p>}
<Mdx code={doc.body.code} />
</div>
<Mdx code={doc.body.code} />
</div>
<TablerSponsorsBanner className="mt-7" />
<div className="pt-7">
<div className="row">
<div className="md:col">
{/* {prev && (
<Link href={prev.url} className="card">
<div className="card-body p-3">
<div className="row items-center g-3">
<div className="col-auto">
<div className="card-chevron card-chevron-left">
<Icon name="chevron-left" />
</div>
</div>
<div className="col">
<div className="h-subheader mb-1">
{prev.category}
</div>
<div className="h5 m-0">{prev.title}</div>
</div>
</div>
</div>
</Link>
)} */}
<TablerSponsorsBanner className="mt-7" />
</div>
</div>
<div className="docs-side-toc">
<div className="pl-6 font-h6 pt-6">
<div className="h6 mb-3">On this page</div>
<div>
<TOC toc={toc} />
</div>
<div className="md:col">
{/* {next && (
<Link href={next.url} className="card text-right">
<div className="card-body p-3">
<div className="row items-center g-3">
<div className="col">
<div className="h-subheader mb-1">
{next.category}
</div>
<div className="h5 m-0">{next.title}</div>
</div>
<div className="col-auto">
<div className="card-chevron">
<Icon name="chevron-right" />
</div>
</div>
</div>
</div>
</Link>
)} */}
<div className="mt-4 border-top pt-4">
<a href={`https://github.com/tabler/tabler/blob/main/docs/${doc.slugAsParams.split(',').join('/')}.mdx`} className="link-muted">
Improve this page
<Icon name="edit" className="icon-inline ml-2" />
</a>
</div>
</div>
</div>
{/* // <main className="relative py-6 lg:gap-10 lg:py-10 xl:grid xl:grid-cols-[1fr_300px]">
// <div className="mx-auto w-full min-w-0">
// {/* <DocsPageHeader heading={doc.title} text={doc.description} />
// <Mdx code={doc.body.code} />
// <hr className="my-4 md:my-6" />
// {/* <DocsPager doc={doc} />
// </div>
// <div className="hidden text-sm xl:block">
// <div className="sticky top-16 -mt-10 max-h-[calc(var(--vh)-4rem)] overflow-y-auto pt-10">
// {/* <DashboardTableOfContents toc={toc} />
// </div>
// </div>
// </main> */}
</>
</div>
);
}
......@@ -653,3 +653,5 @@ export default async function HomePage() {
</>
);
}
HomePage.bodyClass = 'xxx';
......@@ -62,7 +62,7 @@ export const metadata = {
},
};
export default function RootLayout({ children }: { children: React.ReactNode; }) {
export default function RootLayout({ children, bodyClass }: { children: React.ReactNode; bodyClass?: string }) {
return (
<html lang="en">
<head>
......@@ -74,6 +74,7 @@ export default function RootLayout({ children }: { children: React.ReactNode; })
)}
</head>
<body>
{bodyClass}
<PageProgress />
{children}
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
......
'use client';
import * as React from 'react';
import { TableOfContents } from '@/lib/toc';
import useMounted from '@/hooks/use-mounted';
import clsx from 'clsx';
interface TocProps {
toc: TableOfContents;
}
export default function TOC({ toc }: TocProps) {
const itemIds = React.useMemo(
() =>
toc.items
? toc.items
.flatMap((item) => [item.url, item?.items?.map((item) => item.url)])
.flat()
.filter(Boolean)
.map((id) => id?.split('#')[1])
: [],
[toc],
);
const activeHeading = useActiveItem(itemIds);
const mounted = useMounted();
if (!toc?.items) {
return null;
}
return mounted ? (
<Tree tree={toc} activeItem={activeHeading} />
) : null;
}
function useActiveItem(itemIds: (string | undefined)[]) {
const [activeId, setActiveId] = React.useState<string>('');
React.useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setActiveId(entry.target.id);
}
});
},
{ rootMargin: '0% 0% -80% 0%' },
);
itemIds?.forEach((id) => {
if (!id) {
return;
}
const element = document.getElementById(id);
if (element) {
observer.observe(element);
}
});
return () => {
itemIds?.forEach((id) => {
if (!id) {
return;
}
const element = document.getElementById(id);
if (element) {
observer.unobserve(element);
}
});
};
}, [itemIds]);
return activeId;
}
interface TreeProps {
tree: TableOfContents;
level?: number;
activeItem?: string | null;
}
function Tree({ tree, level = 1, activeItem }: TreeProps) {
return tree?.items?.length && level < 3 ? (
<ul className={clsx('p-0 list-unstyled space-y-2', { 'pl-4': level !== 1 })}>
{tree.items.map((item, index) => {
return (
<li key={index}>
<a href={item.url} className={clsx('link-muted', item.url === `#${activeItem}` ? 'font-medium text-headers' : '')}>
{item.title}
</a>
{item.items?.length ? <Tree tree={item} level={level + 1} activeItem={activeItem} /> : null}
</li>
);
})}
</ul>
) : null;
}
......@@ -5,7 +5,7 @@ import { uiDownloadUrl } from '@/config/site';
export default function LayoutHeroUi() {
return (
<>
<header className="hero pt-10 pb-0">
<header className="hero pt-10 pb-0 bg-gradient">
<div className="container">
<h1 className="hero-title" data-aos="zoom-y-out">
Develop beautiful web apps with&nbsp;Tabler
......
import React from "react";
import React from 'react';
const useMounted = () => {
const [mounted, setMounted] = React.useState(false)
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true)
}, [])
setMounted(true);
}, []);
return mounted
}
return mounted;
};
export default useMounted
export default useMounted;
import { RefObject } from 'react'
import useEventListener from './use-event-listener'
import { RefObject } from 'react';
import useEventListener from './use-event-listener';
type Handler = (event: MouseEvent) => void
type Handler = (event: MouseEvent) => void;
const useOnClickOutside = <T extends HTMLElement = HTMLElement>(
ref: RefObject<T>,
handler: Handler,
mouseEvent: 'mousedown' | 'mouseup' = 'mousedown',
): void => {
useEventListener(mouseEvent, event => {
const el = ref?.current
const useOnClickOutside = <T extends HTMLElement = HTMLElement>(ref: RefObject<T>, handler: Handler, mouseEvent: 'mousedown' | 'mouseup' = 'mousedown'): void => {
useEventListener(mouseEvent, (event) => {
const el = ref?.current;
// Do nothing if clicking ref's element or descendent elements
if (!el || el.contains(event.target as Node)) {
return
return;
}
handler(event)
})
}
handler(event);
});
};
export default useOnClickOutside
\ No newline at end of file
export default useOnClickOutside;
import { toc } from 'mdast-util-toc';
import { remark } from 'remark';
import { visit } from 'unist-util-visit';
const textTypes = ['text', 'emphasis', 'strong', 'inlineCode'];
function flattenNode(node: any) {
const p: any[] = [];
visit(node, (node) => {
if (!textTypes.includes(node.type)) {
return;
}
p.push(node.value);
});
return p.join('');
}
interface Item {
title: string;
url: string;
items?: Item[];
}
interface Items {
items?: Item[];
}
function getItems(node, current): Items {
if (!node) {
return {};
}
if (node.type === 'paragraph') {
visit(node, (item) => {
if (item.type === 'link') {
current.url = item.url;
current.title = flattenNode(node);
}
if (item.type === 'text') {
current.title = flattenNode(node);
}
});
return current;
}
if (node.type === 'list') {
current.items = node.children.map((i) => getItems(i, {}));
return current;
} else if (node.type === 'listItem') {
const heading = getItems(node.children[0], {});
if (node.children.length > 1) {
getItems(node.children[1], heading);
}
return heading;
}
return {};
}
const getToc = () => (node, file) => {
const table = toc(node);
file.data = getItems(table.map, {});
};
export type TableOfContents = Items;
export async function getTableOfContents(content: string): Promise<TableOfContents> {
const result = await remark().use(getToc).process(content);
return result.data;
}
......@@ -32,6 +32,11 @@
"@tabler/icons": "^2.28.0",
"@tabler/icons-png": "^2.25.0",
"@tabler/icons-webfont": "^2.25.0",
"@types/debug": "^4.1.8",
"@types/eslint": "^8.44.0",
"@types/eslint-scope": "^3.7.4",
"@types/estree-jsx": "^1.0.0",
"@types/extend": "^3.0.1",
"@types/node": "^20.4.2",
"@types/react": "18.2.15",
"@types/react-dom": "18.2.7",
......@@ -62,6 +67,8 @@
"mdast-util-mdx-jsx": "^3.0.0",
"mdast-util-mdxjs-esm": "^2.0.0",
"mdast-util-to-markdown": "^2.0.0",
"mdast-util-to-string": "^4.0.0",
"mdast-util-toc": "^7.0.0",
"mdx": "^0.3.1",
"mdx-annotations": "^0.1.3",
"minimatch": "^9.0.3",
......@@ -105,12 +112,6 @@
"typescript": "5.1.6",
"unist-util-visit": "^5.0.0",
"webpack": "^5.88.2",
"yaml": "^2.3.1",
"@types/debug": "^4.1.8",
"@types/eslint": "^8.44.0",
"@types/eslint-scope": "^3.7.4",
"@types/estree-jsx": "^1.0.0",
"@types/extend": "^3.0.1",
"mdast-util-to-string": "^4.0.0"
"yaml": "^2.3.1"
}
}
......@@ -142,6 +142,9 @@ dependencies:
mdast-util-to-string:
specifier: ^4.0.0
version: 4.0.0
mdast-util-toc:
specifier: ^7.0.0
version: 7.0.0
mdx:
specifier: ^0.3.1
version: 0.3.1
......@@ -3004,6 +3007,10 @@ packages:
resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
dev: false
/@types/ungap__structured-clone@0.3.0:
resolution: {integrity: sha512-eBWREUhVUGPze+bUW22AgUr05k8u+vETzuYdLYSvWqGTUe0KOf+zVnOB1qER5wMcw8V6D9Ar4DfJmVvD1yu0kQ==}
dev: false
/@types/unist@2.0.6:
resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==}
dev: false
......@@ -3074,6 +3081,10 @@ packages:
eslint-visitor-keys: 3.3.0
dev: false
/@ungap/structured-clone@1.2.0:
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
dev: false
/@vercel/analytics@1.0.1:
resolution: {integrity: sha512-Ux0c9qUfkcPqng3vrR0GTrlQdqNJ2JREn/2ydrVuKwM3RtMfF2mWX31Ijqo1opSjNAq6rK76PwtANw6kl6TAow==}
dev: false
......@@ -6100,6 +6111,18 @@ packages:
unist-util-visit: 4.1.2
dev: false
/mdast-util-toc@7.0.0:
resolution: {integrity: sha512-C28UcSqjmnWuvgT8d97qpaItHKvySqVPAECUzqQ51xuMyNFFJwcFoKW77KoMjtXrclTidLQFDzLUmTmrshRweA==}
dependencies:
'@types/mdast': 4.0.0
'@types/ungap__structured-clone': 0.3.0
'@ungap/structured-clone': 1.2.0
github-slugger: 2.0.0
mdast-util-to-string: 4.0.0
unist-util-is: 6.0.0
unist-util-visit: 5.0.0
dev: false
/mdn-data@2.0.28:
resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
dev: false
......
......@@ -222,6 +222,7 @@ $utilities: (
class: text,
values: map-merge($colors, (
muted: var(--color-muted, #{$color-muted}),
headers: var(--color-headers, #{$color-headers}),
reset: inherit
))
),
......
......@@ -8,7 +8,7 @@
height: calc(100vh - #{$header-height});
display: flex;
flex-direction: column;
width: 15rem;
width: 13rem;
&:after {
content: '';
......@@ -22,6 +22,18 @@
}
}
.docs-side-toc {
width: 15rem;
position: sticky;
top: calc(#{$header-height} + 1px);
height: calc(100vh - #{$header-height});
display: none;
@include media-breakpoint-up(xl) {
display: block;
}
}
.docs-menu,
.docs-menu-group,
.docs-menu-submenu {
......
......@@ -34,7 +34,7 @@
.icon-inline {
width: 1.2em;
height: 1.2em;
stroke-width: 2;
stroke-width: 1.5;
stroke: currentColor;
vertical-align: sub;
}
......
......@@ -88,7 +88,6 @@ h5,
h6,
.h6 {
@extend %headers-common;
font-weight: $font-weight-normal;
font-size: $font-size-h6;
line-height: $line-height-h6;
margin-bottom: $gap-1;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册