1- import React , { memo , forwardRef , type ReactNode , type CSSProperties } from "react" ;
2- import { symToStr } from "tsafe/symToStr" ;
3- import { assert } from "tsafe/assert" ;
1+ import type { ComponentProps , CSSProperties , ReactNode } from "react" ;
2+ import React , { forwardRef , memo } from "react" ;
43import type { Equals } from "tsafe" ;
5- import type { RegisteredLinkProps } from "./link" ;
6- import { getLink } from "./link" ;
4+ import { assert } from "tsafe/assert" ;
5+ import { symToStr } from "tsafe/symToStr" ;
6+
77import { fr } from "./fr" ;
8+ import { type RegisteredLinkProps , getLink } from "./link" ;
89import { cx } from "./tools/cx" ;
910import { useAnalyticsId } from "./tools/useAnalyticsId" ;
1011
@@ -13,25 +14,64 @@ export type TileProps = {
1314 id ?: string ;
1415 className ?: string ;
1516 title : ReactNode ;
16- linkProps : RegisteredLinkProps ;
17+ titleAs ?: `h${2 | 3 | 4 | 5 | 6 } `;
18+ linkProps ?: RegisteredLinkProps ;
19+ buttonProps ?: ComponentProps < "button" > ;
20+ downloadButton ?: boolean ;
1721 desc ?: ReactNode ;
22+ detail ?: ReactNode ;
23+ start ?: ReactNode ;
1824 imageUrl ?: string ;
1925 imageAlt ?: string ;
2026 imageWidth ?: string | number ;
2127 imageHeight ?: string | number ;
28+ imageSvg ?: boolean ;
2229 grey ?: boolean ;
23-
2430 /** make the whole tile clickable */
25- enlargeLink ?: boolean ;
31+ enlargeLinkOrButton ?: boolean ;
2632 classes ?: Partial <
27- Record < "root" | "title" | "link" | "body" | "desc" | "img" | "imgTag" , string >
33+ Record <
34+ | "root"
35+ | "content"
36+ | "title"
37+ | "header"
38+ | "link"
39+ | "button"
40+ | "body"
41+ | "desc"
42+ | "detail"
43+ | "start"
44+ | "img"
45+ | "imgTag" ,
46+ string
47+ >
2848 > ;
29- /** Default false */
30- horizontal ?: boolean ;
49+ orientation ?: "horizontal" | "vertical" ;
50+ small ?: boolean ;
51+ noIcon ?: boolean ;
52+ noBorder ?: boolean ;
53+ noBackground ?: boolean ;
54+ disabled ?: boolean ;
3155 style ?: CSSProperties ;
32- } ;
56+ } & ( TileNextProps . WithLink | TileNextProps . WithButton | TileNextProps . Unclickable ) ;
3357
34- export namespace TileProps { }
58+ export namespace TileNextProps {
59+ export type Unclickable = {
60+ linkProps ?: never ;
61+ buttonProps ?: never ;
62+ enlargeLinkOrButton ?: never ;
63+ } ;
64+
65+ export type WithLink = {
66+ linkProps : RegisteredLinkProps ;
67+ buttonProps ?: never ;
68+ } ;
69+
70+ export type WithButton = {
71+ linkProps ?: never ;
72+ buttonProps : ComponentProps < "button" > ;
73+ } ;
74+ }
3575
3676/** @see <https://components.react-dsfr.codegouv.studio/?path=/docs/components-tile> */
3777export const Tile = memo (
@@ -40,16 +80,27 @@ export const Tile = memo(
4080 id : id_props ,
4181 className,
4282 title,
83+ titleAs : HtmlTitleTag = "h3" ,
4384 linkProps,
85+ buttonProps,
86+ downloadButton,
4487 desc,
88+ detail,
89+ start,
4590 imageUrl,
4691 imageAlt,
4792 imageWidth,
4893 imageHeight,
49- horizontal = false ,
94+ imageSvg = true ,
95+ orientation = "vertical" ,
96+ small = false ,
97+ noBorder = false ,
98+ noIcon = false ,
99+ noBackground = false ,
50100 grey = false ,
51101 classes = { } ,
52- enlargeLink = true ,
102+ enlargeLinkOrButton = true ,
103+ disabled = false ,
53104 style,
54105 ...rest
55106 } = props ;
@@ -69,9 +120,19 @@ export const Tile = memo(
69120 className = { cx (
70121 fr . cx (
71122 "fr-tile" ,
72- enlargeLink && "fr-enlarge-link" ,
73- horizontal && "fr-tile--horizontal" ,
74- grey && "fr-tile--grey"
123+ enlargeLinkOrButton &&
124+ ( linkProps
125+ ? "fr-enlarge-link"
126+ : buttonProps
127+ ? "fr-enlarge-button"
128+ : null ) ,
129+ orientation && `fr-tile--${ orientation } ` ,
130+ noIcon && "fr-tile--no-icon" ,
131+ noBorder && "fr-tile--no-border" ,
132+ noBackground && "fr-tile--no-background" ,
133+ grey && "fr-tile--grey" ,
134+ small && "fr-tile--sm" ,
135+ buttonProps && downloadButton && "fr-tile--download"
75136 ) ,
76137 classes . root ,
77138 className
@@ -81,30 +142,90 @@ export const Tile = memo(
81142 { ...rest }
82143 >
83144 < div className = { cx ( fr . cx ( "fr-tile__body" ) , classes . body ) } >
84- < h3 className = { cx ( fr . cx ( "fr-tile__title" ) , classes . title ) } >
85- < Link { ...linkProps } className = { cx ( classes . link , linkProps . className ) } >
86- { title }
87- </ Link >
88- </ h3 >
89- < p className = { cx ( fr . cx ( "fr-tile__desc" ) , classes . desc ) } > { desc } </ p >
145+ < div className = { cx ( fr . cx ( "fr-tile__content" ) , classes . content ) } >
146+ < HtmlTitleTag className = { cx ( fr . cx ( "fr-tile__title" ) , classes . title ) } >
147+ { linkProps !== undefined ? (
148+ < Link
149+ { ...linkProps }
150+ href = { disabled ? undefined : linkProps . href }
151+ className = { cx ( classes . link , linkProps . className ) }
152+ aria-disabled = { disabled }
153+ >
154+ { title }
155+ </ Link >
156+ ) : buttonProps !== undefined ? (
157+ < button
158+ { ...buttonProps }
159+ className = { cx ( classes . button , buttonProps . className ) }
160+ disabled = { disabled }
161+ >
162+ { title }
163+ </ button >
164+ ) : (
165+ title
166+ ) }
167+ </ HtmlTitleTag >
168+
169+ { desc !== undefined && (
170+ < p className = { cx ( fr . cx ( "fr-tile__desc" ) , classes . desc ) } > { desc } </ p >
171+ ) }
172+ { detail !== undefined && (
173+ < p className = { cx ( fr . cx ( "fr-tile__detail" ) , classes . detail ) } > { detail } </ p >
174+ ) }
175+ { start !== undefined && (
176+ < div className = { cx ( fr . cx ( "fr-tile__start" ) , classes . start ) } >
177+ { start }
178+ </ div >
179+ ) }
180+ </ div >
90181 </ div >
91- { ( imageUrl !== undefined && imageUrl . length && (
92- < div className = { cx ( fr . cx ( "fr-tile__img" ) , classes . img ) } >
93- < img
94- className = { cx ( fr . cx ( "fr-responsive-img" ) , classes . imgTag ) }
95- src = { imageUrl }
96- alt = { imageAlt }
97- width = { imageWidth }
98- height = { imageHeight }
99- />
182+
183+ { imageUrl !== undefined && imageUrl . length > 0 && (
184+ < div className = { cx ( fr . cx ( "fr-tile__header" ) , classes . header ) } >
185+ { imageSvg ? (
186+ < div className = { cx ( fr . cx ( "fr-tile__pictogram" ) , classes . img ) } >
187+ < svg
188+ aria-hidden = { true }
189+ className = { fr . cx ( "fr-artwork" ) }
190+ viewBox = "0 0 80 80"
191+ width = "80px"
192+ height = "80px"
193+ xmlns = "http://www.w3.org/2000/svg"
194+ xmlnsXlink = "http://www.w3.org/1999/xlink"
195+ >
196+ { (
197+ [
198+ "artwork-decorative" ,
199+ "artwork-minor" ,
200+ "artwork-major"
201+ ] as const
202+ ) . map ( label => (
203+ < use
204+ key = { label }
205+ className = { fr . cx ( `fr-${ label } ` ) }
206+ xlinkHref = { `${ imageUrl } #${ label } ` }
207+ />
208+ ) ) }
209+ </ svg >
210+ </ div >
211+ ) : (
212+ < div className = { cx ( fr . cx ( "fr-tile__img" ) , classes . img ) } >
213+ < img
214+ className = { cx ( fr . cx ( "fr-responsive-img" ) , classes . imgTag ) }
215+ src = { imageUrl }
216+ alt = { imageAlt }
217+ width = { imageWidth }
218+ height = { imageHeight }
219+ />
220+ </ div >
221+ ) }
100222 </ div >
101- ) ) ||
102- null }
223+ ) }
103224 </ div >
104225 ) ;
105226 } )
106227) ;
107228
108- Tile . displayName = symToStr ( { Tile } ) ;
229+ Tile . displayName = symToStr ( { TileNext : Tile } ) ;
109230
110231export default Tile ;
0 commit comments