Add inline validation feedback for symbol pin row edits
This commit is contained in:
parent
9ee97ffa8e
commit
c02b14649e
@ -74,6 +74,7 @@ const el = {
|
|||||||
symbolHeightInput: document.getElementById("symbolHeightInput"),
|
symbolHeightInput: document.getElementById("symbolHeightInput"),
|
||||||
addSymbolPinBtn: document.getElementById("addSymbolPinBtn"),
|
addSymbolPinBtn: document.getElementById("addSymbolPinBtn"),
|
||||||
applySymbolBtn: document.getElementById("applySymbolBtn"),
|
applySymbolBtn: document.getElementById("applySymbolBtn"),
|
||||||
|
symbolValidation: document.getElementById("symbolValidation"),
|
||||||
symbolPinsList: document.getElementById("symbolPinsList"),
|
symbolPinsList: document.getElementById("symbolPinsList"),
|
||||||
pinMeta: document.getElementById("pinMeta"),
|
pinMeta: document.getElementById("pinMeta"),
|
||||||
pinNameInput: document.getElementById("pinNameInput"),
|
pinNameInput: document.getElementById("pinNameInput"),
|
||||||
@ -880,10 +881,18 @@ function renderSymbolEditorForRef(ref) {
|
|||||||
el.symbolCategoryInput.value = String(sym.category ?? "");
|
el.symbolCategoryInput.value = String(sym.category ?? "");
|
||||||
el.symbolWidthInput.value = String(Number(sym.body?.width ?? 120));
|
el.symbolWidthInput.value = String(Number(sym.body?.width ?? 120));
|
||||||
el.symbolHeightInput.value = String(Number(sym.body?.height ?? 80));
|
el.symbolHeightInput.value = String(Number(sym.body?.height ?? 80));
|
||||||
|
el.symbolValidation.textContent = "";
|
||||||
|
el.symbolValidation.classList.remove("symbolValidationError");
|
||||||
el.symbolPinsList.innerHTML = (sym.pins ?? []).map((pin) => symbolPinRowHtml(pin)).join("");
|
el.symbolPinsList.innerHTML = (sym.pins ?? []).map((pin) => symbolPinRowHtml(pin)).join("");
|
||||||
el.symbolEditor.classList.remove("hidden");
|
el.symbolEditor.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearSymbolRowValidation(rows) {
|
||||||
|
for (const row of rows) {
|
||||||
|
row.classList.remove("invalidRow");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderPinEditor() {
|
function renderPinEditor() {
|
||||||
if (!state.selectedPin || !state.model) {
|
if (!state.selectedPin || !state.model) {
|
||||||
el.pinEditor.classList.add("hidden");
|
el.pinEditor.classList.add("hidden");
|
||||||
@ -2284,6 +2293,17 @@ function setupEvents() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
el.symbolPinsList.addEventListener("input", (evt) => {
|
||||||
|
const row = evt.target.closest(".symbolPinRow");
|
||||||
|
if (row) {
|
||||||
|
row.classList.remove("invalidRow");
|
||||||
|
}
|
||||||
|
if (el.symbolValidation.textContent) {
|
||||||
|
el.symbolValidation.textContent = "";
|
||||||
|
el.symbolValidation.classList.remove("symbolValidationError");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
el.applySymbolBtn.addEventListener("click", () => {
|
el.applySymbolBtn.addEventListener("click", () => {
|
||||||
if (!state.selectedRef) {
|
if (!state.selectedRef) {
|
||||||
return;
|
return;
|
||||||
@ -2302,12 +2322,18 @@ function setupEvents() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const rows = [...el.symbolPinsList.querySelectorAll(".symbolPinRow")];
|
const rows = [...el.symbolPinsList.querySelectorAll(".symbolPinRow")];
|
||||||
|
clearSymbolRowValidation(rows);
|
||||||
|
el.symbolValidation.textContent = "";
|
||||||
|
el.symbolValidation.classList.remove("symbolValidationError");
|
||||||
if (!rows.length) {
|
if (!rows.length) {
|
||||||
el.jsonFeedback.textContent = "Symbol must have at least one pin.";
|
el.jsonFeedback.textContent = "Symbol must have at least one pin.";
|
||||||
|
el.symbolValidation.textContent = "Symbol must contain at least one pin row.";
|
||||||
|
el.symbolValidation.classList.add("symbolValidationError");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedPins = [];
|
const parsedPins = [];
|
||||||
|
const rowErrors = [];
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const name = String(row.querySelector(".pinName")?.value ?? "").trim();
|
const name = String(row.querySelector(".pinName")?.value ?? "").trim();
|
||||||
const number = String(row.querySelector(".pinNumber")?.value ?? "").trim();
|
const number = String(row.querySelector(".pinNumber")?.value ?? "").trim();
|
||||||
@ -2315,8 +2341,9 @@ function setupEvents() {
|
|||||||
const offset = Number(row.querySelector(".pinOffset")?.value ?? 0);
|
const offset = Number(row.querySelector(".pinOffset")?.value ?? 0);
|
||||||
const type = String(row.querySelector(".pinType")?.value ?? "");
|
const type = String(row.querySelector(".pinType")?.value ?? "");
|
||||||
if (!name || !number || !PIN_SIDES.includes(side) || !PIN_TYPES.includes(type) || !Number.isFinite(offset) || offset < 0) {
|
if (!name || !number || !PIN_SIDES.includes(side) || !PIN_TYPES.includes(type) || !Number.isFinite(offset) || offset < 0) {
|
||||||
el.jsonFeedback.textContent = "Invalid symbol pin row values.";
|
row.classList.add("invalidRow");
|
||||||
return;
|
rowErrors.push("Each pin row needs name, number, valid side/type, and offset >= 0.");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
parsedPins.push({
|
parsedPins.push({
|
||||||
oldName: row.getAttribute("data-old-pin") ?? name,
|
oldName: row.getAttribute("data-old-pin") ?? name,
|
||||||
@ -2324,9 +2351,24 @@ function setupEvents() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rowErrors.length) {
|
||||||
|
el.jsonFeedback.textContent = "Fix invalid symbol pin rows before applying.";
|
||||||
|
el.symbolValidation.textContent = rowErrors[0];
|
||||||
|
el.symbolValidation.classList.add("symbolValidationError");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const unique = new Set(parsedPins.map((p) => p.pin.name));
|
const unique = new Set(parsedPins.map((p) => p.pin.name));
|
||||||
if (unique.size !== parsedPins.length) {
|
if (unique.size !== parsedPins.length) {
|
||||||
el.jsonFeedback.textContent = "Duplicate pin names are not allowed.";
|
el.jsonFeedback.textContent = "Duplicate pin names are not allowed.";
|
||||||
|
el.symbolValidation.textContent = "Duplicate pin names detected. Each pin name must be unique.";
|
||||||
|
el.symbolValidation.classList.add("symbolValidationError");
|
||||||
|
for (const row of rows) {
|
||||||
|
const name = String(row.querySelector(".pinName")?.value ?? "").trim();
|
||||||
|
if (name && parsedPins.filter((p) => p.pin.name === name).length > 1) {
|
||||||
|
row.classList.add("invalidRow");
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2368,6 +2410,8 @@ function setupEvents() {
|
|||||||
if (state.selectedPin && !pinExists(state.selectedPin.ref, state.selectedPin.pin)) {
|
if (state.selectedPin && !pinExists(state.selectedPin.ref, state.selectedPin.pin)) {
|
||||||
state.selectedPin = null;
|
state.selectedPin = null;
|
||||||
}
|
}
|
||||||
|
el.symbolValidation.textContent = "";
|
||||||
|
el.symbolValidation.classList.remove("symbolValidationError");
|
||||||
const removedPinCount = [...beforePins].filter((p) => !allowedPins.has(p)).length;
|
const removedPinCount = [...beforePins].filter((p) => !allowedPins.has(p)).length;
|
||||||
el.jsonFeedback.textContent = removedPinCount
|
el.jsonFeedback.textContent = removedPinCount
|
||||||
? `Updated symbol ${inst.symbol}. Removed ${removedPinCount} pin mappings from nets/UI metadata.`
|
? `Updated symbol ${inst.symbol}. Removed ${removedPinCount} pin mappings from nets/UI metadata.`
|
||||||
|
|||||||
@ -108,6 +108,7 @@
|
|||||||
<button id="addSymbolPinBtn">Add Pin</button>
|
<button id="addSymbolPinBtn">Add Pin</button>
|
||||||
<button id="applySymbolBtn">Apply Symbol</button>
|
<button id="applySymbolBtn">Apply Symbol</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="symbolValidation" class="hintText"></div>
|
||||||
<div id="symbolPinsList" class="miniList"></div>
|
<div id="symbolPinsList" class="miniList"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="pinEditor" class="editorCard hidden">
|
<div id="pinEditor" class="editorCard hidden">
|
||||||
|
|||||||
@ -269,6 +269,15 @@ textarea {
|
|||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.symbolPinRow.invalidRow {
|
||||||
|
background: #fff3f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.symbolValidationError {
|
||||||
|
color: var(--error);
|
||||||
|
font-size: 0.76rem;
|
||||||
|
}
|
||||||
|
|
||||||
.canvasTools {
|
.canvasTools {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user