Files
tailwind-ui-library/src/composites/docs/TWUIDocsLink.tsx
T
tbenandClaude Opus 5.5 cdeeb8201e Fix component bugs across hooks, elements, form, layout, editors and utils
- Hooks: per-instance timers in useReady; cleanup for useIntersectionObserver;
  useWebSocket no longer reconnects after unmount/refresh, drains the queue
  and dispatches non-JSON messages
- Modal/Popover/Dropdown: onClose only on close, Escape to close, no stray
  DOM props, hooks before early return, scrollbar-aware positioning,
  top-left/top-right dropdown positions
- Elements: Toast/Search/StarRating per-instance timers, Pagination labels
  and empty state, Breadcrumbs titles, LinkList dividers/props, segment-wise
  active-link matching (new utils/link-matches-path), Table object cells
- Form: Select with a default option is changeable, Input clear notifies
  controlled parents and copy works on textareas, Checkbox random label,
  NumberInputButtons runaway interval, upload prop/spread fixes
- Layout: Button disabled while loading, Stack gap and Span truncate via
  inline styles, Img fallbackImageSrc, className/JSDoc fixes
- Editors/MDX/AI: TinyMCE id/cleanup/stale handler/duplicate scripts,
  AceEditor debounce, markdown heights and frontmatter regex, last
  assistant message, AIPromptBlock prompt ref
- Utils: fetchApi headers/FormData/fetchOptions/no mutation, base64 file
  names and read errors, slugify underscore trim, serializeQuery zero values
- Tests for fetchApi, link matching, slugify and serializeQuery
- AGENTS.md updated for the package layout

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 10:45:46 +01:00

137 lines
4.4 KiB
TypeScript

import React, {
type AnchorHTMLAttributes,
type ComponentProps,
type DetailedHTMLProps,
} from "react";
import type { DocsLinkType } from ".";
import Stack from "../../layout/Stack";
import { twMerge } from "tailwind-merge";
import Row from "../../layout/Row";
import Divider from "../../layout/Divider";
import { ChevronDown, Circle } from "lucide-react";
import Button from "../../layout/Button";
import twuiLinkMatchesPath from "../../utils/link-matches-path";
type Props = DetailedHTMLProps<
AnchorHTMLAttributes<HTMLAnchorElement>,
HTMLAnchorElement
> & {
docLink: DocsLinkType;
wrapperProps?: ComponentProps<typeof Stack>;
strict?: boolean;
childWrapperProps?: ComponentProps<typeof Stack>;
autoExpandAll?: boolean;
child?: 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,
child,
...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 = twuiLinkMatchesPath({
href: docLink.href,
pathname: window.location.pathname,
});
const isStrictMatch = Boolean(
linkRef.current?.getAttribute("data-strict"),
);
if (strict || isStrictMatch) {
setIsActive(
twuiLinkMatchesPath({
href: docLink.href,
pathname: window.location.pathname,
strict: true,
}),
);
} else {
setIsActive(basePathMatch);
}
if (basePathMatch) {
setExpand(true);
}
}
}, []);
return (
<Stack
{...wrapperProps}
className={twMerge("gap-2 w-full", wrapperProps?.className)}
>
<Row className="flex-nowrap grow justify-between w-full">
{child && <Circle size={6} />}
<a
href={docLink.href}
title={docLink.title}
{...props}
className={twMerge(
"twui-docs-left-aside-link whitespace-nowrap",
"grow overflow-hidden overflow-ellipsis",
isActive ? "active" : "",
props.className,
)}
ref={linkRef}
data-strict={strict || docLink.strict}
>
{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)}
title="Docs Aside Links Dropdown Button"
>
<ChevronDown className="text-slate-500" size={20} />
</Button>
)}
</Row>
{docLink.children && expand && (
<Row className="items-stretch gap-4 grow w-full flex-nowrap">
<Stack
{...childWrapperProps}
className={twMerge(
"gap-2 w-full pl-3",
childWrapperProps?.className,
)}
>
{docLink.children.map((link, index) => (
<TWUIDocsLink
key={index}
docLink={link}
className="opacity-70"
autoExpandAll={autoExpandAll}
child
/>
))}
</Stack>
</Row>
)}
</Stack>
);
}