Make bin editor open to image view; corners editing on demand
- Default view: processed image (full size in dialog), or placeholder if not yet processed. Name and type selector always visible at top. - "Adjust corners…" button reveals the canvas editor (lazy-loaded on first click, so there's no canvas allocation cost on open). - "← Back to image" returns to the image view. - Corners are only re-processed on Save if the canvas was opened. - Tabs reduced to Fields | Contents (Corners is no longer a tab). - Added preview image CSS; btn-link style for the back button. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
102
public/app.mjs
102
public/app.mjs
@@ -2359,6 +2359,8 @@ function open_bin_editor(bin) {
|
|||||||
if (!dlg) return;
|
if (!dlg) return;
|
||||||
|
|
||||||
bin_editor_bin_id = bin.id;
|
bin_editor_bin_id = bin.id;
|
||||||
|
bin_editor_instance = null; // canvas not loaded until needed
|
||||||
|
|
||||||
document.getElementById('bin-editor-name').value = bin.name;
|
document.getElementById('bin-editor-name').value = bin.name;
|
||||||
|
|
||||||
// Populate type selector
|
// Populate type selector
|
||||||
@@ -2385,42 +2387,22 @@ function open_bin_editor(bin) {
|
|||||||
type_sel.addEventListener('change', sync_dims_row);
|
type_sel.addEventListener('change', sync_dims_row);
|
||||||
sync_dims_row();
|
sync_dims_row();
|
||||||
|
|
||||||
// Tabs
|
// Image preview (default view)
|
||||||
let active_tab = 'corners';
|
const view_image = document.getElementById('bin-editor-view-image');
|
||||||
const tab_panels = {
|
const view_corners = document.getElementById('bin-editor-view-corners');
|
||||||
corners: document.getElementById('bin-editor-tab-corners'),
|
const preview_img = document.getElementById('bin-editor-preview');
|
||||||
fields: document.getElementById('bin-editor-tab-fields'),
|
const no_image_el = document.getElementById('bin-editor-no-image');
|
||||||
contents: document.getElementById('bin-editor-tab-contents'),
|
|
||||||
};
|
function show_image_view() {
|
||||||
qs(dlg, '#bin-editor-tabs').onclick = (e) => {
|
view_image.hidden = false;
|
||||||
const tab = e.target.dataset.tab;
|
view_corners.hidden = true;
|
||||||
if (!tab) return;
|
|
||||||
active_tab = tab;
|
|
||||||
qs(dlg, '#bin-editor-tabs').querySelectorAll('.tab-btn').forEach(btn => {
|
|
||||||
btn.classList.toggle('active', btn.dataset.tab === tab);
|
|
||||||
});
|
|
||||||
for (const [name, el] of Object.entries(tab_panels)) {
|
|
||||||
el.hidden = name !== tab;
|
|
||||||
}
|
}
|
||||||
const save_btn = document.getElementById('bin-editor-save');
|
|
||||||
save_btn.textContent = active_tab === 'corners' ? 'Save & process' : 'Save';
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fields tab
|
|
||||||
bin_editor_get_fields = build_field_editor(
|
|
||||||
document.getElementById('bin-field-rows'),
|
|
||||||
document.getElementById('bin-add-field-select'),
|
|
||||||
document.getElementById('bin-new-field'),
|
|
||||||
bin.fields ?? {}
|
|
||||||
).get_fields;
|
|
||||||
|
|
||||||
// Contents tab
|
|
||||||
render_bin_contents(bin.id, document.getElementById('bin-contents-list'));
|
|
||||||
document.getElementById('bin-add-content').onclick = () => open_bin_content_dialog(bin.id);
|
|
||||||
|
|
||||||
// Show dialog first so the canvas has correct layout dimensions
|
|
||||||
dlg.showModal();
|
|
||||||
|
|
||||||
|
function show_corners_view() {
|
||||||
|
view_image.hidden = true;
|
||||||
|
view_corners.hidden = false;
|
||||||
|
// Lazy-load canvas the first time
|
||||||
|
if (!bin_editor_instance) {
|
||||||
const canvas = document.getElementById('bin-editor-canvas');
|
const canvas = document.getElementById('bin-editor-canvas');
|
||||||
bin_editor_instance = new Grid_Setup(canvas);
|
bin_editor_instance = new Grid_Setup(canvas);
|
||||||
bin_editor_instance.set_rows(1);
|
bin_editor_instance.set_rows(1);
|
||||||
@@ -2430,6 +2412,55 @@ function open_bin_editor(bin) {
|
|||||||
bin_editor_instance.set_corners(bin.corners);
|
bin_editor_instance.set_corners(bin.corners);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bin.image_filename) {
|
||||||
|
preview_img.src = `/img/${bin.image_filename}`;
|
||||||
|
preview_img.hidden = false;
|
||||||
|
no_image_el.hidden = true;
|
||||||
|
} else {
|
||||||
|
preview_img.hidden = true;
|
||||||
|
no_image_el.hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('bin-editor-go-corners').onclick = () => {
|
||||||
|
dlg.showModal(); // already open; ensures layout is stable before canvas sizes
|
||||||
|
show_corners_view();
|
||||||
|
};
|
||||||
|
document.getElementById('bin-editor-go-back').onclick = show_image_view;
|
||||||
|
|
||||||
|
show_image_view();
|
||||||
|
|
||||||
|
// Tabs: Fields | Contents
|
||||||
|
const tab_panels = {
|
||||||
|
fields: document.getElementById('bin-editor-tab-fields'),
|
||||||
|
contents: document.getElementById('bin-editor-tab-contents'),
|
||||||
|
};
|
||||||
|
qs(dlg, '#bin-editor-tabs').onclick = (e) => {
|
||||||
|
const tab = e.target.dataset.tab;
|
||||||
|
if (!tab) return;
|
||||||
|
qs(dlg, '#bin-editor-tabs').querySelectorAll('.tab-btn').forEach(btn => {
|
||||||
|
btn.classList.toggle('active', btn.dataset.tab === tab);
|
||||||
|
});
|
||||||
|
for (const [name, el] of Object.entries(tab_panels)) {
|
||||||
|
el.hidden = name !== tab;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fields
|
||||||
|
bin_editor_get_fields = build_field_editor(
|
||||||
|
document.getElementById('bin-field-rows'),
|
||||||
|
document.getElementById('bin-add-field-select'),
|
||||||
|
document.getElementById('bin-new-field'),
|
||||||
|
bin.fields ?? {}
|
||||||
|
).get_fields;
|
||||||
|
|
||||||
|
// Contents
|
||||||
|
render_bin_contents(bin.id, document.getElementById('bin-contents-list'));
|
||||||
|
document.getElementById('bin-add-content').onclick = () => open_bin_content_dialog(bin.id);
|
||||||
|
|
||||||
|
dlg.showModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -2678,10 +2709,9 @@ async function init() {
|
|||||||
const type_id = document.getElementById('bin-editor-type').value || null;
|
const type_id = document.getElementById('bin-editor-type').value || null;
|
||||||
const fields = bin_editor_get_fields?.() ?? {};
|
const fields = bin_editor_get_fields?.() ?? {};
|
||||||
try {
|
try {
|
||||||
// Always save name, type, and fields
|
|
||||||
const corners = bin_editor_instance?.get_corners();
|
const corners = bin_editor_instance?.get_corners();
|
||||||
if (corners) {
|
if (corners) {
|
||||||
// Corners tab active (or image loaded) — also re-process
|
// Canvas was opened — re-process corners too
|
||||||
const phys_w = parseFloat(document.getElementById('bin-editor-width').value) || null;
|
const phys_w = parseFloat(document.getElementById('bin-editor-width').value) || null;
|
||||||
const phys_h = parseFloat(document.getElementById('bin-editor-height').value) || null;
|
const phys_h = parseFloat(document.getElementById('bin-editor-height').value) || null;
|
||||||
await api.update_bin(id, { name, type_id, fields });
|
await api.update_bin(id, { name, type_id, fields });
|
||||||
|
|||||||
@@ -1988,6 +1988,46 @@ nav {
|
|||||||
margin-left: 0.25rem;
|
margin-left: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bin-editor-preview-wrap {
|
||||||
|
width: 100%;
|
||||||
|
background: #0e0e0e;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 120px;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bin-editor-preview-img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 55vh;
|
||||||
|
display: block;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bin-editor-no-image {
|
||||||
|
color: var(--text-faint);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-link {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 0.25rem 0;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-link:hover {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
.bin-editor-canvas {
|
.bin-editor-canvas {
|
||||||
display: block;
|
display: block;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|||||||
@@ -671,14 +671,7 @@
|
|||||||
<template id="t-dialog-bin-editor">
|
<template id="t-dialog-bin-editor">
|
||||||
<dialog id="dialog-bin-editor" class="app-dialog app-dialog-wide">
|
<dialog id="dialog-bin-editor" class="app-dialog app-dialog-wide">
|
||||||
<h2 class="dialog-title">Edit bin</h2>
|
<h2 class="dialog-title">Edit bin</h2>
|
||||||
<div class="tab-bar" id="bin-editor-tabs">
|
|
||||||
<button class="tab-btn active" data-tab="corners">Corners</button>
|
|
||||||
<button class="tab-btn" data-tab="fields">Fields</button>
|
|
||||||
<button class="tab-btn" data-tab="contents">Contents</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Corners tab -->
|
|
||||||
<div id="bin-editor-tab-corners">
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label>Name</label>
|
<label>Name</label>
|
||||||
<input type="text" id="bin-editor-name" autocomplete="off">
|
<input type="text" id="bin-editor-name" autocomplete="off">
|
||||||
@@ -689,6 +682,18 @@
|
|||||||
<option value="">— Custom —</option>
|
<option value="">— Custom —</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Default view: processed image -->
|
||||||
|
<div id="bin-editor-view-image">
|
||||||
|
<div class="bin-editor-preview-wrap">
|
||||||
|
<img id="bin-editor-preview" class="bin-editor-preview-img" alt="" hidden>
|
||||||
|
<div id="bin-editor-no-image" class="bin-editor-no-image">Not yet processed — click "Adjust corners" to set up</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" id="bin-editor-go-corners">Adjust corners…</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Corners canvas (revealed on demand) -->
|
||||||
|
<div id="bin-editor-view-corners" hidden>
|
||||||
<div class="form-row" id="bin-editor-dims-row">
|
<div class="form-row" id="bin-editor-dims-row">
|
||||||
<label>Dimensions (mm)</label>
|
<label>Dimensions (mm)</label>
|
||||||
<div class="bin-editor-dims">
|
<div class="bin-editor-dims">
|
||||||
@@ -699,11 +704,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<canvas id="bin-editor-canvas" class="bin-editor-canvas"></canvas>
|
<canvas id="bin-editor-canvas" class="bin-editor-canvas"></canvas>
|
||||||
|
<button type="button" class="btn btn-link btn-sm" id="bin-editor-go-back">← Back to image</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Fields tab -->
|
<!-- Tabs: Fields | Contents -->
|
||||||
<div id="bin-editor-tab-fields" hidden>
|
<div class="tab-bar" id="bin-editor-tabs">
|
||||||
<div class="form-section-label">Field values</div>
|
<button class="tab-btn active" data-tab="fields">Fields</button>
|
||||||
|
<button class="tab-btn" data-tab="contents">Contents</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="bin-editor-tab-fields">
|
||||||
<div id="bin-field-rows"></div>
|
<div id="bin-field-rows"></div>
|
||||||
<div class="form-row add-field-row">
|
<div class="form-row add-field-row">
|
||||||
<div class="input-with-action">
|
<div class="input-with-action">
|
||||||
@@ -715,7 +725,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Contents tab -->
|
|
||||||
<div id="bin-editor-tab-contents" hidden>
|
<div id="bin-editor-tab-contents" hidden>
|
||||||
<div id="bin-contents-list"></div>
|
<div id="bin-contents-list"></div>
|
||||||
<div class="form-row" style="margin-top:0.5rem">
|
<div class="form-row" style="margin-top:0.5rem">
|
||||||
@@ -725,7 +734,7 @@
|
|||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button type="button" class="btn btn-secondary" id="bin-editor-cancel">Cancel</button>
|
<button type="button" class="btn btn-secondary" id="bin-editor-cancel">Cancel</button>
|
||||||
<button type="button" class="btn btn-primary" id="bin-editor-save">Save & process</button>
|
<button type="button" class="btn btn-primary" id="bin-editor-save">Save</button>
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user