[crawler_platform 삭제]
This commit is contained in:
172
ontology_platform/web/frontend/src/legacy/search.js
Normal file
172
ontology_platform/web/frontend/src/legacy/search.js
Normal file
@@ -0,0 +1,172 @@
|
||||
import { api } from "./api.js";
|
||||
import { state } from "./state.js";
|
||||
import { $, escapeHtml } from "./utils.js";
|
||||
import { t, applyI18n, onLangChange } from "./i18n.js";
|
||||
import { refreshInspector } from "./inspector.js";
|
||||
|
||||
let debounceTimer = null;
|
||||
let lastQuery = "";
|
||||
let resultsCache = null;
|
||||
|
||||
export function mountGlobalSearch() {
|
||||
const input = $("globalSearchInput");
|
||||
const panel = $("globalSearchPanel");
|
||||
applyI18n(input.parentElement);
|
||||
|
||||
input.addEventListener("input", () => {
|
||||
const q = input.value.trim();
|
||||
if (debounceTimer) window.clearTimeout(debounceTimer);
|
||||
debounceTimer = window.setTimeout(() => runSearch(q), 200);
|
||||
});
|
||||
input.addEventListener("focus", () => {
|
||||
if (lastQuery) renderPanel(resultsCache); else renderHint();
|
||||
panel.hidden = false;
|
||||
});
|
||||
input.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Escape") { input.value = ""; panel.hidden = true; input.blur(); }
|
||||
if (event.key === "ArrowDown") {
|
||||
event.preventDefault();
|
||||
const first = panel.querySelector("[data-search-item]");
|
||||
first?.focus();
|
||||
}
|
||||
});
|
||||
document.addEventListener("click", (event) => {
|
||||
if (!panel.contains(event.target) && event.target !== input) panel.hidden = true;
|
||||
});
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (event.key === "/" && !event.target.matches("input, textarea, select")) {
|
||||
event.preventDefault();
|
||||
input.focus();
|
||||
input.select();
|
||||
}
|
||||
});
|
||||
onLangChange(() => {
|
||||
applyI18n(input.parentElement);
|
||||
if (resultsCache) renderPanel(resultsCache);
|
||||
});
|
||||
}
|
||||
|
||||
async function runSearch(q) {
|
||||
if (!state.selectedProject) return;
|
||||
lastQuery = q;
|
||||
const panel = $("globalSearchPanel");
|
||||
if (!q) { renderHint(); return; }
|
||||
try {
|
||||
resultsCache = await api(`/projects/${encodeURIComponent(state.selectedProject)}/search?q=${encodeURIComponent(q)}&limit=8`);
|
||||
renderPanel(resultsCache);
|
||||
} catch (error) {
|
||||
panel.innerHTML = `<div class="search-empty muted">${escapeHtml(error.message)}</div>`;
|
||||
panel.hidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
function renderHint() {
|
||||
const panel = $("globalSearchPanel");
|
||||
panel.innerHTML = `<div class="search-empty muted">${escapeHtml(t("search.hint"))}</div>`;
|
||||
panel.hidden = false;
|
||||
}
|
||||
|
||||
function renderPanel(results) {
|
||||
const panel = $("globalSearchPanel");
|
||||
if (!results) return;
|
||||
const groups = [
|
||||
{ key: "entities", items: results.entities, render: renderEntity },
|
||||
{ key: "claims", items: results.claims, render: renderClaim },
|
||||
{ key: "pages", items: results.pages, render: renderPage },
|
||||
{ key: "predicates", items: results.predicates, render: renderPredicate },
|
||||
].filter((g) => (g.items ?? []).length > 0);
|
||||
|
||||
if (!groups.length) {
|
||||
panel.innerHTML = `<div class="search-empty muted">${escapeHtml(t("search.empty"))}</div>`;
|
||||
} else {
|
||||
panel.innerHTML = groups.map((g) => `
|
||||
<div class="search-group">
|
||||
<div class="search-group-head">${escapeHtml(t(`search.group.${g.key}`))}</div>
|
||||
${g.items.map((item, idx) => g.render(item, idx)).join("")}
|
||||
</div>
|
||||
`).join("");
|
||||
panel.querySelectorAll("[data-search-item]").forEach((node) => {
|
||||
node.addEventListener("click", () => handleClick(node.dataset.searchItem, JSON.parse(node.dataset.payload)));
|
||||
node.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Enter") { event.preventDefault(); node.click(); }
|
||||
if (event.key === "ArrowDown") {
|
||||
event.preventDefault();
|
||||
(node.nextElementSibling || panel.querySelector("[data-search-item]"))?.focus?.();
|
||||
}
|
||||
if (event.key === "ArrowUp") {
|
||||
event.preventDefault();
|
||||
(node.previousElementSibling || panel.querySelector("[data-search-item]:last-of-type"))?.focus?.();
|
||||
}
|
||||
if (event.key === "Escape") {
|
||||
$("globalSearchInput").focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
panel.hidden = false;
|
||||
}
|
||||
|
||||
function renderEntity(e) {
|
||||
return `
|
||||
<button class="search-item" data-search-item="entity" data-payload='${escapeHtmlAttr(JSON.stringify(e))}'>
|
||||
<span class="search-item-main">${escapeHtml(e.name)}</span>
|
||||
<span class="search-item-meta">${escapeHtml(e.type ?? "")}</span>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderClaim(c) {
|
||||
const obj = c.object_value ? JSON.stringify(c.object_value) : "";
|
||||
return `
|
||||
<button class="search-item" data-search-item="claim" data-payload='${escapeHtmlAttr(JSON.stringify(c))}'>
|
||||
<span class="search-item-main">${escapeHtml(c.subject ?? "")} · ${escapeHtml(c.predicate ?? "")} ${escapeHtml(obj)}</span>
|
||||
<span class="search-item-meta">${Math.round((c.confidence ?? 0) * 100)}%</span>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderPage(p) {
|
||||
return `
|
||||
<button class="search-item" data-search-item="page" data-payload='${escapeHtmlAttr(JSON.stringify(p))}'>
|
||||
<span class="search-item-main">${escapeHtml(p.title ?? p.url ?? "")}</span>
|
||||
<span class="search-item-meta">${escapeHtml(p.url ?? "")}</span>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderPredicate(name) {
|
||||
return `
|
||||
<button class="search-item" data-search-item="predicate" data-payload='${escapeHtmlAttr(JSON.stringify(name))}'>
|
||||
<span class="search-item-main">${escapeHtml(name)}</span>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
function escapeHtmlAttr(value) {
|
||||
return String(value).replaceAll("&", "&").replaceAll("'", "'").replaceAll('"', """);
|
||||
}
|
||||
|
||||
function handleClick(kind, payload) {
|
||||
const panel = $("globalSearchPanel");
|
||||
panel.hidden = true;
|
||||
if (kind === "entity") {
|
||||
activateTab("entities");
|
||||
state.selection = { kind: "entity", data: { id: payload.id, name: payload.name, type: payload.type, metadata: {} } };
|
||||
refreshInspector();
|
||||
} else if (kind === "claim") {
|
||||
activateTab("claims");
|
||||
state.selection = { kind: "claim", data: { ...payload, object: undefined } };
|
||||
refreshInspector();
|
||||
} else if (kind === "page") {
|
||||
if (payload.url) window.open(payload.url, "_blank", "noreferrer");
|
||||
} else if (kind === "predicate") {
|
||||
activateTab("graph");
|
||||
const sel = $("graphPredicate");
|
||||
if (sel) { sel.value = payload; sel.dispatchEvent(new Event("change")); }
|
||||
}
|
||||
}
|
||||
|
||||
function activateTab(name) {
|
||||
document.querySelectorAll(".tab").forEach((n) => n.classList.toggle("active", n.dataset.tab === name));
|
||||
document.querySelectorAll(".tab-panel").forEach((n) => n.classList.toggle("active", n.id === name));
|
||||
}
|
||||
Reference in New Issue
Block a user