Unify workspace dropdowns; Update layouts (#458)
Unified Account and Workspace drop-downs; Layout updates, now header lives within the PageBody component; Sidebars now use floating variant
This commit is contained in:
committed by
GitHub
parent
ca585e09be
commit
4bc8448a1d
@@ -1,32 +0,0 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
export function DocsCard({
|
||||
title,
|
||||
subtitle,
|
||||
children,
|
||||
link,
|
||||
}: React.PropsWithChildren<{
|
||||
title: string;
|
||||
subtitle?: string | null;
|
||||
link: { url: string; label?: string };
|
||||
}>) {
|
||||
return (
|
||||
<Link href={link.url} className="flex flex-col">
|
||||
<div
|
||||
className={`hover:bg-muted/70 flex grow flex-col gap-y-0.5 rounded border p-4`}
|
||||
>
|
||||
<h3 className="mt-0 text-lg font-medium hover:underline dark:text-white">
|
||||
{title}
|
||||
</h3>
|
||||
|
||||
{subtitle && (
|
||||
<div className="text-muted-foreground text-sm">
|
||||
<p dangerouslySetInnerHTML={{ __html: subtitle }}></p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{children && <div className="text-sm">{children}</div>}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Cms } from '@kit/cms';
|
||||
|
||||
import { DocsCard } from './docs-card';
|
||||
|
||||
export function DocsCards({ cards }: { cards: Cms.ContentItem[] }) {
|
||||
const cardsSortedByOrder = [...cards].sort((a, b) => a.order - b.order);
|
||||
|
||||
return (
|
||||
<div className={'absolute flex w-full flex-col gap-4 pb-48 lg:max-w-2xl'}>
|
||||
{cardsSortedByOrder.map((item) => {
|
||||
return (
|
||||
<DocsCard
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
subtitle={item.description}
|
||||
link={{
|
||||
url: `/docs/${item.slug}`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { SidebarMenuButton, SidebarMenuItem } from '@kit/ui/shadcn-sidebar';
|
||||
import { cn, isRouteActive } from '@kit/ui/utils';
|
||||
|
||||
export function DocsNavLink({
|
||||
label,
|
||||
url,
|
||||
children,
|
||||
}: React.PropsWithChildren<{ label: string; url: string }>) {
|
||||
const currentPath = usePathname();
|
||||
const isCurrent = isRouteActive(url, currentPath, true);
|
||||
|
||||
return (
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
isActive={isCurrent}
|
||||
className={cn('text-secondary-foreground transition-all')}
|
||||
>
|
||||
<Link href={url}>
|
||||
<span className="block max-w-full truncate">{label}</span>
|
||||
|
||||
{children}
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { Cms } from '@kit/cms';
|
||||
import { Collapsible } from '@kit/ui/collapsible';
|
||||
import { cn, isRouteActive } from '@kit/ui/utils';
|
||||
|
||||
export function DocsNavigationCollapsible(
|
||||
props: React.PropsWithChildren<{
|
||||
node: Cms.ContentItem;
|
||||
prefix: string;
|
||||
}>,
|
||||
) {
|
||||
const currentPath = usePathname();
|
||||
const prefix = props.prefix;
|
||||
|
||||
const isChildActive = props.node.children.some((child) =>
|
||||
isRouteActive(prefix + '/' + child.url, currentPath, false),
|
||||
);
|
||||
|
||||
return (
|
||||
<Collapsible
|
||||
className={cn('group/collapsible', {
|
||||
'group/active': isChildActive,
|
||||
})}
|
||||
defaultOpen={isChildActive ? true : !props.node.collapsed}
|
||||
>
|
||||
{props.children}
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
|
||||
import { Cms } from '@kit/cms';
|
||||
import { CollapsibleContent, CollapsibleTrigger } from '@kit/ui/collapsible';
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarMenuSub,
|
||||
} from '@kit/ui/shadcn-sidebar';
|
||||
|
||||
import { DocsNavLink } from '~/(marketing)/docs/_components/docs-nav-link';
|
||||
import { DocsNavigationCollapsible } from '~/(marketing)/docs/_components/docs-navigation-collapsible';
|
||||
|
||||
import { FloatingDocumentationNavigation } from './floating-docs-navigation';
|
||||
|
||||
function Node({
|
||||
node,
|
||||
level,
|
||||
prefix,
|
||||
}: {
|
||||
node: Cms.ContentItem;
|
||||
level: number;
|
||||
prefix: string;
|
||||
}) {
|
||||
const url = `${prefix}/${node.slug}`;
|
||||
const label = node.label ? node.label : node.title;
|
||||
|
||||
return (
|
||||
<NodeContainer node={node} prefix={prefix}>
|
||||
<NodeTrigger node={node} label={label} url={url} />
|
||||
|
||||
<NodeContentContainer node={node}>
|
||||
<Tree pages={node.children ?? []} level={level + 1} prefix={prefix} />
|
||||
</NodeContentContainer>
|
||||
</NodeContainer>
|
||||
);
|
||||
}
|
||||
|
||||
function NodeContentContainer({
|
||||
node,
|
||||
children,
|
||||
}: {
|
||||
node: Cms.ContentItem;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
if (node.collapsible) {
|
||||
return <CollapsibleContent>{children}</CollapsibleContent>;
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
function NodeContainer({
|
||||
node,
|
||||
prefix,
|
||||
children,
|
||||
}: {
|
||||
node: Cms.ContentItem;
|
||||
prefix: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
if (node.collapsible) {
|
||||
return (
|
||||
<DocsNavigationCollapsible node={node} prefix={prefix}>
|
||||
{children}
|
||||
</DocsNavigationCollapsible>
|
||||
);
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
function NodeTrigger({
|
||||
node,
|
||||
label,
|
||||
url,
|
||||
}: {
|
||||
node: Cms.ContentItem;
|
||||
label: string;
|
||||
url: string;
|
||||
}) {
|
||||
if (node.collapsible) {
|
||||
return (
|
||||
<CollapsibleTrigger asChild>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton>
|
||||
{label}
|
||||
<ChevronDown className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180" />
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</CollapsibleTrigger>
|
||||
);
|
||||
}
|
||||
|
||||
return <DocsNavLink label={label} url={url} />;
|
||||
}
|
||||
|
||||
function Tree({
|
||||
pages,
|
||||
level,
|
||||
prefix,
|
||||
}: {
|
||||
pages: Cms.ContentItem[];
|
||||
level: number;
|
||||
prefix: string;
|
||||
}) {
|
||||
if (level === 0) {
|
||||
return pages.map((treeNode, index) => (
|
||||
<Node key={index} node={treeNode} level={level} prefix={prefix} />
|
||||
));
|
||||
}
|
||||
|
||||
if (pages.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarMenuSub>
|
||||
{pages.map((treeNode, index) => (
|
||||
<Node key={index} node={treeNode} level={level} prefix={prefix} />
|
||||
))}
|
||||
</SidebarMenuSub>
|
||||
);
|
||||
}
|
||||
|
||||
export function DocsNavigation({
|
||||
pages,
|
||||
prefix = '/docs',
|
||||
}: {
|
||||
pages: Cms.ContentItem[];
|
||||
prefix?: string;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<Sidebar
|
||||
variant={'ghost'}
|
||||
className={
|
||||
'border-border/50 sticky z-1 mt-4 max-h-full overflow-y-auto pr-4'
|
||||
}
|
||||
>
|
||||
<SidebarGroup className="p-0">
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu className={'pb-48'}>
|
||||
<Tree pages={pages} level={0} prefix={prefix} />
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
</Sidebar>
|
||||
|
||||
<div className={'lg:hidden'}>
|
||||
<FloatingDocumentationNavigation>
|
||||
<SidebarGroup>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
<Tree pages={pages} level={0} prefix={prefix} />
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
</FloatingDocumentationNavigation>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useEffectEvent, useMemo, useState } from 'react';
|
||||
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { Menu } from 'lucide-react';
|
||||
|
||||
import { isBrowser } from '@kit/shared/utils';
|
||||
import { Button } from '@kit/ui/button';
|
||||
import { If } from '@kit/ui/if';
|
||||
|
||||
export function FloatingDocumentationNavigation(
|
||||
props: React.PropsWithChildren,
|
||||
) {
|
||||
const activePath = usePathname();
|
||||
|
||||
const body = useMemo(() => {
|
||||
return isBrowser() ? document.body : null;
|
||||
}, []);
|
||||
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
const enableScrolling = useEffectEvent(
|
||||
() => body && (body.style.overflowY = ''),
|
||||
);
|
||||
|
||||
const disableScrolling = useEffectEvent(
|
||||
() => body && (body.style.overflowY = 'hidden'),
|
||||
);
|
||||
|
||||
// enable/disable body scrolling when the docs are toggled
|
||||
useEffect(() => {
|
||||
if (isVisible) {
|
||||
disableScrolling();
|
||||
} else {
|
||||
enableScrolling();
|
||||
}
|
||||
}, [isVisible]);
|
||||
|
||||
// hide docs when navigating to another page
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
setIsVisible(false);
|
||||
}, [activePath]);
|
||||
|
||||
const onClick = () => {
|
||||
setIsVisible(!isVisible);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<If condition={isVisible}>
|
||||
<div
|
||||
className={
|
||||
'fixed top-0 left-0 z-10 h-screen w-full p-4' +
|
||||
' dark:bg-background flex flex-col space-y-4 overflow-auto bg-white'
|
||||
}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
</If>
|
||||
|
||||
<Button
|
||||
className={'fixed right-5 bottom-5 z-10 h-16 w-16 rounded-full'}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Menu className={'h-8'} />
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user