import type { ReactNode } from "react";
import type { GestureResponderEvent } from "react-native";
import { tv } from "../../core/tv";
import { LinkText, type LinkTextProps } from "../actions/LinkText";
import { linkTextIconSize } from "../actions/linkTextVariants";
import { Icon, type SVGIconElement } from "../primitives/Icon";
import { Text, type TextProps } from "../primitives/Text";
import { View } from "../primitives/View";
import { useBreadcrumbItemContext } from "./BreadcrumbsContext";
// An ancestor is a LinkText, so a crumb is styled like every other link in the
// system; the row around it only holds the 44px tap target. The current page is
// that same row without the link — bold and sharp, since it labels what is
// already on screen.
const breadcrumbItemVariants = tv({
slots: {
// LinkText is already the row; it only needs the tap target around it.
link: "min-h-control-md px-xxs",
page: "flex-row items-center gap-xxs min-h-control-md px-xxs",
icon: "text-sharp",
label: "select-none font-body-bold text-base text-sharp",
},
});
// react-native's Text types have no `aria-current`, but react-native-web
// forwards it — it marks the page being viewed — while native ignores it.
interface CurrentCrumbTextProps extends TextProps {
"aria-current": "page";
}
function CurrentCrumbText(props: CurrentCrumbTextProps): ReactNode {
return ;
}
export interface BreadcrumbItemProps {
/**
* Destination of the crumb. Renders a real `` on web (native ignores
* it); expo Router's `` injects it, so it does not have to be
* written twice. The last crumb is the current page — give it its own href
* anyway, it is rendered as plain text.
*/
href?: string;
label: string;
/** Leading icon, typically on the root crumb. */
icon?: SVGIconElement;
disabled?: boolean;
/**
* Handles the press instead of the trail's `onNavigate` — this is what
* `` injects. A handler that navigates on web must call
* `event.preventDefault()`, as routers do.
*/
onPress?: LinkTextProps["onPress"];
}
/**
* One crumb of a `Breadcrumbs` trail: a link to an ancestor. The last one is the
* current page instead — plain text carrying `aria-current="page"`.
*/
export function BreadcrumbItem({
href,
label,
icon,
disabled,
onPress,
}: BreadcrumbItemProps): ReactNode {
const {
current,
disabled: breadcrumbsDisabled,
onNavigate,
} = useBreadcrumbItemContext();
const isDisabled = disabled === true || breadcrumbsDisabled;
const styles = breadcrumbItemVariants();
if (current) {
return (
{icon ? (
) : null}
{label}
);
}
// Routing is the app's job, through the trail's onNavigate or an item
// onPress, so the anchor must not navigate on its own once one is set.
const navigate =
onNavigate === undefined || href === undefined
? undefined
: (event: GestureResponderEvent) => {
event.preventDefault();
onNavigate(href);
};
return (
);
}