Add destructive-action confirmations in inspector editors
This commit is contained in:
parent
fcad4b284b
commit
31d2182258
@ -1701,6 +1701,51 @@ function hasNodeOnNet(net, ref, pin) {
|
|||||||
return (net.nodes ?? []).some((n) => n.ref === ref && n.pin === pin);
|
return (net.nodes ?? []).some((n) => n.ref === ref && n.pin === pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refsForSymbol(symbolId) {
|
||||||
|
return new Set((state.model?.instances ?? []).filter((i) => i.symbol === symbolId).map((i) => i.ref));
|
||||||
|
}
|
||||||
|
|
||||||
|
function symbolPinUsage(symbolId, pinName) {
|
||||||
|
const refs = refsForSymbol(symbolId);
|
||||||
|
const nets = new Set();
|
||||||
|
let nodes = 0;
|
||||||
|
for (const net of state.model?.nets ?? []) {
|
||||||
|
for (const node of net.nodes ?? []) {
|
||||||
|
if (refs.has(node.ref) && node.pin === pinName) {
|
||||||
|
nodes += 1;
|
||||||
|
nets.add(net.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { refs: refs.size, nodes, nets: [...nets].sort() };
|
||||||
|
}
|
||||||
|
|
||||||
|
function disconnectImpact(netName, ref, pin) {
|
||||||
|
const net = netByName(netName);
|
||||||
|
if (!net) {
|
||||||
|
return { remainingNodes: 0, orphaned: false };
|
||||||
|
}
|
||||||
|
const remainingNodes = (net.nodes ?? []).filter((n) => !(n.ref === ref && n.pin === pin)).length;
|
||||||
|
return {
|
||||||
|
remainingNodes,
|
||||||
|
orphaned: remainingNodes < 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteComponentImpact(ref) {
|
||||||
|
const impacted = [];
|
||||||
|
let removedNodes = 0;
|
||||||
|
for (const net of state.model?.nets ?? []) {
|
||||||
|
const before = net.nodes?.length ?? 0;
|
||||||
|
const after = (net.nodes ?? []).filter((n) => n.ref !== ref).length;
|
||||||
|
if (before !== after) {
|
||||||
|
removedNodes += before - after;
|
||||||
|
impacted.push({ name: net.name, removed: before - after, orphaned: after < 2 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { removedNodes, impacted };
|
||||||
|
}
|
||||||
|
|
||||||
function connectPinToNet(ref, pin, netName, opts = {}) {
|
function connectPinToNet(ref, pin, netName, opts = {}) {
|
||||||
if (!state.model) {
|
if (!state.model) {
|
||||||
return { ok: false, message: "No model loaded." };
|
return { ok: false, message: "No model loaded." };
|
||||||
@ -2283,6 +2328,16 @@ function setupEvents() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ref = state.selectedRef;
|
const ref = state.selectedRef;
|
||||||
|
const impact = deleteComponentImpact(ref);
|
||||||
|
const orphaned = impact.impacted.filter((x) => x.orphaned).map((x) => x.name);
|
||||||
|
const summary =
|
||||||
|
`Delete ${ref}?\n` +
|
||||||
|
`- Net nodes removed: ${impact.removedNodes}\n` +
|
||||||
|
`- Nets touched: ${impact.impacted.length}\n` +
|
||||||
|
(orphaned.length ? `- Nets that will be removed (<2 nodes): ${orphaned.slice(0, 8).join(", ")}${orphaned.length > 8 ? " ..." : ""}\n` : "");
|
||||||
|
if (!window.confirm(summary)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
pushHistory("delete-component");
|
pushHistory("delete-component");
|
||||||
state.model.instances = state.model.instances.filter((i) => i.ref !== ref);
|
state.model.instances = state.model.instances.filter((i) => i.ref !== ref);
|
||||||
for (const net of state.model.nets ?? []) {
|
for (const net of state.model.nets ?? []) {
|
||||||
@ -2416,6 +2471,13 @@ function setupEvents() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const netName = btn.getAttribute("data-disconnect-net");
|
const netName = btn.getAttribute("data-disconnect-net");
|
||||||
|
const impact = disconnectImpact(netName, state.selectedPin.ref, state.selectedPin.pin);
|
||||||
|
const message = impact.orphaned
|
||||||
|
? `Disconnect ${state.selectedPin.ref}.${state.selectedPin.pin} from ${netName}?\nThis will leave fewer than 2 nodes and remove net '${netName}'.`
|
||||||
|
: `Disconnect ${state.selectedPin.ref}.${state.selectedPin.pin} from ${netName}?`;
|
||||||
|
if (!window.confirm(message)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
pushHistory("disconnect-pin");
|
pushHistory("disconnect-pin");
|
||||||
const out = disconnectPinFromNet(state.selectedPin.ref, state.selectedPin.pin, netName);
|
const out = disconnectPinFromNet(state.selectedPin.ref, state.selectedPin.pin, netName);
|
||||||
if (!out.ok) {
|
if (!out.ok) {
|
||||||
@ -2487,6 +2549,13 @@ function setupEvents() {
|
|||||||
const netName = state.selectedNet;
|
const netName = state.selectedNet;
|
||||||
const [ref, ...pinParts] = String(btn.getAttribute("data-remove-node")).split(".");
|
const [ref, ...pinParts] = String(btn.getAttribute("data-remove-node")).split(".");
|
||||||
const pin = pinParts.join(".");
|
const pin = pinParts.join(".");
|
||||||
|
const impact = disconnectImpact(netName, ref, pin);
|
||||||
|
const message = impact.orphaned
|
||||||
|
? `Remove ${ref}.${pin} from ${netName}?\nThis will leave fewer than 2 nodes and remove net '${netName}'.`
|
||||||
|
: `Remove ${ref}.${pin} from ${netName}?`;
|
||||||
|
if (!window.confirm(message)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
pushHistory("net-node-remove");
|
pushHistory("net-node-remove");
|
||||||
const out = disconnectPinFromNet(ref, pin, netName);
|
const out = disconnectPinFromNet(ref, pin, netName);
|
||||||
if (!out.ok) {
|
if (!out.ok) {
|
||||||
@ -2532,6 +2601,22 @@ function setupEvents() {
|
|||||||
}
|
}
|
||||||
const row = btn.closest(".symbolPinRow");
|
const row = btn.closest(".symbolPinRow");
|
||||||
if (row) {
|
if (row) {
|
||||||
|
const pinName = String(row.querySelector(".pinName")?.value ?? row.getAttribute("data-old-pin") ?? "").trim();
|
||||||
|
const inst = state.selectedRef ? instanceByRef(state.selectedRef) : null;
|
||||||
|
if (inst && pinName) {
|
||||||
|
const usage = symbolPinUsage(inst.symbol, pinName);
|
||||||
|
if (usage.nodes > 0) {
|
||||||
|
const netSample = usage.nets.slice(0, 8).join(", ");
|
||||||
|
const suffix = usage.nets.length > 8 ? " ..." : "";
|
||||||
|
const prompt =
|
||||||
|
`Remove pin row '${pinName}' from symbol '${inst.symbol}' editor?\n` +
|
||||||
|
`Current usage: ${usage.nodes} net node(s) across ${usage.nets.length} net(s): ${netSample}${suffix}\n` +
|
||||||
|
"These references will be dropped when you apply symbol changes.";
|
||||||
|
if (!window.confirm(prompt)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
row.remove();
|
row.remove();
|
||||||
invalidateSymbolMigrationPreview("Pin rows changed. Preview migration again before apply.");
|
invalidateSymbolMigrationPreview("Pin rows changed. Preview migration again before apply.");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user