Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28a982abed | ||
|
|
0ffcc7ae31 | ||
|
|
bb30f23d67 | ||
|
|
87304b1568 |
No files matched your search
@@ -28,8 +28,8 @@
|
||||
<ApplicationId>com.companyname.lmemappecatastaliapp</ApplicationId>
|
||||
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>0.2.24</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>28</ApplicationVersion>
|
||||
<ApplicationDisplayVersion>0.2.28</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>32</ApplicationVersion>
|
||||
|
||||
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
|
||||
+120
-21
@@ -176,10 +176,11 @@ input[type="checkbox"] {
|
||||
position: absolute;
|
||||
top: 72px;
|
||||
right: 12px;
|
||||
bottom: calc(var(--nav-height) + 8px);
|
||||
z-index: 940;
|
||||
display: grid;
|
||||
width: min(320px, calc(100% - 24px));
|
||||
max-height: calc(100% - 152px);
|
||||
box-sizing: border-box;
|
||||
width: min(480px, 50vw);
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
@@ -265,17 +266,30 @@ input[type="checkbox"] {
|
||||
.layer-manager-group,
|
||||
.layer-manager-block,
|
||||
.layer-manager-object {
|
||||
border: 1px solid #dce3e6;
|
||||
border: 1px solid var(--layer-group-border, #dce3e6);
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
background: var(--layer-group-surface, #fff);
|
||||
}
|
||||
|
||||
.layer-manager-group > summary,
|
||||
.layer-manager-block > summary {
|
||||
background: var(--layer-group-parent, #f6f8f9);
|
||||
}
|
||||
|
||||
.layer-manager-object > summary {
|
||||
background: var(--layer-group-surface, #fff);
|
||||
}
|
||||
|
||||
.layer-manager-group > summary,
|
||||
.layer-manager-block > summary,
|
||||
.layer-manager-object > summary {
|
||||
display: grid;
|
||||
grid-template-columns: 28px 20px minmax(0, 1fr) auto auto;
|
||||
align-items: center;
|
||||
grid-template-columns: 28px minmax(0, 1fr) 50px;
|
||||
grid-template-areas:
|
||||
"handle title count"
|
||||
". visibility moves"
|
||||
". actions moves";
|
||||
align-items: start;
|
||||
gap: 6px;
|
||||
min-height: 38px;
|
||||
padding: 4px 6px;
|
||||
@@ -283,14 +297,6 @@ input[type="checkbox"] {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.layer-manager-object > summary {
|
||||
grid-template-columns: 28px 20px minmax(0, 1fr) auto auto auto;
|
||||
}
|
||||
|
||||
.layer-manager-renamable > summary {
|
||||
grid-template-columns: 28px 20px minmax(0, 1fr) auto auto auto;
|
||||
}
|
||||
|
||||
.layer-manager-group > summary::-webkit-details-marker,
|
||||
.layer-manager-block > summary::-webkit-details-marker,
|
||||
.layer-manager-object > summary::-webkit-details-marker {
|
||||
@@ -298,6 +304,7 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
.layer-drag-handle {
|
||||
grid-area: handle;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: 0;
|
||||
@@ -310,22 +317,56 @@ input[type="checkbox"] {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.layer-rename-button {
|
||||
.layer-row-actions {
|
||||
grid-area: actions;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
width: min(130px, 100%);
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.layer-manager-group > summary > input,
|
||||
.layer-manager-block > summary > input,
|
||||
.layer-manager-object > summary > input {
|
||||
grid-area: visibility;
|
||||
justify-self: start;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 1px 0;
|
||||
}
|
||||
|
||||
.layer-rename-button,
|
||||
.layer-remove-button {
|
||||
width: 100%;
|
||||
min-height: 28px;
|
||||
padding: 0 8px;
|
||||
min-width: 0;
|
||||
padding: 4px;
|
||||
border: 1px solid #bfd4cd;
|
||||
border-radius: 4px;
|
||||
background: #eef8f5;
|
||||
color: var(--accent-strong);
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
line-height: 1.15;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.layer-rename-button:active {
|
||||
background: #d9eee7;
|
||||
}
|
||||
|
||||
.layer-remove-button {
|
||||
border-color: #fecaca;
|
||||
background: #fff1f2;
|
||||
color: #b42318;
|
||||
}
|
||||
|
||||
.layer-remove-button:active {
|
||||
background: #ffe4e6;
|
||||
}
|
||||
|
||||
.layer-move-buttons {
|
||||
grid-area: moves;
|
||||
display: inline-grid;
|
||||
grid-template-columns: 24px 24px;
|
||||
gap: 2px;
|
||||
@@ -349,25 +390,83 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
.layer-manager-title {
|
||||
grid-area: title;
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
color: var(--layer-group-accent, var(--ink));
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.layer-manager-group > summary > .layer-manager-title {
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.layer-manager-block > summary > .layer-manager-title {
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.layer-manager-object > summary > .layer-manager-title {
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.layer-manager-count {
|
||||
grid-area: count;
|
||||
justify-self: end;
|
||||
color: var(--muted);
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.layer-manager-children {
|
||||
padding: 0 5px 5px 18px;
|
||||
margin-left: 4px;
|
||||
padding: 4px 3px 5px 6px;
|
||||
border-left: 2px solid var(--layer-group-border, #dce3e6);
|
||||
}
|
||||
|
||||
.layer-manager-block > .layer-manager-children {
|
||||
margin-left: 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.layer-manager-object {
|
||||
margin-left: 3px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.layer-manager-object > summary {
|
||||
grid-template-columns: 24px minmax(0, 1fr) 46px;
|
||||
gap: 4px;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
|
||||
.layer-manager-object > summary > .layer-drag-handle {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.layer-manager-object > summary > .layer-move-buttons {
|
||||
grid-template-columns: 22px 22px;
|
||||
}
|
||||
|
||||
.layer-manager-object > summary > .layer-move-buttons button {
|
||||
width: 22px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.layer-manager-object > summary .layer-rename-button,
|
||||
.layer-manager-object > summary .layer-remove-button {
|
||||
min-height: 24px;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.layer-manager-details {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
padding: 0 8px 7px 40px;
|
||||
padding: 0 6px 7px 12px;
|
||||
color: var(--muted);
|
||||
font-size: 10px;
|
||||
}
|
||||
@@ -918,8 +1017,8 @@ footer {
|
||||
.map-layer-panel {
|
||||
top: 68px;
|
||||
right: 8px;
|
||||
width: min(320px, calc(100% - 16px));
|
||||
max-height: calc(100% - 144px);
|
||||
bottom: calc(var(--nav-height) + 8px);
|
||||
width: 50vw;
|
||||
}
|
||||
|
||||
.tool-sheet {
|
||||
|
||||
+342
-18
@@ -22,8 +22,12 @@ const lmeState = {
|
||||
importedSheetsVisible: false,
|
||||
importedMeasurements: null,
|
||||
drawings: null,
|
||||
drawingsRenderer: null,
|
||||
drawingSelection: null,
|
||||
drawingMeasurements: null,
|
||||
drawControl: null,
|
||||
drawingDeleteButton: null,
|
||||
ignoreMapClickUntil: 0,
|
||||
activeDrawHandler: null,
|
||||
drawEventsBound: false,
|
||||
includeKmlSheetLayer: false,
|
||||
@@ -61,6 +65,7 @@ const lmeState = {
|
||||
managerHiddenMeasurementSummaryIds: new Set(),
|
||||
managerHiddenMeasurementSideIds: new Set(),
|
||||
managerHiddenCategories: new Set(),
|
||||
managerRemovedCategories: new Set(),
|
||||
managerObjectOrder: {},
|
||||
managerCategoryNames: {},
|
||||
managerExpandedRows: new Set(),
|
||||
@@ -177,10 +182,12 @@ function init(dotNetReference) {
|
||||
});
|
||||
const managerHighlightPane = lmeState.map.createPane("managerHighlightPane");
|
||||
managerHighlightPane.style.zIndex = "650";
|
||||
lmeState.drawingsRenderer = L.canvas({ pane: "drawingsPane", tolerance: 14 });
|
||||
setLayerOrder(lmeState.layerOrder);
|
||||
lmeState.imported = L.featureGroup().addTo(lmeState.map);
|
||||
lmeState.importedSheets = L.featureGroup();
|
||||
lmeState.drawings = L.featureGroup().addTo(lmeState.map);
|
||||
lmeState.drawingSelection = L.featureGroup();
|
||||
lmeState.importedMeasurements = L.layerGroup().addTo(lmeState.map);
|
||||
lmeState.drawingMeasurements = L.layerGroup().addTo(lmeState.map);
|
||||
lmeState.selectedParcelsLayer = L.featureGroup().addTo(lmeState.map);
|
||||
@@ -204,6 +211,9 @@ function init(dotNetReference) {
|
||||
}
|
||||
|
||||
lmeState.map.on("click", async event => {
|
||||
if (event.originalEvent?._lmeDrawingSelected || Date.now() < lmeState.ignoreMapClickUntil) {
|
||||
return;
|
||||
}
|
||||
clearManagerHighlight();
|
||||
const info = await queryFeatureInfo(event.latlng);
|
||||
if (info) {
|
||||
@@ -231,6 +241,9 @@ function init(dotNetReference) {
|
||||
if (!lmeState.managerHighlightedObjectId && !lmeState.managerHighlightedParentKey) {
|
||||
return;
|
||||
}
|
||||
if (event.target?.closest?.(".leaflet-draw")) {
|
||||
return;
|
||||
}
|
||||
const selectedObject = event.target?.closest?.(".layer-manager-object");
|
||||
const selectedParent = event.target?.closest?.("[data-highlight-key]");
|
||||
if (selectedObject?.dataset.objectId === lmeState.managerHighlightedObjectId
|
||||
@@ -1251,6 +1264,88 @@ function renameManagedBlock(category, source, records) {
|
||||
setInfo(`Gruppo rinominato: ${name}.`);
|
||||
}
|
||||
|
||||
function managedCategoryCanRemove(category, records) {
|
||||
return records.length > 0
|
||||
&& ["drawings", "imported", "importedSheets", "selectedParcels", "gpsParcels"].includes(category.key);
|
||||
}
|
||||
|
||||
function managedObjectCanRemove(record) {
|
||||
return ["drawings", "imported", "importedSheets", "selectedParcels", "gpsParcels"].includes(record.categoryKey);
|
||||
}
|
||||
|
||||
function layerManagerRowActions(renameButton, removeButton) {
|
||||
if (!renameButton && !removeButton) {
|
||||
return "";
|
||||
}
|
||||
return `<span class="layer-row-actions">${renameButton}${removeButton}</span>`;
|
||||
}
|
||||
|
||||
const layerManagerPalettes = [
|
||||
{ surface: "#f0fdf4", parent: "#dcfce7", border: "#86efac", accent: "#166534" },
|
||||
{ surface: "#eff6ff", parent: "#dbeafe", border: "#93c5fd", accent: "#1d4ed8" },
|
||||
{ surface: "#fffbeb", parent: "#fef3c7", border: "#fcd34d", accent: "#92400e" },
|
||||
{ surface: "#fff1f2", parent: "#ffe4e6", border: "#fda4af", accent: "#be123c" },
|
||||
{ surface: "#f5f3ff", parent: "#ede9fe", border: "#c4b5fd", accent: "#6d28d9" },
|
||||
{ surface: "#ecfeff", parent: "#cffafe", border: "#67e8f9", accent: "#0e7490" },
|
||||
{ surface: "#f7fee7", parent: "#ecfccb", border: "#bef264", accent: "#3f6212" }
|
||||
];
|
||||
|
||||
function applyLayerManagerPalette(row, key) {
|
||||
const hash = [...String(key)].reduce((value, character) =>
|
||||
((value * 31) + character.charCodeAt(0)) >>> 0, 0);
|
||||
const palette = layerManagerPalettes[hash % layerManagerPalettes.length];
|
||||
row.style.setProperty("--layer-group-surface", palette.surface);
|
||||
row.style.setProperty("--layer-group-parent", palette.parent);
|
||||
row.style.setProperty("--layer-group-border", palette.border);
|
||||
row.style.setProperty("--layer-group-accent", palette.accent);
|
||||
}
|
||||
|
||||
function clearManagedObjectState(id) {
|
||||
lmeState.managerHiddenObjects.delete(id);
|
||||
lmeState.managerHiddenObjectIds.delete(id);
|
||||
lmeState.managerHiddenMeasurementSummaryIds.delete(id);
|
||||
lmeState.managerHiddenMeasurementSideIds.delete(id);
|
||||
Object.keys(lmeState.managerObjectOrder).forEach(categoryKey => {
|
||||
lmeState.managerObjectOrder[categoryKey] = lmeState.managerObjectOrder[categoryKey]
|
||||
.filter(objectId => objectId !== id);
|
||||
});
|
||||
}
|
||||
|
||||
function removeManagedRecords(records, subject) {
|
||||
if (!records.length) {
|
||||
return;
|
||||
}
|
||||
const message = records.length === 1
|
||||
? `Rimuovere ${subject} dal progetto? Non verra incluso nel prossimo salvataggio.`
|
||||
: `Rimuovere ${subject} e tutti i suoi ${records.length} oggetti dal progetto? Non verranno inclusi nel prossimo salvataggio.`;
|
||||
if (!window.confirm(message)) {
|
||||
return;
|
||||
}
|
||||
const removedIds = new Set(records.map(record => record.id));
|
||||
const categoryKeys = new Set(records.map(record => record.categoryKey));
|
||||
records.forEach(record => {
|
||||
if (record.parent?.hasLayer?.(record.layer)) {
|
||||
record.parent.removeLayer(record.layer);
|
||||
}
|
||||
clearManagedObjectState(record.id);
|
||||
});
|
||||
categoryKeys.forEach(categoryKey => {
|
||||
const order = lmeState.managerObjectOrder[categoryKey] || [];
|
||||
lmeState.managerObjectOrder[categoryKey] = order.filter(id => !removedIds.has(id));
|
||||
const category = layerManagerCategories().find(item => item.key === categoryKey);
|
||||
if (category && managedRecordsForCategory(category).length === 0) {
|
||||
lmeState.managerRemovedCategories.add(categoryKey);
|
||||
delete lmeState.managerCategoryNames[categoryKey];
|
||||
}
|
||||
});
|
||||
clearManagerHighlight();
|
||||
refreshImportedMeasurements();
|
||||
refreshDrawingMeasurements();
|
||||
updateGpsInsideState();
|
||||
refreshLayerManager();
|
||||
setInfo(`${records.length === 1 ? "Oggetto rimosso" : `${records.length} oggetti rimossi`} dal progetto.`);
|
||||
}
|
||||
|
||||
function managedObjectDetails(layer) {
|
||||
const details = [];
|
||||
const properties = layer?.feature?.properties || {};
|
||||
@@ -1306,6 +1401,11 @@ function refreshLayerManager() {
|
||||
return;
|
||||
}
|
||||
const records = managedRecordsForCategory(category);
|
||||
if (records.length) {
|
||||
lmeState.managerRemovedCategories.delete(category.key);
|
||||
} else if (lmeState.managerRemovedCategories.has(category.key)) {
|
||||
return;
|
||||
}
|
||||
const savedOrder = lmeState.managerObjectOrder[category.key] || [];
|
||||
const positions = new Map(savedOrder.map((id, index) => [id, index]));
|
||||
records.sort((left, right) =>
|
||||
@@ -1328,18 +1428,24 @@ function renderLayerManagerCategory(category, records) {
|
||||
const group = document.createElement("details");
|
||||
group.className = "layer-manager-group";
|
||||
group.dataset.dragType = "category";
|
||||
group.dataset.layerDepth = "0";
|
||||
group.dataset.categoryKey = category.key;
|
||||
applyLayerManagerPalette(group, `category:${category.key}`);
|
||||
bindLayerManagerExpansion(group, `category:${category.key}`);
|
||||
const categoryName = managedCategoryName(category, records);
|
||||
const renameButton = managedCategoryCanRename(category)
|
||||
? `<button type="button" class="layer-rename-button" aria-label="Rinomina gruppo" title="Rinomina">Rinomina</button>`
|
||||
: "";
|
||||
group.classList.toggle("layer-manager-renamable", Boolean(renameButton));
|
||||
const removeButton = managedCategoryCanRemove(category, records)
|
||||
? `<button type="button" class="layer-remove-button" aria-label="Rimuovi gruppo dal progetto" title="Rimuovi dal progetto">Rimuovi</button>`
|
||||
: "";
|
||||
const rowActions = layerManagerRowActions(renameButton, removeButton);
|
||||
group.classList.toggle("layer-manager-with-actions", Boolean(rowActions));
|
||||
const visibleCount = records.filter(record => !lmeState.managerHiddenObjectIds.has(record.id)).length;
|
||||
const categoryVisible = !lmeState.managerHiddenCategories.has(category.key)
|
||||
&& (!category.roots.length || category.roots.some(layer => lmeState.map.hasLayer(layer)));
|
||||
const visible = categoryVisible;
|
||||
group.innerHTML = `<summary><button type="button" class="layer-drag-handle" aria-label="Trascina gruppo">☰</button><input type="checkbox" ${visible ? "checked" : ""} aria-label="Mostra ${escapeHtml(categoryName)}"><span class="layer-manager-title">${escapeHtml(categoryName)}</span>${renameButton}${layerManagerMoveButtons("gruppo")}<span class="layer-manager-count">${records.length}</span></summary><div class="layer-manager-children"></div>`;
|
||||
group.innerHTML = `<summary><button type="button" class="layer-drag-handle" aria-label="Trascina gruppo">☰</button><input type="checkbox" ${visible ? "checked" : ""} aria-label="Mostra ${escapeHtml(categoryName)}"><span class="layer-manager-title">${escapeHtml(categoryName)}</span>${rowActions}${layerManagerMoveButtons("gruppo")}<span class="layer-manager-count">${records.length}</span></summary><div class="layer-manager-children"></div>`;
|
||||
const checkbox = group.querySelector("input");
|
||||
checkbox.indeterminate = categoryVisible && records.length > 0 && visibleCount < records.length;
|
||||
checkbox.addEventListener("click", event => event.stopPropagation());
|
||||
@@ -1349,6 +1455,11 @@ function renderLayerManagerCategory(category, records) {
|
||||
event.stopPropagation();
|
||||
renameManagedCategory(category, records);
|
||||
});
|
||||
group.querySelector(".layer-remove-button")?.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
removeManagedRecords(records, `il gruppo '${categoryName}'`);
|
||||
});
|
||||
bindManagedParentHighlight(group, `category:${category.key}`, records);
|
||||
const children = group.querySelector(".layer-manager-children");
|
||||
if (category.blocks) {
|
||||
@@ -1369,14 +1480,19 @@ function renderLayerManagerBlock(category, source, records) {
|
||||
const block = document.createElement("details");
|
||||
block.className = "layer-manager-block";
|
||||
block.dataset.dragType = "block";
|
||||
block.dataset.layerDepth = "1";
|
||||
block.dataset.categoryKey = category.key;
|
||||
block.dataset.blockKey = source;
|
||||
applyLayerManagerPalette(block, `block:${category.key}:${source}`);
|
||||
bindLayerManagerExpansion(block, `block:${category.key}:${source}`);
|
||||
const visibleCount = records.filter(record => !lmeState.managerHiddenObjectIds.has(record.id)).length;
|
||||
const visible = visibleCount === records.length;
|
||||
const blockName = managedBlockName(source, records);
|
||||
block.classList.add("layer-manager-renamable");
|
||||
block.innerHTML = `<summary><button type="button" class="layer-drag-handle" aria-label="Trascina blocco">☰</button><input type="checkbox" ${visible ? "checked" : ""} aria-label="Mostra ${escapeHtml(blockName)}"><span class="layer-manager-title">${escapeHtml(blockName)}</span><button type="button" class="layer-rename-button" aria-label="Rinomina gruppo" title="Rinomina">Rinomina</button>${layerManagerMoveButtons("blocco")}<span class="layer-manager-count">${records.length}</span></summary><div class="layer-manager-children"></div>`;
|
||||
const rowActions = layerManagerRowActions(
|
||||
`<button type="button" class="layer-rename-button" aria-label="Rinomina gruppo" title="Rinomina">Rinomina</button>`,
|
||||
`<button type="button" class="layer-remove-button" aria-label="Rimuovi gruppo dal progetto" title="Rimuovi dal progetto">Rimuovi</button>`);
|
||||
block.classList.add("layer-manager-with-actions");
|
||||
block.innerHTML = `<summary><button type="button" class="layer-drag-handle" aria-label="Trascina blocco">☰</button><input type="checkbox" ${visible ? "checked" : ""} aria-label="Mostra ${escapeHtml(blockName)}"><span class="layer-manager-title">${escapeHtml(blockName)}</span>${rowActions}${layerManagerMoveButtons("blocco")}<span class="layer-manager-count">${records.length}</span></summary><div class="layer-manager-children"></div>`;
|
||||
const checkbox = block.querySelector("input");
|
||||
checkbox.indeterminate = visibleCount > 0 && !visible;
|
||||
checkbox.addEventListener("click", event => event.stopPropagation());
|
||||
@@ -1389,6 +1505,11 @@ function renderLayerManagerBlock(category, source, records) {
|
||||
event.stopPropagation();
|
||||
renameManagedBlock(category, source, records);
|
||||
});
|
||||
block.querySelector(".layer-remove-button").addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
removeManagedRecords(records, `il gruppo '${blockName}'`);
|
||||
});
|
||||
bindManagedParentHighlight(block, `block:${category.key}:${source}`, records);
|
||||
const children = block.querySelector(".layer-manager-children");
|
||||
records.forEach((record, index) => children.appendChild(renderLayerManagerObject(record, index)));
|
||||
@@ -1402,13 +1523,20 @@ function renderLayerManagerObject(record, index) {
|
||||
object.classList.add("layer-manager-object-selected");
|
||||
}
|
||||
object.dataset.dragType = "object";
|
||||
object.dataset.layerDepth = "2";
|
||||
object.dataset.objectId = record.id;
|
||||
bindLayerManagerExpansion(object, `object:${record.id}`);
|
||||
const visible = !lmeState.managerHiddenObjectIds.has(record.id);
|
||||
const renameButton = managedObjectCanRename(record)
|
||||
? `<button type="button" class="layer-rename-button" aria-label="Rinomina oggetto" title="Rinomina">Rinomina</button>`
|
||||
: "";
|
||||
object.innerHTML = `<summary><button type="button" class="layer-drag-handle" aria-label="Trascina oggetto">☰</button><input type="checkbox" ${visible ? "checked" : ""} aria-label="Mostra oggetto"><span class="layer-manager-title">${escapeHtml(managedObjectLabel(record, index))}</span>${renameButton}${layerManagerMoveButtons("oggetto")}<span class="layer-manager-count">›</span></summary><div class="layer-manager-details"></div>`;
|
||||
const removeButton = managedObjectCanRemove(record)
|
||||
? `<button type="button" class="layer-remove-button" aria-label="Rimuovi oggetto dal progetto" title="Rimuovi dal progetto">Rimuovi</button>`
|
||||
: "";
|
||||
const rowActions = layerManagerRowActions(renameButton, removeButton);
|
||||
object.classList.toggle("layer-manager-with-actions", Boolean(rowActions));
|
||||
const objectName = managedObjectLabel(record, index);
|
||||
object.innerHTML = `<summary><button type="button" class="layer-drag-handle" aria-label="Trascina oggetto">☰</button><input type="checkbox" ${visible ? "checked" : ""} aria-label="Mostra oggetto"><span class="layer-manager-title">${escapeHtml(objectName)}</span>${rowActions}${layerManagerMoveButtons("oggetto")}<span class="layer-manager-count">›</span></summary><div class="layer-manager-details"></div>`;
|
||||
const checkbox = object.querySelector("input");
|
||||
checkbox.addEventListener("click", event => event.stopPropagation());
|
||||
checkbox.addEventListener("change", () => setManagedObjectVisible(record, checkbox.checked, true));
|
||||
@@ -1417,6 +1545,11 @@ function renderLayerManagerObject(record, index) {
|
||||
event.stopPropagation();
|
||||
renameManagedObject(record, index);
|
||||
});
|
||||
object.querySelector(".layer-remove-button")?.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
removeManagedRecords([record], `'${objectName}'`);
|
||||
});
|
||||
object.querySelector(".layer-manager-title").addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
@@ -1648,10 +1781,60 @@ function highlightManagedObject(record) {
|
||||
document.querySelectorAll(".layer-manager-object").forEach(row => {
|
||||
row.classList.toggle("layer-manager-object-selected", row.dataset.objectId === record.id);
|
||||
});
|
||||
setSelectedDrawing(record.categoryKey === "drawings" ? record.layer : null);
|
||||
renderManagerHighlight([record]);
|
||||
fitLayer(record.layer);
|
||||
}
|
||||
|
||||
function drawingEditToolbar() {
|
||||
return lmeState.drawControl?._toolbars?.[L.EditToolbar.TYPE] || null;
|
||||
}
|
||||
|
||||
function drawingEditActive() {
|
||||
return Boolean(drawingEditToolbar()?.enabled?.());
|
||||
}
|
||||
|
||||
function updateDrawingSelectionActions() {
|
||||
const hasSelection = Boolean(lmeState.drawingSelection?.getLayers?.().length);
|
||||
lmeState.drawingDeleteButton?.classList.toggle("leaflet-disabled", !hasSelection);
|
||||
if (lmeState.drawingDeleteButton) {
|
||||
lmeState.drawingDeleteButton.setAttribute("aria-disabled", String(!hasSelection));
|
||||
}
|
||||
}
|
||||
|
||||
function setSelectedDrawing(layer) {
|
||||
if (!lmeState.drawingSelection || drawingEditActive()) {
|
||||
return;
|
||||
}
|
||||
lmeState.drawingSelection.clearLayers();
|
||||
if (layer) {
|
||||
lmeState.drawingSelection.addLayer(layer);
|
||||
}
|
||||
updateDrawingSelectionActions();
|
||||
}
|
||||
|
||||
function selectDrawingLayer(layer) {
|
||||
if (drawingEditActive()) {
|
||||
return;
|
||||
}
|
||||
const record = {
|
||||
id: managedLayerId(layer, "drawings"),
|
||||
categoryKey: "drawings",
|
||||
layer,
|
||||
parent: lmeState.drawings,
|
||||
source: String(layer?.feature?.properties?._lmeSource || "Progetto corrente")
|
||||
};
|
||||
clearManagerHighlight();
|
||||
lmeState.managerHighlightedObjectId = record.id;
|
||||
lmeState.managerHighlightedCategoryKey = "drawings";
|
||||
document.querySelectorAll(".layer-manager-object").forEach(row => {
|
||||
row.classList.toggle("layer-manager-object-selected", row.dataset.objectId === record.id);
|
||||
});
|
||||
setSelectedDrawing(layer);
|
||||
renderManagerHighlight([record]);
|
||||
setInfo(`Selezione: ${managedObjectLabel(record, 0)}. Usa la matita per modificare o il cestino per eliminare.`);
|
||||
}
|
||||
|
||||
function bindManagedParentHighlight(row, key, records) {
|
||||
const summary = row.querySelector(":scope > summary");
|
||||
row.dataset.highlightKey = key;
|
||||
@@ -1691,6 +1874,7 @@ function highlightManagedParent(row, key, records) {
|
||||
lmeState.managerHighlightedParentKey = key;
|
||||
lmeState.managerHighlightedParentRecords = records;
|
||||
lmeState.managerHighlightedCategoryKey = records[0]?.categoryKey || null;
|
||||
setSelectedDrawing(null);
|
||||
row.classList.add("layer-manager-parent-selected");
|
||||
renderManagerHighlight(records);
|
||||
}
|
||||
@@ -1740,6 +1924,7 @@ function clearManagerHighlight() {
|
||||
lmeState.managerHighlightedParentRecords = null;
|
||||
lmeState.managerHoveredParentKey = null;
|
||||
lmeState.managerHoveredParentRecords = null;
|
||||
setSelectedDrawing(null);
|
||||
document.querySelectorAll(".layer-manager-object-selected").forEach(row => {
|
||||
row.classList.remove("layer-manager-object-selected");
|
||||
});
|
||||
@@ -2013,13 +2198,14 @@ function clearImported() {
|
||||
function addProjectMarker(name) {
|
||||
const center = lmeState.map.getCenter();
|
||||
const label = (name || "Nuovo POI").trim();
|
||||
const marker = L.marker(center, { pane: "drawingsPane", draggable: true }).addTo(lmeState.drawings);
|
||||
const marker = L.marker(center, { pane: "drawingsPane", draggable: false }).addTo(lmeState.drawings);
|
||||
marker.lmeManagerLabel = `POI: ${label}`;
|
||||
marker.feature = {
|
||||
type: "Feature",
|
||||
properties: { name: label }
|
||||
};
|
||||
marker.bindPopup(`<strong>${escapeHtml(label)}</strong><br>${center.lat.toFixed(6)}, ${center.lng.toFixed(6)}`).openPopup();
|
||||
bindDrawingLayerSelection(marker);
|
||||
refreshDrawingMeasurements();
|
||||
setInfo(`POI aggiunto: ${label}`);
|
||||
refreshLayerManager();
|
||||
@@ -2031,8 +2217,8 @@ function configureDrawLabels() {
|
||||
L.drawLocal.draw.toolbar.buttons.circle = "Misura cerchio";
|
||||
L.drawLocal.draw.toolbar.buttons.rectangle = "Disegna rettangolo";
|
||||
L.drawLocal.draw.toolbar.buttons.marker = "Aggiungi POI";
|
||||
L.drawLocal.edit.toolbar.buttons.edit = "Modifica disegni";
|
||||
L.drawLocal.edit.toolbar.buttons.remove = "Elimina disegni";
|
||||
L.drawLocal.edit.toolbar.buttons.edit = "Modifica oggetto selezionato";
|
||||
L.drawLocal.edit.toolbar.buttons.remove = "Elimina oggetto selezionato";
|
||||
L.drawLocal.draw.handlers.polygon.tooltip.start = "Tocca il primo vertice dell'area.";
|
||||
L.drawLocal.draw.handlers.polygon.tooltip.cont = "Tocca per aggiungere un vertice.";
|
||||
L.drawLocal.draw.handlers.polygon.tooltip.end = "Tocca il primo punto per chiudere l'area.";
|
||||
@@ -2060,6 +2246,7 @@ function bindDrawingEvents() {
|
||||
};
|
||||
setInfo(completedLabels[event.layerType] || "Disegno completato.");
|
||||
refreshLayerManager();
|
||||
selectDrawingLayer(event.layer);
|
||||
});
|
||||
lmeState.map.on(L.Draw.Event.EDITED, event => {
|
||||
event.layers.eachLayer(layer => annotateDrawingLayer(layer));
|
||||
@@ -2067,7 +2254,9 @@ function bindDrawingEvents() {
|
||||
updateGpsInsideState();
|
||||
refreshLayerManager();
|
||||
});
|
||||
lmeState.map.on(L.Draw.Event.DELETED, () => {
|
||||
lmeState.map.on(L.Draw.Event.DELETED, event => {
|
||||
event.layers?.eachLayer(layer => lmeState.drawings.removeLayer(layer));
|
||||
clearManagerHighlight();
|
||||
refreshDrawingMeasurements();
|
||||
updateGpsInsideState();
|
||||
refreshLayerManager();
|
||||
@@ -2085,10 +2274,28 @@ function enableDrawTools() {
|
||||
}
|
||||
|
||||
configureDrawLabels();
|
||||
const orangeArea = { pane: "drawingsPane", color: "#f97316", weight: 4, fillColor: "#fb923c", fillOpacity: 0.2 };
|
||||
const orangeArea = {
|
||||
pane: "drawingsPane",
|
||||
renderer: lmeState.drawingsRenderer,
|
||||
color: "#f97316",
|
||||
weight: 4,
|
||||
fillColor: "#fb923c",
|
||||
fillOpacity: 0.2
|
||||
};
|
||||
lmeState.drawControl = new L.Control.Draw({
|
||||
position: "topright",
|
||||
edit: { featureGroup: lmeState.drawings },
|
||||
edit: {
|
||||
featureGroup: lmeState.drawingSelection,
|
||||
remove: false,
|
||||
edit: {
|
||||
selectedPathOptions: {
|
||||
color: "#ff00d4",
|
||||
fillColor: "#00fff0",
|
||||
fillOpacity: 0.16,
|
||||
maintainColor: false
|
||||
}
|
||||
}
|
||||
},
|
||||
draw: {
|
||||
circle: { metric: true, feet: false, showRadius: true, shapeOptions: orangeArea },
|
||||
circlemarker: false,
|
||||
@@ -2104,14 +2311,72 @@ function enableDrawTools() {
|
||||
metric: true,
|
||||
feet: false,
|
||||
showLength: true,
|
||||
shapeOptions: { pane: "drawingsPane", color: "#f97316", weight: 4, opacity: 0.95 }
|
||||
shapeOptions: {
|
||||
pane: "drawingsPane",
|
||||
renderer: lmeState.drawingsRenderer,
|
||||
color: "#f97316",
|
||||
weight: 4,
|
||||
opacity: 0.95
|
||||
}
|
||||
},
|
||||
rectangle: { metric: true, shapeOptions: orangeArea }
|
||||
}
|
||||
});
|
||||
lmeState.map.addControl(lmeState.drawControl);
|
||||
installSelectedDrawingDeleteButton();
|
||||
bindDrawingEvents();
|
||||
setInfo("Barra strumenti attiva: misure, disegni, POI, modifica e cancellazione.");
|
||||
setInfo("Barra strumenti attiva: seleziona un disegno sulla mappa, poi usa matita o cestino.");
|
||||
}
|
||||
|
||||
function installSelectedDrawingDeleteButton() {
|
||||
const toolbar = drawingEditToolbar();
|
||||
const container = toolbar?._toolbarContainer;
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
const button = L.DomUtil.create("a", "leaflet-draw-edit-remove", container);
|
||||
button.href = "#";
|
||||
button.title = L.drawLocal.edit.toolbar.buttons.remove;
|
||||
button.setAttribute("role", "button");
|
||||
button.setAttribute("aria-label", L.drawLocal.edit.toolbar.buttons.remove);
|
||||
const accessibleLabel = L.DomUtil.create("span", "sr-only", button);
|
||||
accessibleLabel.textContent = L.drawLocal.edit.toolbar.buttons.remove;
|
||||
L.DomEvent.on(button, "click", L.DomEvent.stop)
|
||||
.on(button, "mousedown", L.DomEvent.stopPropagation)
|
||||
.on(button, "touchstart", L.DomEvent.stopPropagation)
|
||||
.on(button, "click", removeSelectedDrawing);
|
||||
lmeState.drawingDeleteButton = button;
|
||||
updateDrawingSelectionActions();
|
||||
}
|
||||
|
||||
function removeSelectedDrawing() {
|
||||
const layer = lmeState.drawingSelection?.getLayers?.()[0];
|
||||
if (!layer) {
|
||||
setInfo("Seleziona prima il disegno da eliminare toccandolo sulla mappa.");
|
||||
return;
|
||||
}
|
||||
if (drawingEditActive()) {
|
||||
setInfo("Salva o annulla la modifica prima di eliminare il disegno.");
|
||||
return;
|
||||
}
|
||||
const record = {
|
||||
id: managedLayerId(layer, "drawings"),
|
||||
categoryKey: "drawings",
|
||||
layer,
|
||||
parent: lmeState.drawings
|
||||
};
|
||||
const name = managedObjectLabel(record, 0);
|
||||
if (!window.confirm(`Eliminare solo '${name}' dal progetto?`)) {
|
||||
return;
|
||||
}
|
||||
lmeState.drawingSelection.clearLayers();
|
||||
lmeState.drawings.removeLayer(layer);
|
||||
clearManagedObjectState(record.id);
|
||||
clearManagerHighlight();
|
||||
refreshDrawingMeasurements();
|
||||
updateGpsInsideState();
|
||||
refreshLayerManager();
|
||||
setInfo(`'${name}' eliminato dal progetto.`);
|
||||
}
|
||||
|
||||
function startMeasurementDrawing(mode) {
|
||||
@@ -2129,19 +2394,39 @@ function startMeasurementDrawing(mode) {
|
||||
showArea: true,
|
||||
metric: true,
|
||||
feet: false,
|
||||
shapeOptions: { pane: "drawingsPane", color: "#f97316", weight: 4, fillColor: "#fb923c", fillOpacity: 0.2 }
|
||||
shapeOptions: {
|
||||
pane: "drawingsPane",
|
||||
renderer: lmeState.drawingsRenderer,
|
||||
color: "#f97316",
|
||||
weight: 4,
|
||||
fillColor: "#fb923c",
|
||||
fillOpacity: 0.2
|
||||
}
|
||||
}),
|
||||
distance: () => new L.Draw.Polyline(lmeState.map, {
|
||||
metric: true,
|
||||
feet: false,
|
||||
showLength: true,
|
||||
shapeOptions: { pane: "drawingsPane", color: "#f97316", weight: 4, opacity: 0.95 }
|
||||
shapeOptions: {
|
||||
pane: "drawingsPane",
|
||||
renderer: lmeState.drawingsRenderer,
|
||||
color: "#f97316",
|
||||
weight: 4,
|
||||
opacity: 0.95
|
||||
}
|
||||
}),
|
||||
circle: () => new L.Draw.Circle(lmeState.map, {
|
||||
metric: true,
|
||||
feet: false,
|
||||
showRadius: true,
|
||||
shapeOptions: { pane: "drawingsPane", color: "#f97316", weight: 4, fillColor: "#fb923c", fillOpacity: 0.2 }
|
||||
shapeOptions: {
|
||||
pane: "drawingsPane",
|
||||
renderer: lmeState.drawingsRenderer,
|
||||
color: "#f97316",
|
||||
weight: 4,
|
||||
fillColor: "#fb923c",
|
||||
fillOpacity: 0.2
|
||||
}
|
||||
})
|
||||
};
|
||||
const createHandler = handlers[mode];
|
||||
@@ -2162,6 +2447,9 @@ function startMeasurementDrawing(mode) {
|
||||
|
||||
function annotateDrawingLayer(layer, layerType) {
|
||||
layer.options.pane = "drawingsPane";
|
||||
if (layer instanceof L.Path) {
|
||||
layer.options.renderer = lmeState.drawingsRenderer;
|
||||
}
|
||||
layer.feature = layer.feature || { type: "Feature", properties: {} };
|
||||
layer.feature.properties = layer.feature.properties || {};
|
||||
if (layer instanceof L.Circle) {
|
||||
@@ -2171,6 +2459,25 @@ function annotateDrawingLayer(layer, layerType) {
|
||||
layer.feature.properties._lmeShape = layerType;
|
||||
}
|
||||
layer.lmeManagerLabel = drawingLayerLabel(layer, layerType);
|
||||
bindDrawingLayerSelection(layer);
|
||||
}
|
||||
|
||||
function bindDrawingLayerSelection(layer) {
|
||||
if (layer.lmeDrawingSelectionBound) {
|
||||
return;
|
||||
}
|
||||
layer.lmeDrawingSelectionBound = true;
|
||||
layer.on("click", event => {
|
||||
if (drawingEditActive()) {
|
||||
return;
|
||||
}
|
||||
lmeState.ignoreMapClickUntil = Date.now() + 1000;
|
||||
if (event.originalEvent) {
|
||||
event.originalEvent._lmeDrawingSelected = true;
|
||||
L.DomEvent.stopPropagation(event.originalEvent);
|
||||
}
|
||||
selectDrawingLayer(layer);
|
||||
});
|
||||
}
|
||||
|
||||
function setDrawToolsVisible(visible) {
|
||||
@@ -2190,6 +2497,7 @@ function setDrawToolsVisible(visible) {
|
||||
});
|
||||
lmeState.map.removeControl(lmeState.drawControl);
|
||||
lmeState.drawControl = null;
|
||||
lmeState.drawingDeleteButton = null;
|
||||
setInfo("Strumenti di disegno nascosti.");
|
||||
}
|
||||
|
||||
@@ -2204,6 +2512,9 @@ function clearDrawings() {
|
||||
return;
|
||||
}
|
||||
|
||||
drawingEditToolbar()?.disable?.();
|
||||
lmeState.drawingSelection?.clearLayers();
|
||||
clearManagerHighlight();
|
||||
lmeState.drawings.clearLayers();
|
||||
clearManagedCategoryState("drawings");
|
||||
lmeState.drawingMeasurements.clearLayers();
|
||||
@@ -2586,12 +2897,18 @@ function loadProjectSnapshot(raw, projectName) {
|
||||
|
||||
const restoredDrawings = L.geoJSON(data.drawings || emptyFeatureCollection(), {
|
||||
pane: "drawingsPane",
|
||||
style: { color: "#f97316", weight: 3, fillOpacity: 0.16 },
|
||||
style: {
|
||||
renderer: lmeState.drawingsRenderer,
|
||||
color: "#f97316",
|
||||
weight: 3,
|
||||
fillOpacity: 0.16
|
||||
},
|
||||
pointToLayer: (feature, latlng) => {
|
||||
const properties = feature?.properties || {};
|
||||
if (properties._lmeShape === "circle" && Number(properties._lmeRadius) > 0) {
|
||||
return L.circle(latlng, {
|
||||
pane: "drawingsPane",
|
||||
renderer: lmeState.drawingsRenderer,
|
||||
radius: Number(properties._lmeRadius),
|
||||
color: "#f97316",
|
||||
weight: 3,
|
||||
@@ -2599,7 +2916,7 @@ function loadProjectSnapshot(raw, projectName) {
|
||||
fillOpacity: 0.2
|
||||
});
|
||||
}
|
||||
return L.marker(latlng, { pane: "drawingsPane", draggable: true });
|
||||
return L.marker(latlng, { pane: "drawingsPane", draggable: false });
|
||||
},
|
||||
onEachFeature: (feature, layer) => {
|
||||
bindStoredFeaturePopup(feature, layer);
|
||||
@@ -2695,6 +3012,7 @@ function captureDisplayOptions() {
|
||||
layerOrder: [...lmeState.layerOrder],
|
||||
layerObjectOrder: { ...lmeState.managerObjectOrder },
|
||||
layerCategoryNames: { ...lmeState.managerCategoryNames },
|
||||
removedLayerCategories: [...lmeState.managerRemovedCategories],
|
||||
hiddenLayerObjects: [...lmeState.managerHiddenObjectIds],
|
||||
hiddenLayerMeasurementSummaries: [...lmeState.managerHiddenMeasurementSummaryIds],
|
||||
hiddenLayerMeasurementSides: [...lmeState.managerHiddenMeasurementSideIds],
|
||||
@@ -2734,6 +3052,9 @@ function applyProjectDisplayOptions(options) {
|
||||
if (options.layerCategoryNames && typeof options.layerCategoryNames === "object") {
|
||||
lmeState.managerCategoryNames = { ...options.layerCategoryNames };
|
||||
}
|
||||
if (Array.isArray(options.removedLayerCategories)) {
|
||||
lmeState.managerRemovedCategories = new Set(options.removedLayerCategories);
|
||||
}
|
||||
if (Array.isArray(options.hiddenLayerObjects)) {
|
||||
lmeState.managerHiddenObjectIds = new Set(options.hiddenLayerObjects);
|
||||
}
|
||||
@@ -2830,11 +3151,14 @@ function managedGroupGeoJson(group, categoryKey) {
|
||||
}
|
||||
|
||||
function resetLayerManagerProjectState() {
|
||||
drawingEditToolbar()?.disable?.();
|
||||
lmeState.drawingSelection?.clearLayers();
|
||||
lmeState.managerHiddenObjects.clear();
|
||||
lmeState.managerHiddenObjectIds.clear();
|
||||
lmeState.managerHiddenMeasurementSummaryIds.clear();
|
||||
lmeState.managerHiddenMeasurementSideIds.clear();
|
||||
lmeState.managerHiddenCategories.clear();
|
||||
lmeState.managerRemovedCategories.clear();
|
||||
lmeState.managerObjectOrder = {};
|
||||
lmeState.managerCategoryNames = {};
|
||||
clearManagerHighlight();
|
||||
|
||||
Reference in new issue
Block a user