diff --git a/LMEMappeCatastaliApp.csproj b/LMEMappeCatastaliApp.csproj index 9f6e8a3..11bbc75 100644 --- a/LMEMappeCatastaliApp.csproj +++ b/LMEMappeCatastaliApp.csproj @@ -28,8 +28,8 @@ com.companyname.lmemappecatastaliapp - 0.2.24 - 28 + 0.2.25 + 29 None diff --git a/wwwroot/css/app.css b/wwwroot/css/app.css index 8dae8a3..0848879 100644 --- a/wwwroot/css/app.css +++ b/wwwroot/css/app.css @@ -178,7 +178,7 @@ input[type="checkbox"] { right: 12px; z-index: 940; display: grid; - width: min(320px, calc(100% - 24px)); + width: min(760px, 62vw, calc(100% - 24px)); max-height: calc(100% - 152px); overflow-y: auto; border: 1px solid var(--line); @@ -283,11 +283,7 @@ 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 { +.layer-manager-with-actions > summary { grid-template-columns: 28px 20px minmax(0, 1fr) auto auto auto; } @@ -310,7 +306,15 @@ input[type="checkbox"] { cursor: grab; } -.layer-rename-button { +.layer-row-actions { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: 4px; +} + +.layer-rename-button, +.layer-remove-button { min-height: 28px; padding: 0 8px; border: 1px solid #bfd4cd; @@ -325,6 +329,16 @@ input[type="checkbox"] { background: #d9eee7; } +.layer-remove-button { + border-color: #fecaca; + background: #fff1f2; + color: #b42318; +} + +.layer-remove-button:active { + background: #ffe4e6; +} + .layer-move-buttons { display: inline-grid; grid-template-columns: 24px 24px; @@ -918,10 +932,31 @@ footer { .map-layer-panel { top: 68px; right: 8px; - width: min(320px, calc(100% - 16px)); + width: calc(100% - 16px); max-height: calc(100% - 144px); } + .layer-manager-with-actions > summary { + grid-template-columns: 28px 20px minmax(0, 1fr) auto; + } + + .layer-manager-with-actions > summary > .layer-row-actions { + grid-row: 2; + grid-column: 3; + justify-content: flex-start; + } + + .layer-manager-with-actions > summary > .layer-move-buttons { + grid-row: 1; + grid-column: 4; + } + + .layer-manager-with-actions > summary > .layer-manager-count { + grid-row: 2; + grid-column: 4; + justify-self: end; + } + .tool-sheet { top: 10%; bottom: var(--nav-height); @@ -980,6 +1015,12 @@ footer { } } +@media (min-width: 701px) and (max-width: 1100px) { + .map-layer-panel { + width: min(680px, calc(100% - 24px)); + } +} + @media (max-width: 380px) { .app-identity span { display: none; diff --git a/wwwroot/js/lme-map.js b/wwwroot/js/lme-map.js index 390b940..9c0e7f3 100644 --- a/wwwroot/js/lme-map.js +++ b/wwwroot/js/lme-map.js @@ -61,6 +61,7 @@ const lmeState = { managerHiddenMeasurementSummaryIds: new Set(), managerHiddenMeasurementSideIds: new Set(), managerHiddenCategories: new Set(), + managerRemovedCategories: new Set(), managerObjectOrder: {}, managerCategoryNames: {}, managerExpandedRows: new Set(), @@ -1251,6 +1252,60 @@ 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 `${renameButton}${removeButton}`; +} + +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); + } + lmeState.managerHiddenObjects.delete(record.id); + lmeState.managerHiddenObjectIds.delete(record.id); + lmeState.managerHiddenMeasurementSummaryIds.delete(record.id); + lmeState.managerHiddenMeasurementSideIds.delete(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 +1361,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) => @@ -1334,12 +1394,16 @@ function renderLayerManagerCategory(category, records) { const renameButton = managedCategoryCanRename(category) ? `` : ""; - group.classList.toggle("layer-manager-renamable", Boolean(renameButton)); + const removeButton = managedCategoryCanRemove(category, records) + ? `` + : ""; + 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 = `${escapeHtml(categoryName)}${renameButton}${layerManagerMoveButtons("gruppo")}${records.length}
`; + group.innerHTML = `${escapeHtml(categoryName)}${rowActions}${layerManagerMoveButtons("gruppo")}${records.length}
`; const checkbox = group.querySelector("input"); checkbox.indeterminate = categoryVisible && records.length > 0 && visibleCount < records.length; checkbox.addEventListener("click", event => event.stopPropagation()); @@ -1349,6 +1413,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) { @@ -1375,8 +1444,11 @@ function renderLayerManagerBlock(category, source, records) { 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 = `${escapeHtml(blockName)}${layerManagerMoveButtons("blocco")}${records.length}
`; + const rowActions = layerManagerRowActions( + ``, + ``); + block.classList.add("layer-manager-with-actions"); + block.innerHTML = `${escapeHtml(blockName)}${rowActions}${layerManagerMoveButtons("blocco")}${records.length}
`; const checkbox = block.querySelector("input"); checkbox.indeterminate = visibleCount > 0 && !visible; checkbox.addEventListener("click", event => event.stopPropagation()); @@ -1389,6 +1461,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))); @@ -1408,7 +1485,13 @@ function renderLayerManagerObject(record, index) { const renameButton = managedObjectCanRename(record) ? `` : ""; - object.innerHTML = `${escapeHtml(managedObjectLabel(record, index))}${renameButton}${layerManagerMoveButtons("oggetto")}›
`; + const removeButton = managedObjectCanRemove(record) + ? `` + : ""; + const rowActions = layerManagerRowActions(renameButton, removeButton); + object.classList.toggle("layer-manager-with-actions", Boolean(rowActions)); + const objectName = managedObjectLabel(record, index); + object.innerHTML = `${escapeHtml(objectName)}${rowActions}${layerManagerMoveButtons("oggetto")}›
`; const checkbox = object.querySelector("input"); checkbox.addEventListener("click", event => event.stopPropagation()); checkbox.addEventListener("change", () => setManagedObjectVisible(record, checkbox.checked, true)); @@ -1417,6 +1500,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(); @@ -2695,6 +2783,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 +2823,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); } @@ -2835,6 +2927,7 @@ function resetLayerManagerProjectState() { lmeState.managerHiddenMeasurementSummaryIds.clear(); lmeState.managerHiddenMeasurementSideIds.clear(); lmeState.managerHiddenCategories.clear(); + lmeState.managerRemovedCategories.clear(); lmeState.managerObjectOrder = {}; lmeState.managerCategoryNames = {}; clearManagerHighlight();