Done. The Selected Block editor now uses grid-style coordinate dropdowns in [Edit.cshtml (line 423)](C:/Source/PlotLine/PlotLine/Views/FloorPlans/Edit.cshtml:423).

What changed:
X is now a dropdown showing A, B, C, AA, etc., while still saving 0, 1, 2, etc.
Y is now a dropdown showing 1, 2, 3, etc., while still saving 0, 1, 2, etc.
Options are limited to valid positions for the active floor and current block size.
Dragging, manual dropdown changes, block resizing, and grid resizing all keep the controls synced through the existing block write/clamp path.
Verification: dotnet build PlotLine.slnx passes with 0 warnings and 0 errors. No database, rendering model, transition logic, or AJAX architecture changes.
This commit is contained in:
Nick Beckley 2026-06-22 14:43:09 +01:00
parent 7874740f42
commit 92ab8f3202

View File

@ -423,11 +423,21 @@
<div class="row g-2">
<div class="col-6">
<label class="form-label">X</label>
<input asp-for="Blocks[i].X" class="form-control form-control-sm" min="0" max="79" data-block-field="x" />
<select class="form-select form-select-sm"
id="Blocks_@(i)__X"
name="Blocks[@i].X"
data-block-field="x"
data-coordinate-axis="x"
data-coordinate-value="@Model.Blocks[i].X"></select>
</div>
<div class="col-6">
<label class="form-label">Y</label>
<input asp-for="Blocks[i].Y" class="form-control form-control-sm" min="0" max="79" data-block-field="y" />
<select class="form-select form-select-sm"
id="Blocks_@(i)__Y"
name="Blocks[@i].Y"
data-block-field="y"
data-coordinate-axis="y"
data-coordinate-value="@Model.Blocks[i].Y"></select>
</div>
<div class="col-6">
<label class="form-label">Width</label>
@ -1167,6 +1177,23 @@
return label;
}
function populateCoordinateSelect(select, count, labelForValue, selectedValue) {
if (!select) return;
const selected = String(Math.max(0, Math.min(Number(selectedValue || 0), Math.max(count - 1, 0))));
const existingValues = [...select.options].map(option => option.value).join("|");
const nextValues = Array.from({ length: Math.max(count, 1) }, (_, index) => String(index)).join("|");
if (existingValues !== nextValues) {
select.replaceChildren(...Array.from({ length: Math.max(count, 1) }, (_, index) => {
const option = document.createElement("option");
option.value = String(index);
option.textContent = labelForValue(index);
return option;
}));
}
select.value = selected;
select.dataset.coordinateValue = selected;
}
function refreshGridCoordinateLabels() {
const show = gridCoordinatesToggle?.checked === true;
panels.forEach(panel => {
@ -1665,11 +1692,16 @@
const wrap = document.createElement("div");
wrap.className = "col-6";
wrap.innerHTML = `<label class="form-label">${label}</label>`;
const input = document.createElement("input");
input.className = "form-control form-control-sm";
input.type = "number";
input.min = field === "x" || field === "y" ? "0" : "1";
input.max = field === "x" || field === "y" ? "79" : "80";
const input = field === "x" || field === "y" ? document.createElement("select") : document.createElement("input");
input.className = field === "x" || field === "y" ? "form-select form-select-sm" : "form-control form-control-sm";
if (input instanceof HTMLInputElement) {
input.type = "number";
input.min = "1";
input.max = "80";
} else {
input.dataset.coordinateAxis = field;
input.dataset.coordinateValue = value;
}
input.name = `Blocks[${index}].${property}`;
input.value = value;
input.dataset.blockField = field;
@ -1738,6 +1770,7 @@
blocks.push(button);
attachBlockEvents(button);
createBlockEditor(block);
writeBlock(button, readBlock(button));
checkOverlaps();
return button;
}
@ -1818,8 +1851,8 @@
function readBlock(block) {
const fields = fieldsFor(block);
return {
x: Number(fields.x?.value || block.style.getPropertyValue("--x") || 0),
y: Number(fields.y?.value || block.style.getPropertyValue("--y") || 0),
x: Number(fields.x?.value || fields.x?.dataset.coordinateValue || block.style.getPropertyValue("--x") || 0),
y: Number(fields.y?.value || fields.y?.dataset.coordinateValue || block.style.getPropertyValue("--y") || 0),
w: Number(fields.w?.value || block.style.getPropertyValue("--w") || 1),
h: Number(fields.h?.value || block.style.getPropertyValue("--h") || 1)
};
@ -1827,13 +1860,18 @@
function writeBlock(block, values) {
const next = clampBlock(block, values);
const grid = gridFor(block);
const gridWidth = Number(grid?.dataset.gridWidth || 24);
const gridHeight = Number(grid?.dataset.gridHeight || 16);
const maxXOptions = Math.max(gridWidth - next.w + 1, 1);
const maxYOptions = Math.max(gridHeight - next.h + 1, 1);
block.style.setProperty("--x", next.x);
block.style.setProperty("--y", next.y);
block.style.setProperty("--w", next.w);
block.style.setProperty("--h", next.h);
const fields = fieldsFor(block);
if (fields.x) fields.x.value = next.x;
if (fields.y) fields.y.value = next.y;
populateCoordinateSelect(fields.x, maxXOptions, gridColumnLabel, next.x);
populateCoordinateSelect(fields.y, maxYOptions, value => String(value + 1), next.y);
if (fields.w) fields.w.value = next.w;
if (fields.h) fields.h.value = next.h;
scheduleGeometryRefresh();
@ -2467,7 +2505,10 @@
saveLayoutAsync();
});
blocks.forEach(attachBlockEvents);
blocks.forEach(block => {
attachBlockEvents(block);
writeBlock(block, readBlock(block));
});
editor.addEventListener("pointermove", event => {
if (!pointerState) return;