Virtualize instance and net lists for large schematic performance
This commit is contained in:
parent
3cb6feeb15
commit
559ff51013
@ -4,6 +4,8 @@ const SCHEMA_URL = "/schemeta.schema.json";
|
|||||||
const NET_CLASSES = ["power", "ground", "signal", "analog", "differential", "clock", "bus"];
|
const NET_CLASSES = ["power", "ground", "signal", "analog", "differential", "clock", "bus"];
|
||||||
const PIN_SIDES = ["left", "right", "top", "bottom"];
|
const PIN_SIDES = ["left", "right", "top", "bottom"];
|
||||||
const PIN_TYPES = ["power_in", "power_out", "input", "output", "bidirectional", "passive", "analog", "ground"];
|
const PIN_TYPES = ["power_in", "power_out", "input", "output", "bidirectional", "passive", "analog", "ground"];
|
||||||
|
const LIST_ROW_HEIGHT = 36;
|
||||||
|
const LIST_OVERSCAN_ROWS = 8;
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
model: null,
|
model: null,
|
||||||
@ -721,6 +723,26 @@ function resolveLabelCollisions(svg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderVirtualList(listEl, allItems, renderRow, rowHeight = LIST_ROW_HEIGHT) {
|
||||||
|
const items = Array.isArray(allItems) ? allItems : [];
|
||||||
|
if (!items.length) {
|
||||||
|
listEl.innerHTML = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewportHeight = Math.max(rowHeight * 4, listEl.clientHeight || 230);
|
||||||
|
const scrollTop = listEl.scrollTop;
|
||||||
|
const total = items.length;
|
||||||
|
const visibleRows = Math.ceil(viewportHeight / rowHeight);
|
||||||
|
const start = Math.max(0, Math.floor(scrollTop / rowHeight) - LIST_OVERSCAN_ROWS);
|
||||||
|
const end = Math.min(total, start + visibleRows + LIST_OVERSCAN_ROWS * 2);
|
||||||
|
const topPad = start * rowHeight;
|
||||||
|
const bottomPad = (total - end) * rowHeight;
|
||||||
|
|
||||||
|
const rows = items.slice(start, end).map(renderRow).join("");
|
||||||
|
listEl.innerHTML = `<li class="listSpacer" style="height:${topPad}px"></li>${rows}<li class="listSpacer" style="height:${bottomPad}px"></li>`;
|
||||||
|
}
|
||||||
|
|
||||||
function renderInstances() {
|
function renderInstances() {
|
||||||
if (!state.model) {
|
if (!state.model) {
|
||||||
el.instanceList.innerHTML = "";
|
el.instanceList.innerHTML = "";
|
||||||
@ -730,12 +752,15 @@ function renderInstances() {
|
|||||||
const q = el.instanceFilter.value.trim().toLowerCase();
|
const q = el.instanceFilter.value.trim().toLowerCase();
|
||||||
const items = state.model.instances.filter((i) => i.ref.toLowerCase().includes(q));
|
const items = state.model.instances.filter((i) => i.ref.toLowerCase().includes(q));
|
||||||
|
|
||||||
el.instanceList.innerHTML = items
|
renderVirtualList(
|
||||||
.map((inst) => {
|
el.instanceList,
|
||||||
|
items,
|
||||||
|
(inst) => {
|
||||||
const cls = state.selectedRefs.includes(inst.ref) ? "active" : "";
|
const cls = state.selectedRefs.includes(inst.ref) ? "active" : "";
|
||||||
return `<li class="${cls}" data-ref-item="${inst.ref}">${inst.ref} · ${inst.symbol}</li>`;
|
return `<li class="${cls}" data-ref-item="${inst.ref}">${inst.ref} · ${inst.symbol}</li>`;
|
||||||
})
|
},
|
||||||
.join("");
|
LIST_ROW_HEIGHT
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderNets() {
|
function renderNets() {
|
||||||
@ -747,12 +772,15 @@ function renderNets() {
|
|||||||
const q = el.netFilter.value.trim().toLowerCase();
|
const q = el.netFilter.value.trim().toLowerCase();
|
||||||
const items = state.model.nets.filter((n) => n.name.toLowerCase().includes(q));
|
const items = state.model.nets.filter((n) => n.name.toLowerCase().includes(q));
|
||||||
|
|
||||||
el.netList.innerHTML = items
|
renderVirtualList(
|
||||||
.map((net) => {
|
el.netList,
|
||||||
|
items,
|
||||||
|
(net) => {
|
||||||
const cls = net.name === state.selectedNet ? "active" : "";
|
const cls = net.name === state.selectedNet ? "active" : "";
|
||||||
return `<li class="${cls}" data-net-item="${net.name}">${net.name} <small>(${net.class})</small></li>`;
|
return `<li class="${cls}" data-net-item="${net.name}">${net.name} <small>(${net.class})</small></li>`;
|
||||||
})
|
},
|
||||||
.join("");
|
LIST_ROW_HEIGHT
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function netByName(name) {
|
function netByName(name) {
|
||||||
@ -1843,8 +1871,16 @@ async function loadSample() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setupEvents() {
|
function setupEvents() {
|
||||||
el.instanceFilter.addEventListener("input", renderInstances);
|
el.instanceFilter.addEventListener("input", () => {
|
||||||
el.netFilter.addEventListener("input", renderNets);
|
el.instanceList.scrollTop = 0;
|
||||||
|
renderInstances();
|
||||||
|
});
|
||||||
|
el.netFilter.addEventListener("input", () => {
|
||||||
|
el.netList.scrollTop = 0;
|
||||||
|
renderNets();
|
||||||
|
});
|
||||||
|
el.instanceList.addEventListener("scroll", renderInstances, { passive: true });
|
||||||
|
el.netList.addEventListener("scroll", renderNets, { passive: true });
|
||||||
|
|
||||||
el.instanceList.addEventListener("click", (evt) => {
|
el.instanceList.addEventListener("click", (evt) => {
|
||||||
const item = evt.target.closest("[data-ref-item]");
|
const item = evt.target.closest("[data-ref-item]");
|
||||||
|
|||||||
@ -182,6 +182,13 @@ textarea {
|
|||||||
background: var(--accent-soft);
|
background: var(--accent-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list li.listSpacer {
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
cursor: default;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user