11"use client" ;
22
3- import React , { memo , useState , useEffect , useRef } from "react" ;
4- import type { ReactNode , ForwardedRef } from "react" ;
3+ import React , { memo , forwardRef , useState , useEffect , useRef } from "react" ;
4+ import type { ReactNode } from "react" ;
55import type { FrClassName } from "./fr/generatedFromCss/classNames" ;
66import { symToStr } from "tsafe/symToStr" ;
77import { fr } from "./fr" ;
@@ -16,7 +16,6 @@ export type AlertProps = {
1616 severity : AlertProps . Severity ;
1717 /** Default h3 */
1818 as ?: `h${2 | 3 | 4 | 5 | 6 } `;
19- ref ?: ForwardedRef < HTMLDivElement > ;
2019 classes ?: Partial < Record < "root" | "title" | "description" | "close" , string > > ;
2120} & ( AlertProps . DefaultSize | AlertProps . Small ) &
2221 ( AlertProps . NonClosable | AlertProps . Closable ) ;
@@ -68,102 +67,104 @@ export namespace AlertProps {
6867}
6968
7069/** @see <https://react-dsfr-components.etalab.studio/?path=/docs/components-alert> */
71- export const Alert = memo ( ( props : AlertProps ) => {
72- const {
73- className,
74- severity,
75- as : HtmlTitleTag = "h3" ,
76- classes = { } ,
77- ref,
78- small : isSmall ,
79- title,
80- description,
81- closable : isClosable = false ,
82- isClosed : props_isClosed ,
83- onClose,
84- ...rest
85- } = props ;
86-
87- assert < Equals < keyof typeof rest , never > > ( ) ;
88-
89- const [ isClosed , setIsClosed ] = useState ( props_isClosed ?? false ) ;
90-
91- const [ buttonElement , setButtonElement ] = useState < HTMLButtonElement | null > ( null ) ;
92-
93- const refShouldButtonGetFocus = useRef ( false ) ;
94- const refShouldSetRole = useRef ( false ) ;
95-
96- useEffect ( ( ) => {
97- if ( props_isClosed === undefined ) {
98- return ;
99- }
100- setIsClosed ( isClosed => {
101- if ( isClosed && ! props_isClosed ) {
102- refShouldButtonGetFocus . current = true ;
103- refShouldSetRole . current = true ;
70+ export const Alert = memo (
71+ forwardRef < HTMLDivElement , AlertProps > ( ( props , ref ) => {
72+ const {
73+ className,
74+ severity,
75+ as : HtmlTitleTag = "h3" ,
76+ classes = { } ,
77+ small : isSmall ,
78+ title,
79+ description,
80+ closable : isClosable = false ,
81+ isClosed : props_isClosed ,
82+ onClose,
83+ ...rest
84+ } = props ;
85+
86+ assert < Equals < keyof typeof rest , never > > ( ) ;
87+
88+ const [ isClosed , setIsClosed ] = useState ( props_isClosed ?? false ) ;
89+
90+ const [ buttonElement , setButtonElement ] = useState < HTMLButtonElement | null > ( null ) ;
91+
92+ const refShouldButtonGetFocus = useRef ( false ) ;
93+ const refShouldSetRole = useRef ( false ) ;
94+
95+ useEffect ( ( ) => {
96+ if ( props_isClosed === undefined ) {
97+ return ;
98+ }
99+ setIsClosed ( isClosed => {
100+ if ( isClosed && ! props_isClosed ) {
101+ refShouldButtonGetFocus . current = true ;
102+ refShouldSetRole . current = true ;
103+ }
104+
105+ return props_isClosed ;
106+ } ) ;
107+ } , [ props_isClosed ] ) ;
108+
109+ useEffect ( ( ) => {
110+ if ( ! refShouldButtonGetFocus . current ) {
111+ return ;
104112 }
105113
106- return props_isClosed ;
107- } ) ;
108- } , [ props_isClosed ] ) ;
114+ if ( buttonElement === null ) {
115+ //NOTE: This should not be reachable
116+ return ;
117+ }
109118
110- useEffect ( ( ) => {
111- if ( ! refShouldButtonGetFocus . current ) {
112- return ;
113- }
119+ refShouldButtonGetFocus . current = false ;
120+ buttonElement . focus ( ) ;
121+ } , [ buttonElement ] ) ;
122+
123+ const onCloseButtonClick = useConstCallback ( ( ) => {
124+ if ( props_isClosed === undefined ) {
125+ //Uncontrolled
126+ setIsClosed ( true ) ;
127+ onClose ?.( ) ;
128+ } else {
129+ //Controlled
130+ onClose ( ) ;
131+ }
132+ } ) ;
114133
115- if ( buttonElement === null ) {
116- //NOTE: This should not be reachable
117- return ;
118- }
134+ const { t } = useTranslation ( ) ;
119135
120- refShouldButtonGetFocus . current = false ;
121- buttonElement . focus ( ) ;
122- } , [ buttonElement ] ) ;
123-
124- const onCloseButtonClick = useConstCallback ( ( ) => {
125- if ( props_isClosed === undefined ) {
126- //Uncontrolled
127- setIsClosed ( true ) ;
128- onClose ?.( ) ;
129- } else {
130- //Controlled
131- onClose ( ) ;
136+ if ( isClosed ) {
137+ return null ;
132138 }
133- } ) ;
134-
135- const { t } = useTranslation ( ) ;
136139
137- if ( isClosed ) {
138- return null ;
139- }
140-
141- return (
142- < div
143- className = { cx (
144- fr . cx ( "fr-alert" , `fr-alert--${ severity } ` , { "fr-alert--sm" : isSmall } ) ,
145- classes . root ,
146- className
147- ) }
148- { ...( refShouldSetRole . current && { "role" : "alert" } ) }
149- ref = { ref }
150- >
151- < HtmlTitleTag className = { cx ( fr . cx ( "fr-alert__title" ) , classes . title ) } >
152- { title }
153- </ HtmlTitleTag >
154- < p className = { classes . description } > { description } </ p >
155- { isClosable && (
156- < button
157- ref = { setButtonElement }
158- className = { cx ( fr . cx ( "fr-link--close" , "fr-link" ) , classes . close ) }
159- onClick = { onCloseButtonClick }
160- >
161- { t ( "hide message" ) }
162- </ button >
163- ) }
164- </ div >
165- ) ;
166- } ) ;
140+ return (
141+ < div
142+ className = { cx (
143+ fr . cx ( "fr-alert" , `fr-alert--${ severity } ` , { "fr-alert--sm" : isSmall } ) ,
144+ classes . root ,
145+ className
146+ ) }
147+ { ...( refShouldSetRole . current && { "role" : "alert" } ) }
148+ ref = { ref }
149+ { ...rest }
150+ >
151+ < HtmlTitleTag className = { cx ( fr . cx ( "fr-alert__title" ) , classes . title ) } >
152+ { title }
153+ </ HtmlTitleTag >
154+ < p className = { classes . description } > { description } </ p >
155+ { isClosable && (
156+ < button
157+ ref = { setButtonElement }
158+ className = { cx ( fr . cx ( "fr-link--close" , "fr-link" ) , classes . close ) }
159+ onClick = { onCloseButtonClick }
160+ >
161+ { t ( "hide message" ) }
162+ </ button >
163+ ) }
164+ </ div >
165+ ) ;
166+ } )
167+ ) ;
167168
168169Alert . displayName = symToStr ( { Alert } ) ;
169170
0 commit comments