Aller au contenu

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

  1. Demander à l'utilisateur :
  2. La modal a-t-elle besoin de props (données à afficher) ?
  3. La modal a-t-elle un footer sticky (boutons d'action) ?
  4. La modal est-elle dismissible (fermeture par swipe/backdrop) ?

  5. Créer le fichier dans :

  6. Shared : src/components/modals/{ModalName}.tsx
  7. Feature : src/screens/{feature}/components/{ModalName}.tsx

Templates

// 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>
  );
};
// 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>
  );
};
// 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>
  );
};
// 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
  • [ ] useCloseModal pour fermer
  • [ ] Props typées si nécessaire
  • [ ] Traductions ajoutées
  • [ ] isVisible toujours true

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