mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 05:23:40 +00:00
UI: Support custom max width
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-ui': minor
|
||||
---
|
||||
|
||||
Support custom max width
|
||||
@@ -24,5 +24,4 @@ export const { getPage, getPages, pageTree } = loader({
|
||||
|
||||
export const openapi = createOpenAPI({
|
||||
documentOrPath: path.resolve('./unkey.json'),
|
||||
generateTypeScriptSchema: false,
|
||||
});
|
||||
|
||||
@@ -73,12 +73,12 @@ export function DynamicSidebar(props: SidebarProps): React.ReactElement {
|
||||
onPointerLeave: onLeave,
|
||||
'aria-hidden': Boolean(collapsed && !hover),
|
||||
className: cn(
|
||||
'md:transition-[transform,margin]',
|
||||
'md:transition-all',
|
||||
collapsed && [
|
||||
'md:top-1 md:mr-[-240px] md:h-[calc(100dvh-4px)] md:animate-fd-sidebar-collapse md:rounded-xl md:border md:shadow-md xl:mr-[-260px]',
|
||||
'md:top-1 md:me-fd-sidebar-offset md:h-[calc(100dvh-4px)] md:w-[var(--fd-sidebar-width)] md:animate-fd-sidebar-collapse md:rounded-xl md:border md:ps-0 md:shadow-md',
|
||||
hover
|
||||
? 'md:translate-x-1 rtl:md:-translate-x-1'
|
||||
: 'md:-translate-x-full rtl:md:translate-x-full',
|
||||
: 'md:translate-x-[calc(var(--fd-sidebar-width)*-1)] rtl:md:translate-x-[var(--fd-sidebar-width)]',
|
||||
],
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -53,6 +53,13 @@ export interface SidebarProps {
|
||||
|
||||
footer?: React.ReactNode;
|
||||
footerProps?: HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
/**
|
||||
* Hide search trigger
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
hideSearch?: boolean;
|
||||
}
|
||||
|
||||
interface InternalContext {
|
||||
@@ -89,6 +96,7 @@ export function Sidebar({
|
||||
aside?: HTMLAttributes<HTMLElement> & Record<string, unknown>;
|
||||
}): React.ReactElement {
|
||||
const search = useSearchContext();
|
||||
const hasSearch = search.enabled && !props.hideSearch;
|
||||
const context = useMemo<InternalContext>(
|
||||
() => ({
|
||||
defaultOpenLevel,
|
||||
@@ -105,23 +113,25 @@ export function Sidebar({
|
||||
blockScrollingWidth={768} // md
|
||||
{...props.aside}
|
||||
className={cn(
|
||||
'fixed z-30 flex shrink-0 flex-col bg-fd-card text-sm md:sticky md:top-0 md:h-dvh md:w-[240px] md:border-e xl:w-[260px]',
|
||||
'fixed z-30 flex flex-col bg-fd-card text-sm md:sticky md:top-0 md:h-dvh md:w-[var(--fd-c-sidebar)] md:min-w-[var(--fd-sidebar-width)] md:border-e md:ps-[calc(var(--fd-c-sidebar)-var(--fd-sidebar-width))]',
|
||||
'max-md:inset-0 max-md:bg-fd-background/80 max-md:pt-14 max-md:text-[15px] max-md:backdrop-blur-md max-md:data-[open=false]:hidden',
|
||||
props.aside?.className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
{...props.bannerProps}
|
||||
className={cn(
|
||||
'flex flex-col gap-2 px-4 pt-2 md:px-3 md:pt-4',
|
||||
props.bannerProps?.className,
|
||||
)}
|
||||
>
|
||||
{props.banner}
|
||||
{search.enabled ? (
|
||||
<LargeSearchToggle className="rounded-lg max-md:hidden" />
|
||||
) : null}
|
||||
</div>
|
||||
{hasSearch || props.banner ? (
|
||||
<div
|
||||
{...props.bannerProps}
|
||||
className={cn(
|
||||
'flex flex-col gap-2 px-4 pt-2 md:px-3 md:pt-4',
|
||||
props.bannerProps?.className,
|
||||
)}
|
||||
>
|
||||
{props.banner}
|
||||
{hasSearch ? (
|
||||
<LargeSearchToggle className="rounded-lg max-md:hidden" />
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<ViewportContent>
|
||||
{items.length > 0 && (
|
||||
<div className="flex flex-col md:hidden">
|
||||
|
||||
@@ -41,7 +41,7 @@ export function Toc({ items, header, footer }: TOCProps): ReactElement {
|
||||
return (
|
||||
<div
|
||||
data-toc=""
|
||||
className="sticky top-0 flex h-dvh w-[220px] shrink-0 flex-col gap-4 pr-3 pt-12 max-lg:hidden xl:w-[260px] rtl:pl-3"
|
||||
className="sticky top-0 flex h-dvh w-[var(--fd-c-toc)] min-w-[var(--fd-toc-width)] flex-col gap-4 pe-[max(calc(var(--fd-c-toc)-var(--fd-toc-width)),0.75rem)] pt-12 max-lg:hidden"
|
||||
>
|
||||
{header}
|
||||
<h3 className="-mb-1 -ms-0.5 inline-flex items-center gap-1.5 text-sm text-fd-muted-foreground">
|
||||
|
||||
+45
-32
@@ -63,17 +63,44 @@ export function DocsLayout({
|
||||
component: sidebarReplace,
|
||||
...sidebar
|
||||
} = {},
|
||||
links = [],
|
||||
containerProps = {},
|
||||
tree,
|
||||
i18n = false,
|
||||
children,
|
||||
...props
|
||||
}: DocsLayoutProps): React.ReactElement {
|
||||
const finalLinks = getLinks(links, githubUrl);
|
||||
const links = getLinks(props.links ?? [], githubUrl);
|
||||
const Aside = collapsible ? DynamicSidebar : Sidebar;
|
||||
|
||||
const banner: ReactNode[] = [];
|
||||
if (nav?.title)
|
||||
banner.push(
|
||||
<Link
|
||||
key="title"
|
||||
href={nav.url ?? '/'}
|
||||
className="inline-flex items-center gap-2.5 font-medium"
|
||||
>
|
||||
{nav.title}
|
||||
</Link>,
|
||||
);
|
||||
|
||||
if (links.length > 0)
|
||||
banner.push(
|
||||
<LinksMenu
|
||||
key="links"
|
||||
items={links}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
size: 'icon',
|
||||
color: 'ghost',
|
||||
className: 'ms-auto',
|
||||
}),
|
||||
)}
|
||||
>
|
||||
<MoreHorizontal />
|
||||
</LinksMenu>,
|
||||
);
|
||||
|
||||
return (
|
||||
<TreeContextProvider tree={tree}>
|
||||
<TreeContextProvider tree={props.tree}>
|
||||
{replaceOrDefault(nav, <SubNav {...nav} />)}
|
||||
<main
|
||||
id="nd-docs-layout"
|
||||
@@ -84,39 +111,25 @@ export function DocsLayout({
|
||||
{ enabled: sidebarEnabled, component: sidebarReplace },
|
||||
<Aside
|
||||
{...sidebar}
|
||||
items={finalLinks}
|
||||
items={links}
|
||||
banner={
|
||||
<>
|
||||
<div className="flex flex-row items-center justify-between border-b pb-2 max-md:hidden">
|
||||
<Link
|
||||
href={nav?.url ?? '/'}
|
||||
className="inline-flex items-center gap-2.5 font-medium"
|
||||
>
|
||||
{nav?.title}
|
||||
</Link>
|
||||
{finalLinks.length > 0 && (
|
||||
<LinksMenu
|
||||
items={finalLinks}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
size: 'icon',
|
||||
color: 'ghost',
|
||||
}),
|
||||
)}
|
||||
>
|
||||
<MoreHorizontal />
|
||||
</LinksMenu>
|
||||
)}
|
||||
</div>
|
||||
{sidebar.banner}
|
||||
</>
|
||||
banner.length > 0 || sidebar.banner ? (
|
||||
<>
|
||||
{banner.length > 0 ? (
|
||||
<div className="flex flex-row items-center border-b pb-2 max-md:hidden">
|
||||
{banner}
|
||||
</div>
|
||||
) : null}
|
||||
{sidebar.banner}
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
bannerProps={{
|
||||
...sidebar.bannerProps,
|
||||
className: cn(
|
||||
!sidebar.banner && 'max-md:hidden',
|
||||
sidebar.bannerProps?.className,
|
||||
),
|
||||
...sidebar.bannerProps,
|
||||
}}
|
||||
footer={
|
||||
<>
|
||||
@@ -127,7 +140,7 @@ export function DocsLayout({
|
||||
}
|
||||
/>,
|
||||
)}
|
||||
{children}
|
||||
{props.children}
|
||||
</main>
|
||||
</TreeContextProvider>
|
||||
);
|
||||
|
||||
@@ -93,7 +93,7 @@ export function DocsPage({
|
||||
</div>
|
||||
<article
|
||||
className={cn(
|
||||
'mx-auto flex w-full max-w-[840px] flex-1 flex-col gap-6 px-4 pt-10 md:px-6 md:pt-12',
|
||||
'mx-auto flex w-full max-w-[860px] flex-1 flex-col gap-6 px-4 pt-10 md:px-6 md:pt-12',
|
||||
!tocOptions.enabled && 'max-w-[1200px]',
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -27,6 +27,13 @@ interface DocsUIOptions {
|
||||
*/
|
||||
modifyContainer?: boolean;
|
||||
|
||||
/**
|
||||
* Max width of docs layout
|
||||
*
|
||||
* @defaultValue '1600px'
|
||||
*/
|
||||
layoutWidth?: string;
|
||||
|
||||
/**
|
||||
* Color preset
|
||||
*/
|
||||
@@ -108,13 +115,22 @@ function createTailwindColors(
|
||||
}
|
||||
|
||||
export const docsUi = plugin.withOptions<DocsUIOptions>(
|
||||
({ cssPrefix = '', preset = 'default' } = {}) => {
|
||||
({ cssPrefix = '', preset = 'default', layoutWidth = '1600px' } = {}) => {
|
||||
return ({ addBase, addComponents, addUtilities }) => {
|
||||
const { light, dark, css } =
|
||||
typeof preset === 'string' ? presets[preset] : preset;
|
||||
|
||||
addBase({
|
||||
':root': getThemeStyles(cssPrefix, light),
|
||||
':root': {
|
||||
...getThemeStyles(cssPrefix, light),
|
||||
'--fd-sidebar-width': '0px',
|
||||
'--fd-toc-width': '0px',
|
||||
'--fd-layout-width': layoutWidth,
|
||||
// computed
|
||||
'--fd-doc':
|
||||
'calc(min(100vw, var(--fd-layout-width)) - var(--fd-toc-width) - var(--fd-sidebar-width))',
|
||||
'--fd-c-sidebar': 'calc(50vw - var(--fd-doc))',
|
||||
},
|
||||
'.dark': getThemeStyles(cssPrefix, dark),
|
||||
'*': {
|
||||
'border-color': `theme('colors.fd-border')`,
|
||||
@@ -123,6 +139,19 @@ export const docsUi = plugin.withOptions<DocsUIOptions>(
|
||||
'background-color': `theme('colors.fd-background')`,
|
||||
color: `theme('colors.fd-foreground')`,
|
||||
},
|
||||
'@screen md': {
|
||||
':root': { '--fd-sidebar-width': '240px' },
|
||||
},
|
||||
'@screen lg': {
|
||||
':root': {
|
||||
'--fd-toc-width': '240px',
|
||||
'--fd-c-toc': 'calc(50vw - var(--fd-doc) / 2)',
|
||||
'--fd-c-sidebar': 'calc(50vw - var(--fd-doc) / 2)',
|
||||
},
|
||||
},
|
||||
'@screen xl': {
|
||||
':root': { '--fd-sidebar-width': '260px', '--fd-toc-width': '260px' },
|
||||
},
|
||||
});
|
||||
|
||||
if (css) addComponents(css);
|
||||
@@ -214,6 +243,11 @@ export const docsUi = plugin.withOptions<DocsUIOptions>(
|
||||
maxWidth: {
|
||||
container: '1400px',
|
||||
},
|
||||
margin: {
|
||||
// the offset given to docs content when the sidebar is collapsed
|
||||
'fd-sidebar-offset':
|
||||
'max(calc(var(--fd-c-sidebar) - 2 * var(--fd-sidebar-width)), var(--fd-sidebar-width) * -1)',
|
||||
},
|
||||
colors: createTailwindColors(cssPrefix, addGlobalColors),
|
||||
...animations,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user