11import React , { memo , forwardRef , useId } from "react" ;
22import { symToStr } from "tsafe/symToStr" ;
3+ import { assert } from "tsafe/assert" ;
4+ import type { Equals } from "tsafe" ;
5+
36import { RegisteredLinkProps , useLink } from "./lib/routing" ;
7+ import { createComponentI18nApi } from "./lib/i18n" ;
48import { fr } from "./lib" ;
59import { cx } from "./lib/tools/cx" ;
610
7- // We make users import dsfr.css, so we don't need to import the scoped CSS
8- // but in the future if we have a complete component coverage it
9- // we could stop requiring users to import the hole CSS and only import on a
10- // per component basis.
1111import "./dsfr/component/breadcrumb/breadcrumb.css" ;
1212
1313export type BreadcrumbProps = {
1414 className ?: string ;
1515 links : BreadcrumbProps . Link [ ] ;
16+ classes ?: Partial < Record < "root" | "button" | "collapse" | "list" | "link" | "text" , string > > ;
1617} ;
1718
1819export namespace BreadcrumbProps {
@@ -23,7 +24,7 @@ export namespace BreadcrumbProps {
2324 } ;
2425}
2526
26- //Longueur et lisibilité : Afin qu’il reste lisible, évitez que le fil d’Ariane soit trop long et passe sur plusieurs lignes.
27+ // Note DSFR: Longueur et lisibilité : Afin qu’il reste lisible, évitez que le fil d’Ariane soit trop long et passe sur plusieurs lignes.
2728// Si les titres de page de votre site sont longs, nous conseillons de n’afficher que les 4 premiers mots du nom de la page courante et d’indiquer que l’élément est tronqué par l’affichage de “…”
2829const trimText = ( label : string ) => {
2930 if ( label && label . split ( " " ) . length > 4 ) {
@@ -35,34 +36,39 @@ const trimText = (label: string) => {
3536/** @see <https://react-dsfr-components.etalab.studio/?path=/docs/components-breadcrumb> */
3637export const Breadcrumb = memo (
3738 forwardRef < HTMLDivElement , BreadcrumbProps > ( ( props , ref ) => {
38- const { links, className, ...rest } = props ;
39+ const { links, className, classes = { } , ...rest } = props ;
40+
41+ assert < Equals < keyof typeof rest , never > > ( ) ;
42+
43+ const { t } = useTranslation ( ) ;
3944
4045 const { Link } = useLink ( ) ;
4146 const breadcrumbId = useId ( ) ;
4247 return (
4348 < nav
4449 ref = { ref }
4550 role = "navigation"
46- className = { cx ( fr . cx ( "fr-breadcrumb" ) , className ) }
47- aria-label = "vous êtes ici :"
51+ className = { cx ( fr . cx ( "fr-breadcrumb" ) , classes . root , className ) }
52+ aria-label = { ` ${ t ( "navigation label" ) } :` }
4853 { ...rest }
4954 >
5055 < button
51- className = "fr-breadcrumb__button"
56+ className = { cx ( fr . cx ( "fr-breadcrumb__button" ) , classes . button ) }
5257 aria-expanded = "false"
5358 aria-controls = { breadcrumbId }
5459 >
55- Voir le fil d’Ariane
60+ { t ( "show breadcrumb" ) }
5661 </ button >
57- < div className = "fr-collapse" id = { breadcrumbId } >
58- < ol className = "fr-breadcrumb__list" >
62+ < div className = { cx ( fr . cx ( "fr-collapse" ) , classes . collapse ) } id = { breadcrumbId } >
63+ < ol className = { cx ( fr . cx ( "fr-breadcrumb__list" ) , classes . list ) } >
5964 < >
6065 { links . map ( link => (
6166 < li key = { link . linkProps . href } >
6267 < Link
6368 { ...link . linkProps }
6469 className = { cx (
6570 fr . cx ( "fr-breadcrumb__link" ) ,
71+ classes . link ,
6672 link . linkProps . className
6773 ) }
6874 aria-current = { link . isActive ? "page" : undefined }
@@ -81,4 +87,22 @@ export const Breadcrumb = memo(
8187
8288Breadcrumb . displayName = symToStr ( { Breadcrumb } ) ;
8389
90+ const { useTranslation, addBreadcrumbTranslations } = createComponentI18nApi ( {
91+ "componentName" : symToStr ( { Breadcrumb } ) ,
92+ "frMessages" : {
93+ "show breadcrumb" : "Voir le fil d’Ariane" ,
94+ "navigation label" : "vous êtes ici"
95+ }
96+ } ) ;
97+
98+ addBreadcrumbTranslations ( {
99+ "lang" : "en" ,
100+ "messages" : {
101+ "show breadcrumb" : "Show navigation" ,
102+ "navigation label" : "you are here"
103+ }
104+ } ) ;
105+
106+ export { addBreadcrumbTranslations } ;
107+
84108export default Breadcrumb ;
0 commit comments