Skip to content

Commit 93a1efa

Browse files
committed
add forwardRef in Modal component
1 parent 01f6d22 commit 93a1efa

File tree

1 file changed

+124
-117
lines changed

1 file changed

+124
-117
lines changed

src/Modal.tsx

Lines changed: 124 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { memo, ForwardedRef } from "react";
1+
import React, { memo, forwardRef } from "react";
22
import { fr } from "./fr";
33
import { cx } from "./tools/cx";
44
import type { ReactNode } from "react";
@@ -19,7 +19,6 @@ export type ModalProps = {
1919
/** Default: true */
2020
concealingBackdrop?: boolean;
2121
topAnchor?: boolean;
22-
ref?: ForwardedRef<HTMLDialogElement>;
2322
iconId?: FrIconClassName | RiIconClassName;
2423
buttons?:
2524
| [ModalProps.ActionAreaButtonProps, ...ModalProps.ActionAreaButtonProps[]]
@@ -33,126 +32,134 @@ export namespace ModalProps {
3332
};
3433
}
3534

36-
const Modal = memo((props: ModalProps & { id: string }) => {
37-
const {
38-
className,
39-
id,
40-
title,
41-
children,
42-
concealingBackdrop = true,
43-
topAnchor = false,
44-
ref,
45-
iconId,
46-
buttons: buttons_props,
47-
size = "medium",
48-
...rest
49-
} = props;
50-
51-
assert<Equals<keyof typeof rest, never>>();
52-
53-
const buttons =
54-
buttons_props === undefined
55-
? undefined
56-
: buttons_props instanceof Array
57-
? buttons_props
58-
: [buttons_props];
59-
60-
const { t } = useTranslation();
61-
62-
return (
63-
<dialog
64-
aria-labelledby="fr-modal-title-modal-1"
65-
role="dialog"
66-
id={id}
67-
className={cx(fr.cx("fr-modal", topAnchor && "fr-modal--top"), className)}
68-
ref={ref}
69-
data-fr-concealing-backdrop={concealingBackdrop}
70-
>
71-
<div className={fr.cx("fr-container", "fr-container--fluid", "fr-container-md")}>
72-
<div className={fr.cx("fr-grid-row", "fr-grid-row--center")}>
73-
<div
74-
className={(() => {
75-
switch (size) {
76-
case "large":
77-
return fr.cx("fr-col-12", "fr-col-md-10", "fr-col-lg-8");
78-
case "small":
79-
return fr.cx("fr-col-12", "fr-col-md-6", "fr-col-lg-4");
80-
case "medium":
81-
return fr.cx("fr-col-12", "fr-col-md-8", "fr-col-lg-6");
82-
}
83-
})()}
84-
>
85-
<div className={fr.cx("fr-modal__body")}>
86-
<div className={fr.cx("fr-modal__header")}>
87-
<button
88-
className={fr.cx("fr-link--close", "fr-link")}
89-
title={t("close")}
90-
aria-controls={id}
91-
>
92-
{t("close")}
93-
</button>
94-
</div>
95-
<div className={fr.cx("fr-modal__content")}>
96-
<h1
97-
id="fr-modal-title-modal-1"
98-
className={fr.cx("fr-modal__title")}
99-
>
100-
{iconId !== undefined && (
101-
<span className={fr.cx(iconId, "fr-fi--lg")} />
102-
)}
103-
{title}
104-
</h1>
105-
{children}
106-
</div>
107-
{buttons !== undefined && (
108-
<div className="fr-modal__footer">
109-
<ul
110-
className={fr.cx(
111-
"fr-btns-group",
112-
"fr-btns-group--right",
113-
"fr-btns-group--inline-reverse",
114-
"fr-btns-group--inline-lg",
115-
"fr-btns-group--icon-left"
116-
)}
35+
const Modal = memo(
36+
forwardRef<HTMLDialogElement, ModalProps & { id: string }>((props, ref) => {
37+
const {
38+
className,
39+
id,
40+
title,
41+
children,
42+
concealingBackdrop = true,
43+
topAnchor = false,
44+
iconId,
45+
buttons: buttons_props,
46+
size = "medium",
47+
...rest
48+
} = props;
49+
50+
assert<Equals<keyof typeof rest, never>>();
51+
52+
const buttons =
53+
buttons_props === undefined
54+
? undefined
55+
: buttons_props instanceof Array
56+
? buttons_props
57+
: [buttons_props];
58+
59+
const { t } = useTranslation();
60+
61+
return (
62+
<dialog
63+
aria-labelledby="fr-modal-title-modal-1"
64+
role="dialog"
65+
id={id}
66+
className={cx(fr.cx("fr-modal", topAnchor && "fr-modal--top"), className)}
67+
ref={ref}
68+
data-fr-concealing-backdrop={concealingBackdrop}
69+
>
70+
<div className={fr.cx("fr-container", "fr-container--fluid", "fr-container-md")}>
71+
<div className={fr.cx("fr-grid-row", "fr-grid-row--center")}>
72+
<div
73+
className={(() => {
74+
switch (size) {
75+
case "large":
76+
return fr.cx("fr-col-12", "fr-col-md-10", "fr-col-lg-8");
77+
case "small":
78+
return fr.cx("fr-col-12", "fr-col-md-6", "fr-col-lg-4");
79+
case "medium":
80+
return fr.cx("fr-col-12", "fr-col-md-8", "fr-col-lg-6");
81+
}
82+
})()}
83+
>
84+
<div className={fr.cx("fr-modal__body")}>
85+
<div className={fr.cx("fr-modal__header")}>
86+
<button
87+
className={fr.cx("fr-link--close", "fr-link")}
88+
title={t("close")}
89+
aria-controls={id}
11790
>
118-
{[...buttons]
119-
.reverse()
120-
.map(({ doClosesModal = true, ...buttonProps }, i) => (
121-
<li key={i}>
122-
<Button
123-
{...buttonProps}
124-
priority={
125-
buttonProps.priority ??
126-
(i === 0 ? "primary" : "secondary")
127-
}
128-
{...(!doClosesModal
129-
? {}
130-
: "linkProps" in buttonProps
131-
? {
132-
"linkProps": {
133-
...buttonProps.linkProps,
134-
"aria-controls": id
135-
} as any
136-
}
137-
: {
138-
"nativeButtonProps": {
139-
...buttonProps.nativeButtonProps,
140-
"aria-controls": id
141-
} as any
142-
})}
143-
/>
144-
</li>
145-
))}
146-
</ul>
91+
{t("close")}
92+
</button>
14793
</div>
148-
)}
94+
<div className={fr.cx("fr-modal__content")}>
95+
<h1
96+
id="fr-modal-title-modal-1"
97+
className={fr.cx("fr-modal__title")}
98+
>
99+
{iconId !== undefined && (
100+
<span className={fr.cx(iconId, "fr-fi--lg")} />
101+
)}
102+
{title}
103+
</h1>
104+
{children}
105+
</div>
106+
{buttons !== undefined && (
107+
<div className="fr-modal__footer">
108+
<ul
109+
className={fr.cx(
110+
"fr-btns-group",
111+
"fr-btns-group--right",
112+
"fr-btns-group--inline-reverse",
113+
"fr-btns-group--inline-lg",
114+
"fr-btns-group--icon-left"
115+
)}
116+
>
117+
{[...buttons]
118+
.reverse()
119+
.map(
120+
(
121+
{ doClosesModal = true, ...buttonProps },
122+
i
123+
) => (
124+
<li key={i}>
125+
<Button
126+
{...buttonProps}
127+
priority={
128+
buttonProps.priority ??
129+
(i === 0
130+
? "primary"
131+
: "secondary")
132+
}
133+
{...(!doClosesModal
134+
? {}
135+
: "linkProps" in buttonProps
136+
? {
137+
"linkProps": {
138+
...buttonProps.linkProps,
139+
"aria-controls": id
140+
} as any
141+
}
142+
: {
143+
"nativeButtonProps": {
144+
...buttonProps.nativeButtonProps,
145+
"aria-controls": id
146+
} as any
147+
})}
148+
/>
149+
</li>
150+
)
151+
)}
152+
</ul>
153+
</div>
154+
)}
155+
</div>
149156
</div>
150157
</div>
151158
</div>
152-
</div>
153-
</dialog>
154-
);
155-
});
159+
</dialog>
160+
);
161+
})
162+
);
156163

157164
Modal.displayName = symToStr({ Modal });
158165

0 commit comments

Comments
 (0)