diff --git a/app/editor/DocsLayout.tsx b/app/editor/DocsLayout.tsx
index 5cb3ea7..4394f91 100644
--- a/app/editor/DocsLayout.tsx
+++ b/app/editor/DocsLayout.tsx
@@ -8,14 +8,16 @@ export const DocsLayout: FC<{
}> = ({ nav, pageNav, content }) => {
return (
-
+
{nav}
-
{content}
+
{content}
-
+ {pageNav && (
+
+ )}
)
}
diff --git a/app/editor/NavToggle.tsx b/app/editor/NavToggle.tsx
index 1fd75ba..300db1b 100644
--- a/app/editor/NavToggle.tsx
+++ b/app/editor/NavToggle.tsx
@@ -14,14 +14,17 @@ export const NavToggle: React.FC<{ children: ReactNode }> = ({ children }) => {
e.preventDefault()
setShowNav((v) => !v)
}}
- className="block md:hidden fixed top-4 right-4 z-10"
+ className="block md:hidden fixed top-4 right-4 z-40"
>
{children}
diff --git a/app/editor/api/NavGroup.tsx b/app/editor/api/NavGroup.tsx
new file mode 100644
index 0000000..43893aa
--- /dev/null
+++ b/app/editor/api/NavGroup.tsx
@@ -0,0 +1,47 @@
+'use client'
+import { ApiRefNode, TypeFieldMap, RefNodeField, MarkdownApiRefDocument } from '@/app/apiDocsStructures'
+import React, { useEffect } from 'react'
+import ExpandMoreIcon from '@/app/images/expand_more.svg'
+import ExpandLessIcon from '@/app/images/expand_less.svg'
+import { useParams } from 'next/navigation'
+import Link from 'next/link'
+
+interface NavLink {
+ label: string
+ location: string
+}
+
+interface NavGroup {
+ title: string
+ links: NavLink[]
+}
+
+export default function NavGroup({ group }: { group: NavGroup }) {
+ let slugParam = useParams().slug
+ if (typeof slugParam === 'string') {
+ slugParam = [slugParam]
+ }
+ const slug = (slugParam ?? []).join('/')
+
+ const [expanded, setExpanded] = React.useState(() => {
+ const currentLocation = `/editor/api/${slug}`
+ return group.links.some(({ location }) => location === currentLocation)
+ })
+
+ return (
+
+ - setExpanded((v) => !v)}>
+ {group.title}
+ {expanded ? : }
+
+ {expanded &&
+ group.links.map(({ location, label }, index) => {
+ return (
+ -
+ {label}
+
+ )
+ })}
+
+ )
+}
diff --git a/app/editor/api/[[...slug]]/page.tsx b/app/editor/api/[[...slug]]/page.tsx
new file mode 100644
index 0000000..6254eb7
--- /dev/null
+++ b/app/editor/api/[[...slug]]/page.tsx
@@ -0,0 +1,96 @@
+/* eslint-disable react-refresh/only-export-components */
+import { getApiDocs } from '@/app/getApiDocs'
+import { MDXRemote } from 'next-mdx-remote/rsc'
+import rehypePrism from 'rehype-prism-plus'
+import rehypeSlug from 'rehype-slug'
+import rehypeRewrite from 'rehype-rewrite'
+import { Root, RootContent } from 'hast'
+import remarkGfm from 'remark-gfm'
+import * as fs from 'fs'
+import * as path from 'path'
+import classNames from 'classnames'
+
+interface SlugParam {
+ slug: string[]
+}
+
+export function generateStaticParams() {
+ const files: SlugParam[] = []
+
+ function readFilesRecursively(currentPath: string) {
+ const entries = fs.readdirSync(currentPath)
+
+ for (const entry of entries) {
+ const entryPath = path.join(currentPath, entry)
+ const stat = fs.statSync(entryPath)
+
+ if (stat.isDirectory()) {
+ readFilesRecursively(entryPath)
+ } else if (stat.isFile() && path.extname(entryPath) === '.md') {
+ const slugParts = entryPath.replace('api-ref/', '').replace('.md', '').replace('README', '').split('/')
+
+ files.push({
+ slug: slugParts,
+ })
+ }
+ }
+ }
+
+ readFilesRecursively('./api-ref')
+
+ return files
+}
+
+interface PageParams {
+ slug: string[]
+}
+
+export function generateMetadata({ params }: { params: PageParams }) {
+ return {}
+ const { docs } = getApiDocs('./api-ref')
+ const doc = docs.find((file) => file.slug === params.slug)
+ return {
+ title: `${doc?.title} | MDXEditor`,
+ description:
+ 'MDXEditor is an open-source React component that lets your users edit markdown documents naturally, just like in Google docs or Notion.',
+ }
+}
+
+export default function Page({ params }: { params: PageParams }) {
+ let slug = params.slug
+ if (!slug) {
+ slug = ['README']
+ }
+
+ const pageContent = fs.readFileSync(`./api-ref/${slug.join('/')}.md`, 'utf-8')
+
+ return (
+
+
+
+ )
+}
diff --git a/app/editor/api/[slug]/Nav.tsx b/app/editor/api/[slug]/Nav.tsx
deleted file mode 100644
index d8da702..0000000
--- a/app/editor/api/[slug]/Nav.tsx
+++ /dev/null
@@ -1,184 +0,0 @@
-'use client'
-import { ApiRefNode, TypeFieldMap, RefNodeField, MarkdownApiRefDocument } from '@/app/apiDocsStructures'
-import React from 'react'
-import ExpandMoreIcon from '@/app/images/expand_more.svg'
-import ExpandLessIcon from '@/app/images/expand_less.svg'
-
-function nodeTitle(node: ApiRefNode) {
- if (node.document.title === 'editor') {
- return '@mdxeditor/editor package'
- }
-
- if (node.document.type === 'property' || node.document.type === 'method') {
- return node.document.title.split('.').at(-1)
- }
- return node.document.title
-}
-
-const ExpandedNodesContext = React.createContext<{ expandedNodes: Set
; toggle: (path: string) => void; currentDocSlug: string }>({
- expandedNodes: new Set(),
- currentDocSlug: '',
- toggle: () => {
- throw new Error('implement this')
- },
-})
-
-const ApiRefNodeNav: React.FC<{ node: ApiRefNode }> = ({ node }) => {
- const { expandedNodes, toggle, currentDocSlug } = React.useContext(ExpandedNodesContext)
-
- return node.document.type === 'interface' || node.document.type === 'variable' ? (
-
- ) : node.document.type === 'class' ? (
-
- ) : (
- <>
-
- {nodeTitle(node)}
-
-
- {node.constructorNode && (
- -
- constructor
-
- )}
- {Object.values(TypeFieldMap)
- .filter((value) => value !== 'constructor')
- .map((fieldName, index) => {
- const expandKey = `${node.document.slug}.${fieldName}`
- const isExpanded = expandedNodes.has(expandKey)
- return node[fieldName] ? (
-
- - toggle(expandKey)}>
- {fieldName.replace(/./, (c) => c.toUpperCase())}
- {isExpanded ? : }
-
- {isExpanded &&
- node[fieldName as Exclude]!.map((child: ApiRefNode, index: number) => {
- return (
- -
-
-
- )
- })}
-
- ) : null
- })}
-
- >
- )
-}
-
-const InterfaceRefNodeNav: React.FC<{ node: ApiRefNode }> = ({ node }) => {
- const { expandedNodes, currentDocSlug } = React.useContext(ExpandedNodesContext)
- const isExpanded = expandedNodes.has(node.document.slug)
-
- return (
- <>
-
- {isExpanded && node.properties && (
-
- {node.properties.map((child: ApiRefNode, index: number) => {
- return (
- -
-
-
- )
- })}
-
- )}
- >
- )
-}
-
-const ClassRefNodeNav: React.FC<{ node: ApiRefNode }> = ({ node }) => {
- const { currentDocSlug, expandedNodes } = React.useContext(ExpandedNodesContext)
- const isExpanded = expandedNodes.has(node.document.slug)
-
- return (
- <>
-
- {isExpanded && node.properties && (
-
- {node.constructorNode && (
- -
- constructor
-
- )}
- {Object.values(TypeFieldMap)
- .filter((value) => value !== 'constructor')
- .map((fieldName, index) => {
- return node[fieldName] ? (
-
- - {fieldName.replace(/./, (c) => c.toUpperCase())}
- {node[fieldName as Exclude]!.map((child: ApiRefNode, index: number) => {
- return (
- -
-
-
- )
- })}
-
- ) : null
- })}
-
- )}
- >
- )
-}
-
-export function ApiNav({
- root,
- currentDoc,
- docs,
-}: {
- root: ApiRefNode
- currentDoc: MarkdownApiRefDocument
- docs: MarkdownApiRefDocument[]
-}) {
- const [expandedNodes, setExpandedNodes] = React.useState(() => {
- const set = new Set()
- const pathPieces = currentDoc.slug.split('.')
- set.add(currentDoc.slug)
- for (let i = pathPieces.length; i > 1; i--) {
- const slug = pathPieces.slice(0, i).join('.')
- const parentSlug = pathPieces.slice(0, i - 1).join('.')
- const doc = docs.find((doc) => doc.slug === slug)
- if (!doc) {
- throw new Error(`Cant find ${slug} ${pathPieces} ${i}`)
- }
- // const parent = docs.find((doc) => doc.slug === parentSlug)!
- const fieldToExpandInParent = TypeFieldMap[doc?.type]
- set.add(`${parentSlug}`)
- set.add(`${parentSlug}.${fieldToExpandInParent}`)
- }
- return set
- })
- const toggle = React.useCallback((path: string) => {
- setExpandedNodes((expanded) => {
- if (expanded.has(path)) {
- expanded.delete(path)
- } else {
- expanded.add(path)
- }
- return new Set(expanded)
- })
- }, [])
-
- return (
-
-
-
- )
-}
diff --git a/app/editor/api/[slug]/page.tsx b/app/editor/api/[slug]/page.tsx
deleted file mode 100644
index b258e26..0000000
--- a/app/editor/api/[slug]/page.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-/* eslint-disable react-refresh/only-export-components */
-import { getApiDocs } from '@/app/getApiDocs'
-import { ApiNav } from './Nav'
-import { fromMarkdown } from 'mdast-util-from-markdown'
-import { toc } from 'mdast-util-toc'
-import { toMarkdown } from 'mdast-util-to-markdown'
-import { MDXRemote } from 'next-mdx-remote/rsc'
-import rehypePrism from 'rehype-prism-plus'
-import rehypeSlug from 'rehype-slug'
-import rehypeRewrite from 'rehype-rewrite'
-import { Root, RootContent } from 'hast'
-import remarkGfm from 'remark-gfm'
-import { DocsLayout } from '../../DocsLayout'
-
-export function generateStaticParams() {
- const { docs } = getApiDocs('./api-ref')
- return docs.map(({ slug }) => ({ slug }))
-}
-
-interface PageParams {
- slug: string
-}
-
-export function generateMetadata({ params }: { params: PageParams }) {
- const { docs } = getApiDocs('./api-ref')
- const doc = docs.find((file) => file.slug === params.slug)
- return {
- title: `${doc?.title} | MDXEditor`,
- description:
- 'MDXEditor is an open-source React component that lets your users edit markdown documents naturally, just like in Google docs or Notion.',
- }
-}
-
-export default function Page({ params }: { params: PageParams }) {
- const { docs, root } = getApiDocs('./api-ref')
- const doc = docs.find((file) => file.slug === params.slug)
-
- if (!doc) {
- throw new Error(`No doc found for ${params.slug}`)
- }
-
- const tree = fromMarkdown(doc.content, {
- extensions: [],
- mdastExtensions: [],
- })
-
- const tocTree = toc(tree)
- const tocMarkdown = toMarkdown(tocTree.map!)
-
- return (
- }
- content={
-
- }
- pageNav={}
- />
- )
-}
diff --git a/app/editor/api/layout.tsx b/app/editor/api/layout.tsx
new file mode 100644
index 0000000..cecb660
--- /dev/null
+++ b/app/editor/api/layout.tsx
@@ -0,0 +1,63 @@
+/* eslint-disable react-refresh/only-export-components */
+import { DocsLayout } from '../DocsLayout'
+import { readFileSync } from 'fs'
+import { fromMarkdown } from 'mdast-util-from-markdown'
+import * as Mdast from 'mdast'
+import NavGroup from './NavGroup'
+
+interface NavLink {
+ label: string
+ location: string
+}
+
+interface NavGroup {
+ title: string
+ links: NavLink[]
+}
+
+function getNav(): NavGroup[] {
+ const markdown = readFileSync('./api-ref/README.md', 'utf-8')
+ const md = markdown.split('\n## ')[1].split('\n').slice(1).join('\n')
+
+ const navGroups: NavGroup[] = []
+
+ const tree = fromMarkdown(md, {
+ extensions: [],
+ mdastExtensions: [],
+ })
+
+ for (let i = 0; i < tree.children.length; i += 2) {
+ const heading = tree.children[i] as Mdast.Heading
+ const list = tree.children[i + 1] as Mdast.List
+ navGroups.push({
+ title: (heading.children[0] as Mdast.Text).value,
+ links: list.children.map((item) => {
+ const link = (item.children[0] as Mdast.Paragraph).children[0] as Mdast.Link
+ return {
+ label: (link.children[0] as Mdast.Text).value,
+ location: `/editor/api/${link.url.replace('.md', '').replace('README', '')}`,
+ }
+ }),
+ })
+ }
+
+ return navGroups
+}
+
+export default function Layout({ children }: { children: React.ReactNode }) {
+ const navGroups = getNav()
+
+ const nav = (
+
+ )
+
+ return (
+ <>
+
+ >
+ )
+}
diff --git a/app/editor/demo/live-demo.tsx b/app/editor/demo/live-demo.tsx
index f269460..b96d623 100644
--- a/app/editor/demo/live-demo.tsx
+++ b/app/editor/demo/live-demo.tsx
@@ -69,5 +69,12 @@ const allPlugins = (diffMarkdown: string) => [
]
export function LiveDemo({ markdown }: { markdown: string }) {
- return
+ return (
+
+ )
}
diff --git a/app/getApiRefPaths.ts b/app/getApiRefPaths.ts
new file mode 100644
index 0000000..f6134ff
--- /dev/null
+++ b/app/getApiRefPaths.ts
@@ -0,0 +1,32 @@
+import * as fs from 'fs'
+import * as path from 'path'
+
+interface SlugParam {
+ slug: string[]
+}
+export function getApiRefPaths(): SlugParam[] {
+ const files: SlugParam[] = []
+
+ function readFilesRecursively(currentPath: string) {
+ const entries = fs.readdirSync(currentPath)
+
+ for (const entry of entries) {
+ const entryPath = path.join(currentPath, entry)
+ const stat = fs.statSync(entryPath)
+
+ if (stat.isDirectory()) {
+ readFilesRecursively(entryPath)
+ } else if (stat.isFile() && path.extname(entryPath) === '.md') {
+ const slugParts = entryPath.replace('api-ref/', '').replace('.md', '').replace('README', '').split('/')
+
+ files.push({
+ slug: slugParts,
+ })
+ }
+ }
+ }
+
+ readFilesRecursively('./api-ref')
+
+ return files
+}
diff --git a/app/globals.css b/app/globals.css
index c1f2bf6..6812255 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1,6 +1,7 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
+@import "tailwindcss/base";
+@import "tailwindcss/components";
+@import "tailwindcss/utilities";
+@import '../node_modules/@mdxeditor/editor/dist/style.css';
@layer components {
.my-line-highlight span.token {
@@ -9,11 +10,13 @@
.prose {
min-width: 0;
+
& code {
@apply text-secondary-text bg-neutral-bgSubtle px-1;
- &:before, &:after {
+ &:before,
+ &:after {
content: none;
@apply text-accent-text;
}
@@ -28,18 +31,24 @@
@apply font-mono;
font-size: inherit;
font-weight: inherit;
- &:before, &:after {
+
+ &:before,
+ &:after {
content: none;
}
}
/* reset the prose borders */
- & [data-lexical-decorator] > table {
+ & [data-lexical-decorator]>table {
table-layout: fixed;
+
& td {
vertical-align: middle;
}
- & > thead, & > tfoot, & > tbody > tr {
+
+ &>thead,
+ &>tfoot,
+ &>tbody>tr {
border: 0;
}
@@ -50,7 +59,7 @@
vertical-align: middle;
}
- & > thead [data-tool-cell]:first-child {
+ &>thead [data-tool-cell]:first-child {
vertical-align: middle;
}
}
@@ -59,12 +68,13 @@
overflow-x: auto;
}
- & pre[class*="language-"], & code[class*="language-"] {
+ & pre[class*="language-"],
+ & code[class*="language-"] {
@apply font-mono;
font-size: inherit;
}
- & pre[class*="language-"] > code {
+ & pre[class*="language-"]>code {
@apply text-sm;
display: block;
@@ -75,11 +85,16 @@
margin: 0;
font-size: inherit;
width: auto;
- & thead, & tbody, & tfoot, & tr {
+
+ & thead,
+ & tbody,
+ & tfoot,
+ & tr {
border: 0;
}
}
}
+
& li[role=checkbox] {
text-indent: 1.2rem;
}
@@ -87,17 +102,48 @@
& li[role=checkbox]::before {
transform: translate(6px, 6px);
}
+
& li[role=checkbox]::after {
transform: translate(6px, 6px) rotate(45deg);
}
}
- .api-ref-nav > a {
+ .api-ref-nav>a {
@apply mb-3;
display: block;
}
- .api-ref-nav > dl > dd > dl {
+ .api-ref-nav a::before {
+ @apply rounded-sm border-neutral-text border-[1px] font-mono text-xs;
+ content: ' ';
+ line-height: 1rem;
+ position: relative;
+ display: inline-block;
+ margin-inline-end: 0.5rem;
+ padding: 0 0.15rem;
+ }
+
+ .api-ref-nav a[href*='types']::before {
+ content: 'T';
+ }
+
+ .api-ref-nav a[href*='variables']::before {
+ content: 'V';
+ }
+
+ .api-ref-nav a[href*='functions']::before {
+ content: 'F';
+ }
+
+ .api-ref-nav a[href*='interfaces']::before {
+ content: 'I';
+ }
+
+ .api-ref-nav a[href*='classes']::before {
+ content: 'C';
+ }
+
+ .api-ref-nav>dl>dd>dl {
@apply pl-2;
}
@@ -105,12 +151,12 @@
@apply mb-2;
}
- .api-ref-nav > dl > dt {
+ .api-ref-nav>dl>dt {
@apply font-medium;
}
.in-page-nav {
- @apply border-l-accent-solid border-dotted border-l-2 pl-4 text-sm sticky top-2 w-48;
+ @apply border-l-accent-solid border-dotted border-l-2 pl-4 text-sm sticky top-[94px] w-48;
p {
@apply mb-2;
@@ -121,7 +167,7 @@
@apply pl-2;
}
- li > a {
+ li>a {
@apply block mb-1 text-neutral-text;
}
}
@@ -137,12 +183,17 @@
}
}
- h2#classes + table,
- h2#functions + table,
- h2#interfaces + table,
- h2#type-aliases + table
- {
- & td:first-child,
+ .doc-content .homepage ul {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ }
+
+ h2#classes+table,
+ h2#functions+table,
+ h2#interfaces+table,
+ h2#type-aliases+table {
+
+ & td:first-child,
& tr:first-child {
width: 50%;
}
@@ -156,8 +207,19 @@
@apply inline;
}
}
+
& p {
@apply mb-4;
}
}
}
+
+.full-demo-mdxeditor [role=toolbar] {
+ top: 74px;
+}
+
+@media (max-width: 768px) {
+ .full-demo-mdxeditor [role=toolbar] {
+ top: 196px;
+ }
+}
diff --git a/app/layout.tsx b/app/layout.tsx
index fafa31b..5f62dbd 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,4 +1,3 @@
-import '@mdxeditor/editor/style.css'
import 'prism-themes/themes/prism-one-light.css'
import './globals.css'
@@ -36,12 +35,12 @@ const RootLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
-
+