Replace row/col number inputs with graphical grid cell picker

Click a cell in the visual grid to select it. Cell images shown where
available. Selected cell highlighted with accent border.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-22 01:16:40 +00:00
parent 488fa7ff53
commit 64af0862f2
3 changed files with 74 additions and 19 deletions

View File

@@ -1625,8 +1625,9 @@ function open_inventory_dialog(entry = null, default_component_id = null, defaul
const new_comp_btn = qs(dlg, '#i-new-component');
const grid_row_div = qs(dlg, '#i-grid-row');
const grid_sel = qs(dlg, '#i-grid-select');
const row_num_input = qs(dlg, '#i-grid-row-num');
const col_num_input = qs(dlg, '#i-grid-col-num');
const grid_visual = qs(dlg, '#i-grid-visual');
let picker_row = null;
let picker_col = null;
title.textContent = entry ? 'Edit inventory entry' : 'Add inventory entry';
@@ -1659,8 +1660,44 @@ function open_inventory_dialog(entry = null, default_component_id = null, defaul
}))
);
if (effective_grid_cell?.grid_id) { grid_sel.value = effective_grid_cell.grid_id; }
row_num_input.value = effective_grid_cell?.grid_row != null ? effective_grid_cell.grid_row + 1 : 1;
col_num_input.value = effective_grid_cell?.grid_col != null ? effective_grid_cell.grid_col + 1 : 1;
picker_row = effective_grid_cell?.grid_row ?? null;
picker_col = effective_grid_cell?.grid_col ?? null;
function rebuild_grid_visual() {
const grid = all_grids.find(g => g.id === grid_sel.value);
if (!grid) { grid_visual.replaceChildren(); return; }
grid_visual.style.gridTemplateColumns = `repeat(${grid.cols}, 1fr)`;
const cells = [];
for (let r = 0; r < grid.rows; r++) {
for (let c = 0; c < grid.cols; c++) {
const cell = document.createElement('div');
cell.className = 'igv-cell';
if (r === picker_row && c === picker_col) cell.classList.add('igv-selected');
const filename = grid.cells?.[r]?.[c];
if (filename) {
const img = document.createElement('img');
img.src = `/img/${filename}`;
img.className = 'igv-img';
cell.appendChild(img);
}
cell.addEventListener('click', () => {
picker_row = r;
picker_col = c;
grid_visual.querySelectorAll('.igv-cell').forEach(el => el.classList.remove('igv-selected'));
cell.classList.add('igv-selected');
});
cells.push(cell);
}
}
grid_visual.replaceChildren(...cells);
}
rebuild_grid_visual();
const old_grid_handler = grid_sel._change_handler;
if (old_grid_handler) grid_sel.removeEventListener('change', old_grid_handler);
grid_sel._change_handler = () => { picker_row = null; picker_col = null; rebuild_grid_visual(); };
grid_sel.addEventListener('change', grid_sel._change_handler);
function update_ref_label() {
ref_label.textContent = ref_label_for_type(type_sel.value);
@@ -1700,8 +1737,8 @@ function open_inventory_dialog(entry = null, default_component_id = null, defaul
quantity: qty_input.value.trim(),
notes: notes_input.value.trim(),
grid_id: is_grid ? (grid_sel.value || null) : null,
grid_row: is_grid ? parseInt(row_num_input.value) - 1 : null,
grid_col: is_grid ? parseInt(col_num_input.value) - 1 : null,
grid_row: is_grid ? picker_row : null,
grid_col: is_grid ? picker_col : null,
};
if (entry) {
const result = await api.update_inventory(entry.id, body);

View File

@@ -1504,15 +1504,38 @@ nav {
gap: 0.4rem;
}
.i-grid-coords {
display: flex;
align-items: center;
gap: 0.4rem;
font-size: 0.9rem;
.i-grid-visual {
display: grid;
gap: 2px;
max-height: 320px;
overflow-y: auto;
}
.input-narrow {
width: 4rem;
.igv-cell {
position: relative;
aspect-ratio: 1;
background: var(--surface-raised);
border: 2px solid transparent;
border-radius: 2px;
cursor: pointer;
overflow: hidden;
}
.igv-cell:hover {
border-color: var(--accent);
}
.igv-cell.igv-selected {
border-color: var(--accent);
box-shadow: inset 0 0 0 1px var(--accent);
}
.igv-img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
pointer-events: none;
}
/* ===== CELL INVENTORY OVERLAY ===== */

View File

@@ -444,12 +444,7 @@
<label>Grid cell</label>
<div class="i-grid-cell-picker">
<select id="i-grid-select" class="filter-select"></select>
<div class="i-grid-coords">
<label for="i-grid-row-num">Row</label>
<input type="number" id="i-grid-row-num" min="1" class="input-narrow">
<label for="i-grid-col-num">Col</label>
<input type="number" id="i-grid-col-num" min="1" class="input-narrow">
</div>
<div id="i-grid-visual" class="i-grid-visual"></div>
</div>
</div>
<div class="form-row">