Create Modal¶
Crée une nouvelle modal/bottom sheet suivant le pattern useModals.
Arguments¶
name(requis) : Nom de la modal en PascalCase (ex:ConfirmPayment,SelectProperty)
Instructions¶
- Demander à l'utilisateur :
- La modal a-t-elle besoin de props (données à afficher) ?
- La modal a-t-elle un footer sticky (boutons d'action) ?
-
La modal est-elle dismissible (fermeture par swipe/backdrop) ?
-
Créer le fichier dans :
- Shared :
src/components/modals/{ModalName}.tsx - Feature :
src/screens/{feature}/components/{ModalName}.tsx
Templates¶
Modal basique¶
// src/components/modals/{{ModalName}}.tsx
import { Box } from "@bricks-common/uimmo/components/Box";
import { Button } from "@bricks-common/uimmo/components/buttons";
import { BottomSheetModal, useCloseModal } from "@bricks-common/uimmo/components/modals";
import { Text } from "@bricks-common/uimmo/components/Text";
import { useTranslation } from "react-i18next";
export const {{ModalName}} = () => {
const { t } = useTranslation();
const closeModal = useCloseModal();
return (
<BottomSheetModal isVisible onClose={closeModal} title={t("modals.{{modalName}}.title")}>
<Box p="2xl" gap="lg">
<Text>{t("modals.{{modalName}}.description")}</Text>
<Button onPress={closeModal} label={t("common.close")} />
</Box>
</BottomSheetModal>
);
};
Modal avec props¶
// src/components/modals/{{ModalName}}.tsx
import { Box } from "@bricks-common/uimmo/components/Box";
import { Button } from "@bricks-common/uimmo/components/buttons";
import { BottomSheetModal, useCloseModal } from "@bricks-common/uimmo/components/modals";
import { Text } from "@bricks-common/uimmo/components/Text";
import { useTranslation } from "react-i18next";
type {{ModalName}}Props = {
propertyId: string;
propertyName: string;
onConfirm: () => void;
};
export const {{ModalName}} = ({ propertyId, propertyName, onConfirm }: {{ModalName}}Props) => {
const { t } = useTranslation();
const closeModal = useCloseModal();
const handleConfirm = () => {
onConfirm();
closeModal();
};
return (
<BottomSheetModal isVisible onClose={closeModal} title={t("modals.{{modalName}}.title")}>
<Box p="2xl" gap="lg">
<Text>{t("modals.{{modalName}}.description", { name: propertyName })}</Text>
<Button onPress={handleConfirm} label={t("common.confirm")} />
<Button onPress={closeModal} label={t("common.cancel")} variant="secondary" />
</Box>
</BottomSheetModal>
);
};
Modal avec footer sticky¶
// src/components/modals/{{ModalName}}.tsx
import { Box } from "@bricks-common/uimmo/components/Box";
import { Button } from "@bricks-common/uimmo/components/buttons";
import { BottomSheetModal, useCloseModal } from "@bricks-common/uimmo/components/modals";
import { Text } from "@bricks-common/uimmo/components/Text";
import { useTranslation } from "react-i18next";
type {{ModalName}}Props = {
onConfirm: () => void;
};
export const {{ModalName}} = ({ onConfirm }: {{ModalName}}Props) => {
const { t } = useTranslation();
const closeModal = useCloseModal();
const handleConfirm = () => {
onConfirm();
closeModal();
};
const Footer = (
<Box row gap="md">
<Button onPress={closeModal} label={t("common.cancel")} variant="secondary" fill />
<Button onPress={handleConfirm} label={t("common.confirm")} fill />
</Box>
);
return (
<BottomSheetModal
isVisible
onClose={closeModal}
title={t("modals.{{modalName}}.title")}
footer={Footer}
>
<Box p="2xl" gap="lg">
<Text>{t("modals.{{modalName}}.description")}</Text>
{/* Contenu scrollable */}
</Box>
</BottomSheetModal>
);
};
Modal non-dismissible (action obligatoire)¶
// src/components/modals/{{ModalName}}.tsx
import { Box } from "@bricks-common/uimmo/components/Box";
import { Button } from "@bricks-common/uimmo/components/buttons";
import { BottomSheetModal, useCloseModal } from "@bricks-common/uimmo/components/modals";
import { Text } from "@bricks-common/uimmo/components/Text";
import { useTranslation } from "react-i18next";
type {{ModalName}}Props = {
onAccept: () => void;
};
export const {{ModalName}} = ({ onAccept }: {{ModalName}}Props) => {
const { t } = useTranslation();
const closeModal = useCloseModal();
const handleAccept = () => {
onAccept();
closeModal();
};
return (
<BottomSheetModal
isVisible
onClose={closeModal}
title={t("modals.{{modalName}}.title")}
isDismissible={false}
hideHandlebar
>
<Box p="2xl" gap="lg">
<Text>{t("modals.{{modalName}}.description")}</Text>
<Button onPress={handleAccept} label={t("common.accept")} />
</Box>
</BottomSheetModal>
);
};
Utilisation¶
Ouvrir la modal¶
import { useModals } from "@bricks-common/uimmo/components/modals";
import { {{ModalName}} } from "@components/modals/{{ModalName}}";
const MyComponent = () => {
const { openModal } = useModals();
const handleOpenModal = () => {
openModal({{ModalName}});
// ou avec props
openModal({{ModalName}}, { propertyId: "123", onConfirm: handleConfirm });
};
return <Button onPress={handleOpenModal} label="Ouvrir" />;
};
Ajouter les traductions¶
Dans src/i18n/locales/fr.json :
{
"modals": {
"{{modalName}}": {
"title": "Titre de la modal",
"description": "Description de la modal"
}
}
}
Props BottomSheetModal¶
| Prop | Type | Description |
|---|---|---|
isVisible |
boolean |
Toujours true (modal montée = visible) |
onClose |
() => void |
Fonction de fermeture |
title |
string |
Titre avec header |
footer |
ReactNode |
Footer sticky |
isDismissible |
boolean |
Fermeture par swipe/backdrop (défaut: true) |
hideHandlebar |
boolean |
Cache la barre de drag |
Checklist finale¶
- [ ] Modal créée avec
BottomSheetModal - [ ]
useCloseModalpour fermer - [ ] Props typées si nécessaire
- [ ] Traductions ajoutées
- [ ]
isVisibletoujourstrue
Anti-patterns à éviter¶
// ❌ State local pour contrôler la modal
const [isOpen, setIsOpen] = useState(false);
<BottomSheetModal isVisible={isOpen} />
// ✅ Utiliser useModals
const { openModal } = useModals();
openModal(MyModal);
// ❌ BricksBottomSheet (legacy)
<BricksBottomSheet controller={controller} />
// ✅ BottomSheetModal + useModals
<BottomSheetModal isVisible onClose={closeModal} />
Règles appliquées¶
- front-modals.mdc
- front-i18n.mdc