1 | import React, { memo, forwardRef, useId, type ReactNode, type CSSProperties } from "react";
|
2 | import { symToStr } from "tsafe/symToStr";
|
3 | import { assert } from "tsafe/assert";
|
4 | import type { Equals } from "tsafe";
|
5 | import { getLink } from "./link";
|
6 | import type { RegisteredLinkProps } from "./link";
|
7 | import { createComponentI18nApi } from "./i18n";
|
8 | import { fr } from "./fr";
|
9 | import { cx } from "./tools/cx";
|
10 | import { useAnalyticsId } from "./tools/useAnalyticsId";
|
11 |
|
12 | export 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 |
|
26 | export 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 |
|
103 | Breadcrumb.displayName = symToStr({ Breadcrumb });
|
104 |
|
105 | const { useTranslation, addBreadcrumbTranslations } = createComponentI18nApi({
|
106 | "componentName": symToStr({ Breadcrumb }),
|
107 | "frMessages": {
|
108 |
|
109 | "show breadcrumb": "Voir le fil d’Ariane",
|
110 | "navigation label": "vous êtes ici",
|
111 | "home": "Accueil"
|
112 |
|
113 | }
|
114 | });
|
115 |
|
116 | addBreadcrumbTranslations({
|
117 | "lang": "en",
|
118 | "messages": {
|
119 | "show breadcrumb": "Show navigation",
|
120 | "navigation label": "you are here",
|
121 | "home": "Home"
|
122 | }
|
123 | });
|
124 |
|
125 | export { addBreadcrumbTranslations };
|
126 |
|
127 | export default Breadcrumb;
|
128 |
|
\ | No newline at end of file |