<div class="relative w-full">
<div class="relative w-full bg-white rounded-lg border border-neutral-300 px-4 py-3 flex items-center gap-3" x-data="SearchPlace()" @click.away="showPredictions = false">
<!-- Icône de recherche -->
<svg class=" shrink-0" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 3.75C6.99594 3.75 3.75 6.99594 3.75 11C3.75 15.0041 6.99594 18.25 11 18.25C15.0041 18.25 18.25 15.0041 18.25 11C18.25 6.99594 15.0041 3.75 11 3.75ZM2.25 11C2.25 6.16751 6.16751 2.25 11 2.25C15.8325 2.25 19.75 6.16751 19.75 11C19.75 13.1462 18.9773 15.112 17.6949 16.6342L21.5303 20.4697C21.8232 20.7626 21.8232 21.2374 21.5303 21.5303C21.2374 21.8232 20.7626 21.8232 20.4697 21.5303L16.6342 17.6949C15.112 18.9773 13.1462 19.75 11 19.75C6.16751 19.75 2.25 15.8325 2.25 11Z" fill="currentColor" />
</svg>
<!-- Champ de recherche -->
<input type="text" x-model="query" @focus="showPredictions = predictions.length > 0" placeholder="Rechercher une ville..." class="w-full p-0 border-none text-neutral-900 placeholder-neutral-900 focus:outline-none focus:ring-0">
<!-- Bouton de géolocalisation -->
<button @click="getLocation()" class="flex-shrink-0">
<svg class=" shrink-0" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.75 2C12.75 1.58579 12.4142 1.25 12 1.25C11.5858 1.25 11.25 1.58579 11.25 2V4.28582C7.5685 4.63935 4.63935 7.5685 4.28582 11.25H2C1.58579 11.25 1.25 11.5858 1.25 12C1.25 12.4142 1.58579 12.75 2 12.75H4.28582C4.63935 16.4315 7.5685 19.3607 11.25 19.7142V22C11.25 22.4142 11.5858 22.75 12 22.75C12.4142 22.75 12.75 22.4142 12.75 22V19.7142C16.4315 19.3607 19.3607 16.4315 19.7142 12.75H22C22.4142 12.75 22.75 12.4142 22.75 12C22.75 11.5858 22.4142 11.25 22 11.25H19.7142C19.3607 7.5685 16.4315 4.63935 12.75 4.28582V2ZM18.25 12C18.25 15.4518 15.4518 18.25 12 18.25C8.54822 18.25 5.75 15.4518 5.75 12C5.75 8.54822 8.54822 5.75 12 5.75C15.4518 5.75 18.25 8.54822 18.25 12Z" fill="currentColor" />
</svg>
</button>
<!-- Liste des prédictions -->
<div x-show="showPredictions" class="absolute z-10 top-14 left-0 right-0 mt-2 bg-white rounded-lg shadow-lg border border-neutral-300 overflow-hidden" x-transition>
<template x-for="prediction in predictions" :key="prediction.place_id">
<button @click="selectPlace(prediction)" class="w-full px-4 py-3 text-left hover:bg-neutral-50 text-base">
<span x-text="prediction.structured_formatting.main_text"></span>
<span x-text="prediction.structured_formatting.secondary_text" class="text-neutral-500"></span>
</button>
</template>
</div>
</div>
</div>
<script>
function SearchPlace() {
return {
query: '',
autocomplete: null,
predictions: [],
showPredictions: false,
selectedPlace: null,
isLocating: false,
async init() {
try {
const {
AutocompleteService
} = await google.maps.importLibrary("places");
this.autocomplete = new AutocompleteService();
this.geocoder = new google.maps.Geocoder();
// Vérifie le cookie de géolocalisation refusée
if (!document.cookie.includes('geolocation_denied=true')) {
this.getLocation(false);
}
this.$watch('query', (value) => {
if (value.length > 2) {
this.getPredictions(value);
} else {
this.predictions = [];
this.showPredictions = false;
}
});
} catch (error) {
console.error('Error initializing Google Places:', error);
}
},
async getPredictions(input) {
if (!input) return;
try {
const response = await this.autocomplete.getPlacePredictions({
input: input,
types: ['(cities)'],
fields: ['formatted_address', 'geometry', 'name']
});
this.predictions = response.predictions;
this.showPredictions = true;
} catch (error) {
console.error('Error fetching predictions:', error);
}
},
async selectPlace(prediction) {
try {
const response = await this.geocoder.geocode({
placeId: prediction.place_id
});
const location = response.results[0].geometry.location;
Alpine.store('locator').setMapCenter(location.lat(), location.lng(), 12);
this.query = prediction.structured_formatting.main_text;
this.showPredictions = false;
this.predictions = [];
} catch (error) {
console.error('Error getting coordinates:', error);
}
},
getLocation(showError = true) {
// Vérifie si la géolocalisation a été refusée précédemment
if (document.cookie.includes('geolocation_denied=true')) {
showError && alert('Vous avez précédemment refusé la géolocalisation. Pour la réactiver, veuillez effacer vos cookies pour ce site.');
return;
}
if (!navigator.geolocation) {
showError && alert('La géolocalisation n\'est pas supportée par ce navigateur.');
return;
}
this.isLocating = true;
navigator.geolocation.getCurrentPosition(
(position) => {
Alpine.store('locator').setMapCenter(
position.coords.latitude,
position.coords.longitude,
12
);
this.isLocating = false;
},
(error) => {
this.isLocating = false;
if (!showError) return;
if (error.code === error.PERMISSION_DENIED) {
// Cookie valide 30 jours
document.cookie = "geolocation_denied=true;max-age=2592000;path=/";
alert("Veuillez autoriser la géolocalisation dans les paramètres de votre navigateur. Cette préférence sera mémorisée pendant 30 jours.");
} else if (error.code === error.POSITION_UNAVAILABLE) {
alert("Votre position n'est pas disponible actuellement.");
} else if (error.code === error.TIMEOUT) {
alert("La demande de géolocalisation a pris trop de temps.");
}
}, {
enableHighAccuracy: true,
timeout: 3000,
maximumAge: 30000
}
);
},
}
}
</script>
<div class="relative w-full">
<div class="relative w-full bg-white rounded-lg border border-neutral-300 px-4 py-3 flex items-center gap-3"
x-data="SearchPlace()"
@click.away="showPredictions = false"
>
<!-- Icône de recherche -->
{% render "@icons-library--search-outline" %}
<!-- Champ de recherche -->
<input
type="text"
x-model="query"
@focus="showPredictions = predictions.length > 0"
placeholder="Rechercher une ville..."
class="w-full p-0 border-none text-neutral-900 placeholder-neutral-900 focus:outline-none focus:ring-0"
>
<!-- Bouton de géolocalisation -->
<button
@click="getLocation()"
class="flex-shrink-0"
>
{% render "@icons-library--locate" %}
</button>
<!-- Liste des prédictions -->
<div
x-show="showPredictions"
class="absolute z-10 top-14 left-0 right-0 mt-2 bg-white rounded-lg shadow-lg border border-neutral-300 overflow-hidden"
x-transition
>
<template x-for="prediction in predictions" :key="prediction.place_id">
<button
@click="selectPlace(prediction)"
class="w-full px-4 py-3 text-left hover:bg-neutral-50 text-base"
>
<span x-text="prediction.structured_formatting.main_text"></span>
<span x-text="prediction.structured_formatting.secondary_text" class="text-neutral-500"></span>
</button>
</template>
</div>
</div>
</div>
<script>
function SearchPlace() {
return {
query: '',
autocomplete: null,
predictions: [],
showPredictions: false,
selectedPlace: null,
isLocating: false,
async init() {
try {
const {AutocompleteService} = await google.maps.importLibrary("places");
this.autocomplete = new AutocompleteService();
this.geocoder = new google.maps.Geocoder();
// Vérifie le cookie de géolocalisation refusée
if (!document.cookie.includes('geolocation_denied=true')) {
this.getLocation(false);
}
this.$watch('query', (value) => {
if (value.length > 2) {
this.getPredictions(value);
} else {
this.predictions = [];
this.showPredictions = false;
}
});
} catch (error) {
console.error('Error initializing Google Places:', error);
}
},
async getPredictions(input) {
if (!input) return;
try {
const response = await this.autocomplete.getPlacePredictions({
input: input,
types: ['(cities)'],
fields: ['formatted_address', 'geometry', 'name']
});
this.predictions = response.predictions;
this.showPredictions = true;
} catch (error) {
console.error('Error fetching predictions:', error);
}
},
async selectPlace(prediction) {
try {
const response = await this.geocoder.geocode({
placeId: prediction.place_id
});
const location = response.results[0].geometry.location;
Alpine.store('locator').setMapCenter(location.lat(), location.lng(), 12);
this.query = prediction.structured_formatting.main_text;
this.showPredictions = false;
this.predictions = [];
} catch (error) {
console.error('Error getting coordinates:', error);
}
},
getLocation(showError = true) {
// Vérifie si la géolocalisation a été refusée précédemment
if (document.cookie.includes('geolocation_denied=true')) {
showError && alert('Vous avez précédemment refusé la géolocalisation. Pour la réactiver, veuillez effacer vos cookies pour ce site.');
return;
}
if (!navigator.geolocation) {
showError && alert('La géolocalisation n\'est pas supportée par ce navigateur.');
return;
}
this.isLocating = true;
navigator.geolocation.getCurrentPosition(
(position) => {
Alpine.store('locator').setMapCenter(
position.coords.latitude,
position.coords.longitude,
12
);
this.isLocating = false;
},
(error) => {
this.isLocating = false;
if (!showError) return;
if (error.code === error.PERMISSION_DENIED) {
// Cookie valide 30 jours
document.cookie = "geolocation_denied=true;max-age=2592000;path=/";
alert("Veuillez autoriser la géolocalisation dans les paramètres de votre navigateur. Cette préférence sera mémorisée pendant 30 jours.");
} else if (error.code === error.POSITION_UNAVAILABLE) {
alert("Votre position n'est pas disponible actuellement.");
} else if (error.code === error.TIMEOUT) {
alert("La demande de géolocalisation a pris trop de temps.");
}
},
{enableHighAccuracy: true, timeout: 3000, maximumAge: 30000}
);
},
}
}
</script>
/* No context defined. */
No notes defined.