Files
docs/packages/base-ui/src/utils/link-item.tsx
2026-03-19 19:00:54 +08:00

119 lines
2.3 KiB
TypeScript

'use client';
import type { ComponentProps, ReactNode } from 'react';
import { usePathname } from '@hanzo/docs-core/framework';
import { isActive } from './urls';
import Link from '@hanzo/docs-core/link';
interface Filterable {
/**
* Restrict where the item is displayed
*
* @defaultValue 'all'
*/
on?: 'menu' | 'nav' | 'all';
}
interface WithHref {
url: string;
/**
* When the item is marked as active
*
* @defaultValue 'url'
*/
active?: 'url' | 'nested-url' | 'none';
external?: boolean;
}
export interface MainItemType extends WithHref, Filterable {
type?: 'main';
icon?: ReactNode;
text: ReactNode;
description?: ReactNode;
}
export interface IconItemType extends WithHref, Filterable {
type: 'icon';
/**
* `aria-label` of icon button
*/
label?: string;
icon: ReactNode;
text: ReactNode;
/**
* @defaultValue true
*/
secondary?: boolean;
}
export interface ButtonItemType extends WithHref, Filterable {
type: 'button';
icon?: ReactNode;
text: ReactNode;
/**
* @defaultValue false
*/
secondary?: boolean;
}
export interface MenuItemType extends Partial<WithHref>, Filterable {
type: 'menu';
icon?: ReactNode;
text: ReactNode;
items: (
| (MainItemType & {
/**
* Options when displayed on navigation menu
*/
menu?: ComponentProps<'a'> & {
banner?: ReactNode;
};
})
| CustomItemType
)[];
/**
* @defaultValue false
*/
secondary?: boolean;
}
export interface CustomItemType extends Filterable {
type: 'custom';
/**
* @defaultValue false
*/
secondary?: boolean;
children: ReactNode;
}
export type LinkItemType =
| MainItemType
| IconItemType
| ButtonItemType
| MenuItemType
| CustomItemType;
export function LinkItem({
ref,
item,
...props
}: Omit<ComponentProps<'a'>, 'href'> & { item: LinkItemType & WithHref }) {
const active = useLinkItemActive(item);
return (
<Link ref={ref} href={item.url} external={item.external} {...props} data-active={active}>
{props.children}
</Link>
);
}
export function useLinkItemActive(link: LinkItemType) {
const pathname = usePathname();
if (link.type === 'custom' || !link.url) return false;
if (link.active === 'none') return false;
return isActive(link.url, pathname, link.active === 'nested-url');
}