fix: resolve marker positioning bug and integrate Kirby design system
All checks were successful
Deploy / Build and Deploy to Production (push) Successful in 17s
All checks were successful
Deploy / Build and Deploy to Production (push) Successful in 17s
- Fix marker positioning issue where markers would glide and misalign during zoom - Implement two-wrapper structure to isolate CSS transforms from MapLibre positioning - Outer .custom-marker: MapLibre handles positioning via translate3d() - Inner .marker-inner: Visual transforms (rotate, scale) isolated from MapLibre - Remove debug console.log statements - Integrate Kirby design system in MarkerList and GeocodeSearch components - Use Kirby CSS variables (--input-color-back, --color-border, etc.) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
63dc136309
commit
bad465406d
5 changed files with 170 additions and 163 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -215,6 +215,7 @@ export default {
|
|||
background: var(--color-white);
|
||||
transition: border-color 0.2s;
|
||||
color: #000;
|
||||
background: var(--input-color-back);
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
|
|
|
|||
|
|
@ -138,10 +138,11 @@ export default {
|
|||
const isMarkerClick = target.closest('.custom-marker');
|
||||
|
||||
if (!isMarkerClick) {
|
||||
emit("map-click", {
|
||||
const clickPos = {
|
||||
lat: e.lngLat.lat,
|
||||
lng: e.lngLat.lng
|
||||
});
|
||||
};
|
||||
emit("map-click", clickPos);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
|
|
@ -176,20 +177,27 @@ export default {
|
|||
return;
|
||||
}
|
||||
|
||||
// Create marker element
|
||||
// Create marker element (outer wrapper for MapLibre positioning)
|
||||
const el = document.createElement("div");
|
||||
el.className = "custom-marker";
|
||||
|
||||
// Create inner wrapper for visual transforms (isolates from MapLibre transforms)
|
||||
const inner = document.createElement("div");
|
||||
inner.className = "marker-inner";
|
||||
if (props.selectedMarkerId === markerData.id) {
|
||||
el.classList.add("selected");
|
||||
inner.classList.add("selected");
|
||||
}
|
||||
|
||||
// Add marker number
|
||||
const numberEl = document.createElement("div");
|
||||
numberEl.className = "marker-number";
|
||||
numberEl.textContent = index + 1;
|
||||
el.appendChild(numberEl);
|
||||
inner.appendChild(numberEl);
|
||||
el.appendChild(inner);
|
||||
|
||||
try {
|
||||
const coords = [markerData.position.lon, markerData.position.lat];
|
||||
|
||||
// Create MapLibre marker
|
||||
// Anchor at bottom-center (where the pin tip is)
|
||||
const marker = new maplibregl.Marker({
|
||||
|
|
@ -197,7 +205,7 @@ export default {
|
|||
draggable: true,
|
||||
anchor: 'bottom'
|
||||
})
|
||||
.setLngLat([markerData.position.lon, markerData.position.lat])
|
||||
.setLngLat(coords)
|
||||
.addTo(map.value);
|
||||
|
||||
// Handle marker drag
|
||||
|
|
@ -244,10 +252,13 @@ export default {
|
|||
function updateMarkerSelection(selectedId) {
|
||||
markerElements.value.forEach(({ element }, markerId) => {
|
||||
if (element) {
|
||||
const inner = element.querySelector('.marker-inner');
|
||||
if (inner) {
|
||||
if (markerId === selectedId) {
|
||||
element.classList.add("selected");
|
||||
inner.classList.add("selected");
|
||||
} else {
|
||||
element.classList.remove("selected");
|
||||
inner.classList.remove("selected");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -332,22 +343,27 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
/* Custom marker styles */
|
||||
/* Custom marker outer wrapper - NO transforms here, MapLibre handles positioning */
|
||||
.custom-marker {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
/* MapLibre will position this element via transform: translate3d() */
|
||||
cursor: grab;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.custom-marker:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.custom-marker::before {
|
||||
/* Inner wrapper for visual styling - transforms are isolated here */
|
||||
.marker-inner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.marker-inner::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
|
|
@ -360,12 +376,12 @@ export default {
|
|||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.custom-marker:hover::before {
|
||||
.marker-inner:hover::before {
|
||||
background: #c0392b;
|
||||
transform: rotate(-45deg) scale(1.1);
|
||||
}
|
||||
|
||||
.custom-marker.selected::before {
|
||||
.marker-inner.selected::before {
|
||||
background: #3498db;
|
||||
border-color: #2980b9;
|
||||
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.6);
|
||||
|
|
@ -378,7 +394,7 @@ export default {
|
|||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
transform: translateY(-4px);
|
||||
/* transform removed - was causing positioning issues */
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +1,65 @@
|
|||
<template>
|
||||
<div class="marker-list-sidebar">
|
||||
<!-- Header with add button -->
|
||||
<div class="marker-list-header">
|
||||
<h3>Marqueurs ({{ markers.length }}/{{ maxMarkers }})</h3>
|
||||
<button
|
||||
type="button"
|
||||
class="k-button k-button-icon"
|
||||
<aside class="k-map-markers-sidebar">
|
||||
<!-- Header with counter and add button -->
|
||||
<header class="k-section-header">
|
||||
<k-headline>
|
||||
Marqueurs
|
||||
<k-counter>{{ markers.length }}/{{ maxMarkers }}</k-counter>
|
||||
</k-headline>
|
||||
<k-button
|
||||
icon="add"
|
||||
size="xs"
|
||||
variant="filled"
|
||||
@click="$emit('add-marker')"
|
||||
:disabled="!canAddMarker"
|
||||
title="Ajouter un marqueur"
|
||||
>
|
||||
<k-icon type="add" />
|
||||
</button>
|
||||
</div>
|
||||
Ajouter
|
||||
</k-button>
|
||||
</header>
|
||||
|
||||
<!-- Geocode search -->
|
||||
<div class="geocode-search-container">
|
||||
<div class="k-map-markers-search">
|
||||
<GeocodeSearch @select-location="$emit('select-location', $event)" />
|
||||
</div>
|
||||
|
||||
<!-- Marker list items -->
|
||||
<div class="marker-list-items">
|
||||
<!-- Marker list -->
|
||||
<div class="k-map-markers-list">
|
||||
<div
|
||||
v-for="(marker, index) in markers"
|
||||
:key="marker.id"
|
||||
class="marker-item"
|
||||
:class="{ active: selectedMarkerId === marker.id }"
|
||||
class="k-map-marker-item"
|
||||
:class="{ 'is-selected': selectedMarkerId === marker.id }"
|
||||
@click="$emit('select-marker', marker.id)"
|
||||
>
|
||||
<div class="marker-item-content">
|
||||
<span class="marker-number">{{ index + 1 }}</span>
|
||||
<span class="marker-title">
|
||||
<span class="k-map-marker-icon">
|
||||
{{ index + 1 }}
|
||||
</span>
|
||||
<span class="k-map-marker-text">
|
||||
{{ marker.title || `Marqueur ${index + 1}` }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="marker-item-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="k-button k-button-small"
|
||||
<span class="k-map-marker-options">
|
||||
<k-button
|
||||
icon="open"
|
||||
size="xs"
|
||||
@click.stop="$emit('edit-marker', marker.id)"
|
||||
title="Modifier le marqueur"
|
||||
>
|
||||
<k-icon type="edit" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="k-button k-button-small"
|
||||
/>
|
||||
<k-button
|
||||
icon="trash"
|
||||
size="xs"
|
||||
@click.stop="$emit('delete-marker', marker.id)"
|
||||
title="Supprimer le marqueur"
|
||||
>
|
||||
<k-icon type="trash" />
|
||||
</button>
|
||||
</div>
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-if="markers.length === 0" class="marker-list-empty">
|
||||
Cliquez sur la carte pour ajouter des marqueurs
|
||||
</div>
|
||||
<div v-if="markers.length === 0" class="k-map-markers-empty">
|
||||
<k-icon type="map-pin" />
|
||||
<p class="k-map-markers-empty-text">Aucun marqueur</p>
|
||||
<p class="k-map-markers-empty-info">
|
||||
Cliquez sur la carte ou sur "Ajouter" pour créer un marqueur
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -94,7 +95,9 @@ export default {
|
|||
],
|
||||
|
||||
setup(props) {
|
||||
const canAddMarker = computed(() => props.markers.length < props.maxMarkers);
|
||||
const canAddMarker = computed(
|
||||
() => props.markers.length < props.maxMarkers
|
||||
);
|
||||
|
||||
return {
|
||||
canAddMarker,
|
||||
|
|
@ -104,143 +107,130 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.marker-list-sidebar {
|
||||
width: var(--marker-list-width, 250px);
|
||||
/* Sidebar container - uses Kirby's layout system */
|
||||
.k-map-markers-sidebar {
|
||||
width: var(--marker-list-width, 280px);
|
||||
flex-shrink: 0;
|
||||
border-right: 1px solid var(--color-border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--color-background);
|
||||
border-right: 1px solid var(--color-border);
|
||||
background: var(--color-white);
|
||||
}
|
||||
|
||||
.marker-list-header {
|
||||
/* Header - minimal override of k-section-header */
|
||||
.k-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
gap: var(--spacing-2);
|
||||
padding: var(--spacing-3);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background: var(--color-white);
|
||||
background: var(--panel-color-back);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.marker-list-header h3 {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-text-light);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.marker-list-header .k-button-icon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
min-width: 2rem;
|
||||
padding: 0;
|
||||
.k-section-header .k-headline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-black);
|
||||
gap: var(--spacing-2);
|
||||
}
|
||||
|
||||
.marker-list-header .k-button-icon .k-icon {
|
||||
color: var(--color-black);
|
||||
}
|
||||
|
||||
.marker-list-header .k-button-icon:hover {
|
||||
/* Search container */
|
||||
.k-map-markers-search {
|
||||
padding: var(--spacing-3);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background: var(--color-background);
|
||||
}
|
||||
|
||||
.marker-list-header .k-button-icon:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.geocode-search-container {
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background: var(--color-white);
|
||||
}
|
||||
|
||||
.marker-list-items {
|
||||
/* List container */
|
||||
.k-map-markers-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 0.5rem;
|
||||
padding: var(--spacing-2);
|
||||
background: var(--color-background);
|
||||
}
|
||||
|
||||
.marker-item {
|
||||
/* Marker item - styled like Kirby's k-item */
|
||||
.k-map-marker-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
gap: var(--spacing-2);
|
||||
padding: var(--spacing-2);
|
||||
margin-bottom: var(--spacing-1);
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--rounded-sm);
|
||||
border-radius: var(--rounded);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.marker-item:hover {
|
||||
background: var(--color-background);
|
||||
border-color: var(--color-focus);
|
||||
color: #fff;
|
||||
.k-map-marker-item:hover {
|
||||
background: var(--color-gray-200);
|
||||
}
|
||||
|
||||
.marker-item.active {
|
||||
background: var(--color-focus-outline);
|
||||
border-color: var(--color-focus);
|
||||
.k-map-marker-item.is-selected {
|
||||
background: var(--color-blue-300);
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
.marker-item-content {
|
||||
.k-map-marker-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.marker-number {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: var(--color-gray-400);
|
||||
color: var(--color-white);
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: 50%;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
background: var(--color-gray-300);
|
||||
color: var(--color-white);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.marker-item.active .marker-number {
|
||||
background: var(--color-focus);
|
||||
.k-map-marker-item.is-selected .k-map-marker-icon {
|
||||
background: var(--color-blue-600);
|
||||
}
|
||||
|
||||
.marker-title {
|
||||
font-size: 0.875rem;
|
||||
.k-map-marker-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
|
||||
.marker-item-actions {
|
||||
.k-map-marker-options {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
gap: var(--spacing-1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.k-button-small {
|
||||
padding: 0.25rem 0.5rem;
|
||||
min-height: auto;
|
||||
/* Empty state - styled like Kirby's k-empty */
|
||||
.k-map-markers-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--spacing-12) var(--spacing-6);
|
||||
text-align: center;
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
|
||||
.marker-list-empty {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
color: var(--color-text-light);
|
||||
font-size: 0.875rem;
|
||||
.k-map-markers-empty .k-icon {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-bottom: var(--spacing-3);
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.k-map-markers-empty-text {
|
||||
margin: 0 0 var(--spacing-2);
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
color: var(--color-gray-800);
|
||||
}
|
||||
|
||||
.k-map-markers-empty-info {
|
||||
margin: 0;
|
||||
font-size: var(--text-sm);
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue