198 lines
6.3 KiB
JavaScript
198 lines
6.3 KiB
JavaScript
import { app } from "../../../scripts/app.js";
|
|
|
|
function filterChildDirs(allDirs, parentPrefix) {
|
|
const prefixNorm = parentPrefix.replace(/\/$/, "").toLowerCase();
|
|
const results = [];
|
|
|
|
for (const dir of allDirs) {
|
|
const parts = dir.split("/").filter(Boolean);
|
|
if (parts.length < 1) continue;
|
|
|
|
const parent = parts.slice(0, -1).join("/").toLowerCase();
|
|
const child = parts[parts.length - 1];
|
|
|
|
if (parent === prefixNorm) {
|
|
results.push(child);
|
|
}
|
|
}
|
|
|
|
return [...new Set(results)].sort();
|
|
}
|
|
|
|
function showDropdown(listEl, items, directoryWidget, inputEl) {
|
|
listEl.innerHTML = "";
|
|
if (items.length === 0) {
|
|
listEl.style.display = "none";
|
|
return;
|
|
}
|
|
for (const item of items) {
|
|
const li = document.createElement("li");
|
|
li.textContent = item;
|
|
li.style.padding = "4px 8px";
|
|
li.style.cursor = "pointer";
|
|
li.style.color = "#ccc";
|
|
li.style.listStyle = "none";
|
|
li.addEventListener("mouseenter", () => {
|
|
listEl.querySelectorAll("li").forEach((el) => (el.style.background = "transparent"));
|
|
li.style.background = "#444";
|
|
});
|
|
li.addEventListener("click", () => {
|
|
const newValue = inputEl._currentPrefix + item + "/";
|
|
inputEl.value = newValue;
|
|
inputEl._currentPrefix = newValue;
|
|
directoryWidget.value = newValue;
|
|
hideDropdown(listEl);
|
|
inputEl.focus();
|
|
});
|
|
listEl.appendChild(li);
|
|
}
|
|
|
|
Object.assign(listEl.style, {
|
|
display: "block",
|
|
background: "#222",
|
|
border: "1px solid #444",
|
|
position: "absolute",
|
|
zIndex: "9999",
|
|
width: inputEl.offsetWidth + "px",
|
|
maxHeight: "200px",
|
|
overflowY: "auto",
|
|
});
|
|
}
|
|
|
|
function hideDropdown(listEl) {
|
|
listEl.innerHTML = "";
|
|
listEl.style.display = "none";
|
|
}
|
|
|
|
app.registerExtension({
|
|
name: "CompassImageLoader",
|
|
|
|
async beforeRegisterNodeDef(nodeType, nodeData) {
|
|
if (nodeData.name !== "CompassImageLoader") return;
|
|
|
|
const origOnNodeCreated = nodeType.prototype.onNodeCreated;
|
|
nodeType.prototype.onNodeCreated = function () {
|
|
if (origOnNodeCreated) origOnNodeCreated.apply(this, []);
|
|
|
|
const directoryWidget = this.widgets?.find((w) => w.name === "directory");
|
|
if (!directoryWidget) return;
|
|
|
|
const defDirs = nodeData.input?.required?.directory;
|
|
directoryWidget._all_values = defDirs && Array.isArray(defDirs[0]) ? defDirs[0] : [];
|
|
|
|
const filterWidget = this.widgets?.find((w) => w.name === "directory_filter");
|
|
const inputEl = filterWidget?.inputEl;
|
|
if (!inputEl) return;
|
|
|
|
const listId = `compass_dir_list_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
const listEl = document.createElement("ul");
|
|
listEl.id = listId;
|
|
listEl.style.display = "none";
|
|
listEl.style.position = "absolute";
|
|
listEl.style.zIndex = "9999";
|
|
document.body.appendChild(listEl);
|
|
inputEl._listId = listId;
|
|
|
|
inputEl._currentPrefix = directoryWidget.value || "";
|
|
|
|
inputEl.addEventListener("input", () => {
|
|
const val = inputEl.value;
|
|
const lastSlash = val.lastIndexOf("/");
|
|
|
|
let prefix, filter;
|
|
if (lastSlash >= 0) {
|
|
prefix = val.substring(0, lastSlash + 1);
|
|
filter = val.substring(lastSlash + 1);
|
|
} else {
|
|
prefix = "";
|
|
filter = val;
|
|
}
|
|
|
|
if (prefix) {
|
|
const cleanPrefix = prefix.replace(/\/$/, "");
|
|
const candidates = filterChildDirs(directoryWidget._all_values, cleanPrefix);
|
|
const filtered = filter
|
|
? candidates.filter((c) => c.toLowerCase().startsWith(filter.toLowerCase()))
|
|
: candidates;
|
|
showDropdown(listEl, filtered, directoryWidget, inputEl);
|
|
} else {
|
|
const filtered = filter
|
|
? directoryWidget._all_values.filter((d) =>
|
|
d.toLowerCase().includes(filter.toLowerCase())
|
|
)
|
|
: [...new Set(directoryWidget._all_values.map((d) => d.split("/")[0]))].sort();
|
|
showDropdown(listEl, filtered, directoryWidget, inputEl);
|
|
}
|
|
});
|
|
|
|
inputEl.addEventListener("focus", () => {
|
|
const val = inputEl.value;
|
|
const lastSlash = val.lastIndexOf("/");
|
|
|
|
if (lastSlash >= 0 && val.endsWith("/")) {
|
|
const prefix = val.replace(/\/$/, "");
|
|
inputEl._currentPrefix = prefix + "/";
|
|
const items = filterChildDirs(directoryWidget._all_values, prefix);
|
|
showDropdown(listEl, items, directoryWidget, inputEl);
|
|
} else if (lastSlash >= 0) {
|
|
const prefix = val.substring(0, lastSlash + 1);
|
|
inputEl._currentPrefix = prefix;
|
|
inputEl.focus();
|
|
return;
|
|
} else {
|
|
inputEl._currentPrefix = "";
|
|
const items = filter
|
|
? directoryWidget._all_values.filter((d) =>
|
|
d.toLowerCase().includes(val.toLowerCase())
|
|
)
|
|
: [...new Set(directoryWidget._all_values.map((d) => d.split("/")[0]))].sort();
|
|
showDropdown(listEl, items, directoryWidget, inputEl);
|
|
}
|
|
});
|
|
|
|
inputEl.addEventListener("blur", () => {
|
|
setTimeout(() => {
|
|
const list = document.getElementById(inputEl._listId);
|
|
if (list && !list.contains(document.activeElement)) {
|
|
hideDropdown(list);
|
|
}
|
|
}, 150);
|
|
});
|
|
|
|
inputEl.addEventListener("keydown", (e) => {
|
|
if (e.key === "Escape") {
|
|
hideDropdown(listEl);
|
|
return;
|
|
}
|
|
if (e.key === "Enter" || e.key === "Tab") {
|
|
const visible = listEl.querySelectorAll("li");
|
|
if (visible.length === 1) {
|
|
visible[0].click();
|
|
e.preventDefault();
|
|
} else if (visible.length > 0) {
|
|
visible[0].click();
|
|
e.preventDefault();
|
|
}
|
|
hideDropdown(listEl);
|
|
}
|
|
});
|
|
|
|
const origSetValue = directoryWidget.callback
|
|
? directoryWidget.callback.bind(directoryWidget)
|
|
: () => {};
|
|
directoryWidget.callback = (value) => {
|
|
origSetValue(value);
|
|
inputEl._currentPrefix = value || "";
|
|
inputEl.value = value || "";
|
|
};
|
|
|
|
const origOnRemoved = this.onRemoved ? this.onRemoved.bind(this) : () => {};
|
|
this.onRemoved = () => {
|
|
origOnRemoved();
|
|
const list = document.getElementById(listId);
|
|
if (list) list.remove();
|
|
};
|
|
};
|
|
},
|
|
});
|