UNPKG

4.4 kBTypeScriptView Raw
1import React, { memo, forwardRef, useId, type ReactNode, type CSSProperties } from "react";
2import { symToStr } from "tsafe/symToStr";
3import { assert } from "tsafe/assert";
4import type { Equals } from "tsafe";
5import { getLink } from "./link";
6import type { RegisteredLinkProps } from "./link";
7import { createComponentI18nApi } from "./i18n";
8import { fr } from "./fr";
9import { cx } from "./tools/cx";
10import { useAnalyticsId } from "./tools/useAnalyticsId";
11
12export type BreadcrumbProps = {
13 id?: string;
14 className?: string;
15 homeLinkProps?: RegisteredLinkProps;
16 segments: {
17 label: ReactNode;
18 linkProps: RegisteredLinkProps;
19 }[];
20 currentPageLabel: ReactNode;
21 classes?: Partial<Record<"root" | "button" | "collapse" | "list" | "link" | "text", string>>;
22 style?: CSSProperties;
23};
24
25/** @see <https://components.react-dsfr.codegouv.studio/?path=/docs/components-breadcrumb> */
26export const Breadcrumb = memo(
27 forwardRef<HTMLDivElement, BreadcrumbProps>((props, ref) => {
28 const {
29 id: props_id,
30 className,
31 homeLinkProps,
32 segments,
33 currentPageLabel,
34 classes = {},
35 style,
36 ...rest
37 } = props;
38
39 assert<Equals<keyof typeof rest, never>>();
40
41 const id = useAnalyticsId({
42 "defaultIdPrefix": "fr-breadcrumb",
43 "explicitlyProvidedId": props_id
44 });
45
46 const { t } = useTranslation();
47
48 const { Link } = getLink();
49 const breadcrumbId = `breadcrumb-${useId()}`;
50
51 return (
52 <nav
53 id={id}
54 ref={ref}
55 role="navigation"
56 className={cx(fr.cx("fr-breadcrumb"), classes.root, className)}
57 style={style}
58 aria-label={`${t("navigation label")} :`}
59 {...rest}
60 >
61 <button
62 className={cx(fr.cx("fr-breadcrumb__button"), classes.button)}
63 aria-expanded="false"
64 aria-controls={breadcrumbId}
65 >
66 {t("show breadcrumb")}
67 </button>
68 <div className={cx(fr.cx("fr-collapse"), classes.collapse)} id={breadcrumbId}>
69 <ol className={cx(fr.cx("fr-breadcrumb__list"), classes.list)}>
70 <>
71 {[
72 ...(homeLinkProps === undefined
73 ? []
74 : [{ "linkProps": homeLinkProps, "label": t("home") }]),
75 ...segments
76 ].map(({ linkProps, label }, i) => (
77 <li key={i}>
78 <Link
79 {...linkProps}
80 className={cx(
81 fr.cx("fr-breadcrumb__link"),
82 classes.link,
83 linkProps.className
84 )}
85 >
86 {label}
87 </Link>
88 </li>
89 ))}
90 <li>
91 <a className={fr.cx("fr-breadcrumb__link")} aria-current="page">
92 {currentPageLabel}
93 </a>
94 </li>
95 </>
96 </ol>
97 </div>
98 </nav>
99 );
100 })
101);
102
103Breadcrumb.displayName = symToStr({ Breadcrumb });
104
105const { useTranslation, addBreadcrumbTranslations } = createComponentI18nApi({
106 "componentName": symToStr({ Breadcrumb }),
107 "frMessages": {
108 /* spell-checker: disable */
109 "show breadcrumb": "Voir le fil d’Ariane",
110 "navigation label": "vous êtes ici",
111 "home": "Accueil"
112 /* spell-checker: enable */
113 }
114});
115
116addBreadcrumbTranslations({
117 "lang": "en",
118 "messages": {
119 "show breadcrumb": "Show navigation",
120 "navigation label": "you are here",
121 "home": "Home"
122 }
123});
124
125export { addBreadcrumbTranslations };
126
127export default Breadcrumb;
128
\No newline at end of file