@lebonplan/shared-address-selector

v1.3.3

Published

React address selector component with GPS geolocation and Google Places autocomplete using Places API (New)

Readme

@lebonplan/shared-address-selector

Package React réutilisable pour la sélection d'adresse avec géolocalisation GPS et recherche Google Places.

Utilise Places API (New) pour l'autocomplete et Geocoding API v3 (version standard) pour le reverse geocoding.

Installation

pnpm add @lebonplan/shared-address-selector

Dépendances requises

  • React 19.x
  • React DOM 19.x
  • Google Maps API Key (à configurer dans votre application)

Utilisation

Composant principal : AddressSelectorModal

import { AddressSelectorModal, AddressSelectionData } from '@lebonplan/shared-address-selector';

function MyComponent() {
  const [isOpen, setIsOpen] = useState(false);
  const [selectedAddress, setSelectedAddress] = useState<AddressSelectionData | null>(null);

  const handleComplete = (address: AddressSelectionData) => {
    setSelectedAddress(address);
    // Transformer en CreateAddressDto pour l'API
    const dto = {
      placeId: address.googlePlaceId,
      latitude: address.latitude, // Toujours présent
      longitude: address.longitude, // Toujours présent
      accuracy: address.accuracy, // Toujours présent (GPS réel ou 50m)
      displayAddress: address.displayAddress,
      isCustomDisplayAddress: address.isCustomDisplayAddress,
    };
    // Envoyer à POST /addresses
  };

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Sélectionner une adresse</button>
      <AddressSelectorModal
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        onComplete={handleComplete}
        apiKey={process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY || ''}
        maxAccuracy={50} // Optionnel, 50m par défaut
      />
    </>
  );
}

Flux utilisateur

Le composant suit un flux privilégiant la géolocalisation GPS :

  1. Ouverture automatique GPS : À l'ouverture du modal, une tentative de géolocalisation GPS est lancée automatiquement
  2. Vérification de précision : Si la précision est supérieure à maxAccuracy (50m par défaut), l'utilisateur est invité à réessayer ou rechercher manuellement
  3. En cas d'échec GPS : Si la géolocalisation échoue (permission, timeout, etc.), l'utilisateur peut :
    • Réessayer la géolocalisation GPS
    • Rechercher manuellement via autocomplete Google Places
  4. Confirmation : Une fois l'adresse obtenue (GPS ou recherche), l'utilisateur peut modifier l'adresse d'affichage avant de confirmer

Composant d'affichage : AddressDisplay

import { AddressDisplay, AddressSelectionData } from '@lebonplan/shared-address-selector';

function MyComponent() {
  const [address, setAddress] = useState<AddressSelectionData | null>(null);
  const [isModalOpen, setIsModalOpen] = useState(false);

  return (
    <AddressDisplay
      address={address}
      onEdit={() => setIsModalOpen(true)}
      showFormattedAddress={true} // Afficher l'adresse Google formatée si personnalisée (défaut: true)
    />
  );
}

Props disponibles :

  • address : L'adresse à afficher (ou null)
  • onEdit : Callback appelé lors du clic sur le bouton d'édition
  • className : Classes CSS supplémentaires (optionnel)
  • showFormattedAddress : Afficher l'adresse Google formatée quand l'adresse est personnalisée (défaut: true)

Hook : useAddressSelector

import { useAddressSelector, AddressSelectionData } from '@lebonplan/shared-address-selector';

function MyComponent() {
  const { isOpen, open, close, handleComplete } = useAddressSelector(address => {
    console.log('Address selected:', address);
  });

  return <button onClick={open}>Sélectionner une adresse</button>;
}

Types

AddressSelectionData

Type unifié représentant une adresse sélectionnée (GPS ou Google Places) :

interface AddressSelectionData {
  type: 'google_place' | 'gps';
  googlePlaceId: string;
  latitude: number;
  longitude: number;
  accuracy: number; // Précision GPS réelle ou 50m pour Google Places
  formattedAddress: string;
  displayAddress: string;
  isCustomDisplayAddress: boolean;
  city: string; // Ville extraite depuis Google Places (depuis v1.2.0)
}
  • type: 'gps' : Adresse obtenue via géolocalisation GPS (accuracy = précision réelle de l'appareil)
  • type: 'google_place' : Adresse sélectionnée via recherche Google Places (accuracy = 50m par défaut)
  • city : Ville extraite automatiquement depuis les addressComponents de Google Places (locality ou administrative_area_level_1)

Intégration avec le backend

Le composant retourne AddressSelectionData qui doit être transformé en CreateAddressDto avant l'envoi à POST /addresses :

const dto: CreateAddressDto = {
  placeId: addressData.googlePlaceId,
  latitude: addressData.latitude, // Toujours présent (GPS réel ou Google Places)
  longitude: addressData.longitude, // Toujours présent (GPS réel ou Google Places)
  accuracy: addressData.accuracy, // Toujours présent (GPS réel ou 50m pour Google Places)
  displayAddress: addressData.displayAddress,
  isCustomDisplayAddress: addressData.isCustomDisplayAddress,
  city: addressData.city, // Disponible depuis v1.2.0 (extrait automatiquement)
  // label et isDefault peuvent être ajoutés par l'app
};

Note : Le champ city est maintenant automatiquement extrait depuis les addressComponents de Google Places lors de la sélection d'une adresse (via GPS ou recherche manuelle).

Configuration

Props du composant AddressSelectorModal

  • isOpen : État d'ouverture du modal (requis)
  • onClose : Callback appelé lors de la fermeture (requis)
  • onComplete : Callback appelé avec AddressSelectionData lors de la confirmation (requis)
  • apiKey : Clé API Google Maps (requis)
  • maxAccuracy (optionnel, défaut: 50) : Précision maximale acceptée en mètres pour la géolocalisation GPS. Si la précision est supérieure, l'utilisateur devra réessayer ou rechercher manuellement.

Améliorations UX (v1.2.0+)

  • Modal responsive : Le modal s'adapte à différentes tailles d'écran avec un layout flex et scrollable
  • Gestion améliorée du displayAddress : L'adresse d'affichage peut être modifiée indépendamment de l'adresse formatée
  • Meilleure lisibilité : Amélioration des styles et de la hiérarchie visuelle dans tous les composants
  • Extraction automatique de la ville : Le champ city est maintenant automatiquement extrait depuis Google Places

Variables d'environnement

  • NEXT_PUBLIC_GOOGLE_MAPS_API_KEY : Clé API Google Maps (requise)

Permissions navigateur

Pour la géolocalisation GPS, l'utilisateur doit autoriser l'accès à sa position. Le composant gère automatiquement les cas d'erreur (permission refusée, timeout, etc.).

Structure

packages/shared-address-selector/
├── src/
│   ├── types/
│   │   └── address-selection.types.ts (contient AddressSelectionData avec champ city)
│   ├── hooks/
│   │   ├── useGeolocation.ts
│   │   ├── useGooglePlaces.ts (utilise Places API New - REST, optimisé avec gestion d'erreurs)
│   │   └── useAddressSelector.ts
│   ├── components/
│   │   ├── AddressSelectorModal.tsx (utilise Geocoding API New - REST, UX améliorée)
│   │   ├── AddressConfirmationScreen.tsx (UX améliorée avec meilleure gestion du displayAddress)
│   │   └── AddressDisplay.tsx (lisibilité améliorée, option showFormattedAddress)
│   └── index.ts

Changelog

v1.3.0 (publié)

  • Publication sur npm

v1.2.0

  • Nouveau champ city : Extraction automatique de la ville depuis Google Places
  • 🎨 Amélioration UX des composants :
    • Modal responsive avec layout flex et scrollable
    • Meilleure gestion du displayAddress dans AddressConfirmationScreen
    • Amélioration de la lisibilité dans AddressDisplay avec option showFormattedAddress
    • Styles améliorés et hiérarchie visuelle optimisée
  • Optimisation useGooglePlaces :
    • Gestion améliorée des erreurs avec AbortController
    • Optimisation des appels API avec annulation des requêtes précédentes
    • Meilleure gestion des timeouts et erreurs réseau

v1.1.0

  • Version initiale avec fonctionnalités de base

Développement

# Build
pnpm build

# Watch mode
pnpm dev

# Clean
pnpm clean

# Type check
pnpm type-check

Publication sur npm

Prérequis

  1. Être connecté à npm : npm login
  2. Avoir les droits de publication sur le scope @lebonplan

Scripts de publication

# Bump de version patch (1.0.0 -> 1.0.1)
pnpm publish:patch

# Bump de version minor (1.0.0 -> 1.1.0)
pnpm publish:minor

# Bump de version major (1.0.0 -> 2.0.0)
pnpm publish:major

# Test de publication (dry-run)
pnpm publish:dry-run

Les scripts de publication effectuent automatiquement :

  1. Bump de version dans package.json
  2. Build du package
  3. Publication sur npm avec --access public (nécessaire pour les scoped packages publics)

Bump de version manuel

Si vous voulez juste bump la version sans publier :

# Patch
pnpm version:patch

# Minor
pnpm version:minor

# Major
pnpm version:major