This commit is contained in:
Benjamin Toby
2025-03-27 07:37:16 +01:00
parent 9dd6c3a70e
commit 8762e2da8d
23 changed files with 968 additions and 88 deletions
@@ -0,0 +1,38 @@
import { DetailedHTMLProps, HTMLAttributes } from "react";
import { DocsLinkType } from ".";
import Stack from "../../layout/Stack";
import TWUIDocsLink from "./TWUIDocsLink";
import { twMerge } from "tailwind-merge";
type Props = DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> & {
DocsLinks: DocsLinkType[];
before?: React.ReactNode;
after?: React.ReactNode;
autoExpandAll?: boolean;
};
export default function TWUIDocsAside({
DocsLinks,
after,
before,
autoExpandAll,
...props
}: Props) {
return (
<aside
{...props}
className={twMerge("py-10 hidden xl:flex", props.className)}
>
<Stack>
{before}
{DocsLinks.map((link, index) => (
<TWUIDocsLink
docLink={link}
key={index}
autoExpandAll={autoExpandAll}
/>
))}
{after}
</Stack>
</aside>
);
}
@@ -0,0 +1,121 @@
import React, {
AnchorHTMLAttributes,
ComponentProps,
DetailedHTMLProps,
} from "react";
import { DocsLinkType } from ".";
import Stack from "../../layout/Stack";
import { twMerge } from "tailwind-merge";
import Row from "../../layout/Row";
import Divider from "../../layout/Divider";
import { ChevronDown } from "lucide-react";
import Button from "../../layout/Button";
type Props = DetailedHTMLProps<
AnchorHTMLAttributes<HTMLAnchorElement>,
HTMLAnchorElement
> & {
docLink: DocsLinkType;
wrapperProps?: ComponentProps<typeof Stack>;
strict?: boolean;
childWrapperProps?: ComponentProps<typeof Stack>;
autoExpandAll?: boolean;
};
/**
* # TWUI Docs Left Aside Link
* @note use dataset attribute `data-strict` for strict matching
*
* @className `twui-docs-left-aside-link`
*/
export default function TWUIDocsLink({
docLink,
wrapperProps,
childWrapperProps,
strict,
autoExpandAll,
...props
}: Props) {
const [isActive, setIsActive] = React.useState(false);
const [expand, setExpand] = React.useState(autoExpandAll || false);
const linkRef = React.useRef<HTMLAnchorElement>(null);
React.useEffect(() => {
if (typeof window !== "undefined") {
const basePathMatch = window.location.pathname.includes(
docLink.href
);
const isStrictMatch = Boolean(
linkRef.current?.getAttribute("data-strict")
);
if (strict || isStrictMatch) {
setIsActive(window.location.pathname === docLink.href);
} else {
setIsActive(basePathMatch);
}
if (basePathMatch) {
setExpand(true);
}
}
}, []);
return (
<Stack
className={twMerge("gap-2 w-full", wrapperProps?.className)}
{...wrapperProps}
>
<Row className="flex-nowrap grow justify-between w-full">
<a
href={docLink.href}
{...props}
className={twMerge(
"twui-docs-left-aside-link whitespace-nowrap",
"grow",
isActive ? "active" : "",
props.className
)}
ref={linkRef}
>
{docLink.title}
</a>
{docLink.children?.[0] && (
<Button
variant="ghost"
color="gray"
className={twMerge(
"p-1 hover:opacity-100",
expand ? "rotate-180 opacity-30" : "opacity-70"
)}
onClick={() => setExpand(!expand)}
>
<ChevronDown className="text-slate-500" size={20} />
</Button>
)}
</Row>
{docLink.children && expand && (
<Row className="items-stretch gap-4 grow w-full flex-nowrap">
<Divider vertical className="h-auto" />
<Stack
className={twMerge(
"gap-2 w-full",
childWrapperProps?.className
)}
{...childWrapperProps}
>
{docLink.children.map((link, index) => (
<TWUIDocsLink
docLink={link}
key={index}
className="text-sm"
/>
))}
</Stack>
</Row>
)}
</Stack>
);
}
+76
View File
@@ -0,0 +1,76 @@
import {
ComponentProps,
DetailedHTMLProps,
HTMLAttributes,
PropsWithChildren,
} from "react";
import Stack from "../../layout/Stack";
import Container from "../../layout/Container";
import Row from "../../layout/Row";
import Divider from "../../layout/Divider";
import TWUIDocsAside from "./TWUIDocsAside";
import { twMerge } from "tailwind-merge";
type Props = PropsWithChildren & {
DocsLinks: DocsLinkType[];
docsAsideBefore?: React.ReactNode;
docsAsideAfter?: React.ReactNode;
wrapperProps?: ComponentProps<typeof Stack>;
docsContentProps?: ComponentProps<typeof Row>;
leftAsideProps?: DetailedHTMLProps<
HTMLAttributes<HTMLElement>,
HTMLElement
>;
autoExpandAll?: boolean;
};
export type DocsLinkType = {
title: string;
href: string;
children?: DocsLinkType[];
};
/**
* # TWUI Docs
* @className `twui-docs-content`
*/
export default function TWUIDocs({
children,
DocsLinks,
docsAsideAfter,
docsAsideBefore,
wrapperProps,
docsContentProps,
leftAsideProps,
autoExpandAll,
}: Props) {
return (
<Stack
center
{...wrapperProps}
className={twMerge("w-full px-4 sm:px-6", wrapperProps?.className)}
>
<Container>
<Row
{...docsContentProps}
className={twMerge(
"items-stretch gap-6 w-full flex-nowrap",
docsContentProps?.className
)}
>
<TWUIDocsAside
DocsLinks={DocsLinks}
after={docsAsideAfter}
before={docsAsideBefore}
autoExpandAll={autoExpandAll}
{...leftAsideProps}
/>
<Divider vertical className="h-auto hidden xl:flex" />
<div className="block twui-docs-content py-10 pl-0 xl:pl-6 grow">
{children}
</div>
</Row>
</Container>
</Stack>
);
}