mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 01:01:39 -05:00
Merge pull request #1928 from Freika/fix/map-side-panel
Fix/map side panel
This commit is contained in:
commit
28bc68ffe2
4 changed files with 89 additions and 41 deletions
File diff suppressed because one or more lines are too long
|
|
@ -76,33 +76,46 @@
|
|||
/* Drawer Panel Styles */
|
||||
.leaflet-drawer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 338px;
|
||||
height: 100%;
|
||||
top: 10px;
|
||||
right: 70px; /* Position to the left of the control buttons with margin */
|
||||
width: 24rem;
|
||||
max-height: calc(100% - 20px);
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
transform: translateX(100%);
|
||||
transition: transform 0.3s ease-in-out;
|
||||
border-radius: 8px;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: scale(0.95);
|
||||
transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out, visibility 0.2s;
|
||||
z-index: 450;
|
||||
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
height: auto; /* Make height fit content */
|
||||
cursor: default; /* Override map cursor */
|
||||
}
|
||||
|
||||
.leaflet-drawer * {
|
||||
cursor: default; /* Ensure all children have default cursor */
|
||||
}
|
||||
|
||||
.leaflet-drawer a,
|
||||
.leaflet-drawer button,
|
||||
.leaflet-drawer .btn,
|
||||
.leaflet-drawer input[type="checkbox"] {
|
||||
cursor: pointer; /* Interactive elements get pointer cursor */
|
||||
}
|
||||
|
||||
.leaflet-drawer.open {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
/* Controls transition */
|
||||
/* Controls remain in place - no transition needed */
|
||||
.leaflet-control-layers,
|
||||
.leaflet-control-button,
|
||||
.toggle-panel-button {
|
||||
transition: right 0.3s ease-in-out;
|
||||
z-index: 500;
|
||||
}
|
||||
|
||||
.controls-shifted {
|
||||
right: 338px !important;
|
||||
}
|
||||
|
||||
/* Selection Tool Styles */
|
||||
.leaflet-control-custom {
|
||||
background-color: white;
|
||||
|
|
@ -127,6 +140,5 @@
|
|||
|
||||
/* Cancel Selection Button */
|
||||
#cancel-selection-button {
|
||||
margin-bottom: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@ export class VisitsManager {
|
|||
const visitsCount = dateGroups[dateStr].count || 0;
|
||||
|
||||
return `
|
||||
<div class="flex justify-between items-center py-1 border-b border-base-300 last:border-0 my-2 hover:bg-accent hover:text-accent-content transition-colors">
|
||||
<div class="flex justify-between items-center py-1 border-b border-base-300 last:border-0 my-2 hover:bg-accent hover:text-accent-content transition-colors border-radius-md">
|
||||
<div class="font-medium">${dateStr}</div>
|
||||
<div class="flex gap-2">
|
||||
${pointsCount > 0 ? `<div class="badge badge-secondary">${pointsCount} pts</div>` : ''}
|
||||
|
|
@ -379,14 +379,18 @@ export class VisitsManager {
|
|||
`;
|
||||
}).join('');
|
||||
|
||||
// Create the whole panel
|
||||
// Create the whole panel with collapsible content
|
||||
return `
|
||||
<div class="bg-base-100 rounded-lg p-3 mb-4 shadow-sm">
|
||||
<h3 class="text-lg font-bold mb-2">Data in Selected Area</h3>
|
||||
<div class="divide-y divide-base-300">
|
||||
${dateItems}
|
||||
<details id="data-section-collapse" class="collapse collapse-arrow bg-base-100 rounded-lg mb-4 shadow-sm">
|
||||
<summary class="collapse-title text-lg font-bold">
|
||||
Data in Selected Area
|
||||
</summary>
|
||||
<div class="collapse-content">
|
||||
<div class="divide-y divide-base-300">
|
||||
${dateItems}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
`;
|
||||
}
|
||||
|
||||
|
|
@ -411,20 +415,20 @@ export class VisitsManager {
|
|||
|
||||
// Create a button container
|
||||
const buttonContainer = document.createElement('div');
|
||||
buttonContainer.className = 'flex gap-2 mb-4';
|
||||
buttonContainer.className = 'flex flex-col gap-2 mb-4';
|
||||
buttonContainer.id = 'selection-button-container';
|
||||
|
||||
// Cancel button
|
||||
const cancelButton = document.createElement('button');
|
||||
cancelButton.id = 'cancel-selection-button';
|
||||
cancelButton.className = 'btn btn-sm btn-warning flex-1';
|
||||
cancelButton.className = 'btn btn-sm btn-warning w-full';
|
||||
cancelButton.textContent = 'Cancel Selection';
|
||||
cancelButton.onclick = () => this.clearSelection();
|
||||
|
||||
// Delete all selected points button
|
||||
const deleteButton = document.createElement('button');
|
||||
deleteButton.id = 'delete-selection-button';
|
||||
deleteButton.className = 'btn btn-sm btn-error flex-1';
|
||||
deleteButton.className = 'btn btn-sm btn-error w-full';
|
||||
deleteButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="inline mr-1"><path d="M3 6h18"></path><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path></svg>Delete Points';
|
||||
deleteButton.onclick = () => this.deleteSelectedPoints();
|
||||
|
||||
|
|
@ -626,11 +630,6 @@ export class VisitsManager {
|
|||
drawerButton.innerHTML = this.drawerOpen ? '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-panel-right-close-icon lucide-panel-right-close"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/><path d="m8 9 3 3-3 3"/></svg>' : '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-panel-right-open-icon lucide-panel-right-open"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/><path d="m10 15-3-3 3-3"/></svg>';
|
||||
}
|
||||
|
||||
const controls = document.querySelectorAll('.leaflet-control-layers, .toggle-panel-button, .leaflet-right-panel, .drawer-button, #selection-tool-button');
|
||||
controls.forEach(control => {
|
||||
control.classList.toggle('controls-shifted');
|
||||
});
|
||||
|
||||
// Update the drawer content if it's being opened - but don't fetch visits automatically
|
||||
// Only show the "no data" message if there's no selection active
|
||||
if (this.drawerOpen && !this.isSelectionActive) {
|
||||
|
|
@ -654,16 +653,18 @@ export class VisitsManager {
|
|||
createDrawer() {
|
||||
const drawer = document.createElement('div');
|
||||
drawer.id = 'visits-drawer';
|
||||
drawer.className = 'fixed top-0 right-0 h-full w-64 bg-base-100 shadow-lg transform translate-x-full transition-transform duration-300 ease-in-out z-39 overflow-y-auto leaflet-drawer';
|
||||
drawer.className = 'bg-base-100 shadow-lg z-39 overflow-y-auto leaflet-drawer';
|
||||
|
||||
// Add styles to make the drawer scrollable
|
||||
drawer.style.overflowY = 'auto';
|
||||
drawer.style.maxHeight = '100vh';
|
||||
|
||||
drawer.innerHTML = `
|
||||
<div class="p-3 drawer">
|
||||
<h2 class="text-xl font-bold mb-4 text-accent-content">Recent Visits</h2>
|
||||
<div id="visits-list" class="space-y-2">
|
||||
<div class="p-3 my-2 drawer flex flex-col items-center relative">
|
||||
<button id="close-visits-drawer" class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2" title="Close panel">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-x-icon lucide-circle-x"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>
|
||||
</button>
|
||||
<h2 class="text-xl font-bold mb-4 text-accent-content w-full text-center">Recent Visits</h2>
|
||||
<div id="visits-list" class="space-y-2 w-full">
|
||||
<p class="text-gray-500">Loading visits...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -675,6 +676,15 @@ export class VisitsManager {
|
|||
L.DomEvent.disableClickPropagation(drawer);
|
||||
|
||||
this.map.getContainer().appendChild(drawer);
|
||||
|
||||
// Add close button event listener
|
||||
const closeButton = drawer.querySelector('#close-visits-drawer');
|
||||
if (closeButton) {
|
||||
closeButton.addEventListener('click', () => {
|
||||
this.toggleDrawer();
|
||||
});
|
||||
}
|
||||
|
||||
return drawer;
|
||||
}
|
||||
|
||||
|
|
@ -833,6 +843,10 @@ export class VisitsManager {
|
|||
return;
|
||||
}
|
||||
|
||||
// Save the current state of collapsible sections before updating
|
||||
const dataSectionOpen = document.querySelector('#data-section-collapse')?.open || false;
|
||||
const visitsSectionOpen = document.querySelector('#visits-section-collapse')?.open || false;
|
||||
|
||||
// Update the drawer title if selection is active
|
||||
if (this.isSelectionActive && this.selectionRect) {
|
||||
const visitsCount = visits ? visits.filter(visit => visit.status !== 'declined').length : 0;
|
||||
|
|
@ -896,7 +910,7 @@ export class VisitsManager {
|
|||
const visitStyle = visit.status === 'suggested' ? 'border: 2px dashed #60a5fa;' : '';
|
||||
|
||||
return `
|
||||
<div class="w-full p-3 m-2 rounded-lg hover:bg-base-300 transition-colors visit-item relative ${bgClass}"
|
||||
<div class="w-full p-3 mt-2 rounded-lg hover:bg-base-300 transition-colors visit-item relative ${bgClass}"
|
||||
style="${visitStyle}"
|
||||
data-lat="${visit.place?.latitude || ''}"
|
||||
data-lng="${visit.place?.longitude || ''}"
|
||||
|
|
@ -924,8 +938,31 @@ export class VisitsManager {
|
|||
`;
|
||||
}).join('');
|
||||
|
||||
// Wrap visits in a collapsible section
|
||||
const visitsSection = visits && visits.length > 0 ? `
|
||||
<details id="visits-section-collapse" class="collapse collapse-arrow bg-base-100 rounded-lg mb-4 shadow-sm">
|
||||
<summary class="collapse-title text-lg font-bold">
|
||||
Visits (${visits.filter(v => v.status !== 'declined').length})
|
||||
</summary>
|
||||
<div class="collapse-content">
|
||||
${visitsHtml}
|
||||
</div>
|
||||
</details>
|
||||
` : '';
|
||||
|
||||
// Combine date summary and visits HTML
|
||||
container.innerHTML = dateGroupsHtml + visitsHtml;
|
||||
container.innerHTML = dateGroupsHtml + visitsSection;
|
||||
|
||||
// Restore the state of collapsible sections
|
||||
const dataSection = document.querySelector('#data-section-collapse');
|
||||
const visitsSection2 = document.querySelector('#visits-section-collapse');
|
||||
|
||||
if (dataSection && dataSectionOpen) {
|
||||
dataSection.open = true;
|
||||
}
|
||||
if (visitsSection2 && visitsSectionOpen) {
|
||||
visitsSection2.open = true;
|
||||
}
|
||||
|
||||
// Add the circles layer to the map
|
||||
this.visitCircles.addTo(this.map);
|
||||
|
|
|
|||
|
|
@ -142,7 +142,6 @@ test.describe('Side Panel', () => {
|
|||
const visitCount = await visitItems.count();
|
||||
|
||||
const noVisitsMessage = page.locator('#visits-list p.text-gray-500');
|
||||
const hasNoVisitsMessage = await noVisitsMessage.count() > 0;
|
||||
|
||||
// Either we have visits OR we have a "no visits" message (not "Loading...")
|
||||
if (visitCount > 0) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue