Split frontend UI into feature modules
This commit is contained in:
83
wwwroot/js/ui-utils.js
Normal file
83
wwwroot/js/ui-utils.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import { t } from "./i18n.js";
|
||||
import { state } from "./state.js";
|
||||
|
||||
export const sortByName = (items) =>
|
||||
(items ?? [])
|
||||
.slice()
|
||||
.sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: "base" }));
|
||||
|
||||
export const truncate = (text, max) => {
|
||||
if (!text) return "";
|
||||
return text.length > max ? `${text.slice(0, max - 1)}...` : text;
|
||||
};
|
||||
|
||||
export const escapeHtml = (value) =>
|
||||
(value ?? "")
|
||||
.toString()
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
|
||||
export const safeUrl = (url) => {
|
||||
if (!url) return null;
|
||||
try {
|
||||
const u = new URL(url);
|
||||
if (u.protocol === "http:" || u.protocol === "https:") return u.href;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const cssEscapeUrl = (url) => url.replace(/['")\\]/g, "\\$&");
|
||||
|
||||
export function linkRootId(s) {
|
||||
return s?.parentSuggestionId ?? s?.id;
|
||||
}
|
||||
|
||||
export function linkedPeerIds(s) {
|
||||
if (!s) return [];
|
||||
if (Array.isArray(s.linkedIds) && s.linkedIds.length > 0) {
|
||||
return s.linkedIds.filter((id) => id !== s.id);
|
||||
}
|
||||
if (!state.allSuggestions?.length) return [];
|
||||
const root = linkRootId(s);
|
||||
return state.allSuggestions
|
||||
.filter((other) => linkRootId(other) === root && other.id !== s.id)
|
||||
.map((other) => other.id);
|
||||
}
|
||||
|
||||
export function linkedPeerTitles(s) {
|
||||
if (!s) return [];
|
||||
if (Array.isArray(s.linkedTitles) && s.linkedTitles.length > 0) {
|
||||
return s.linkedTitles;
|
||||
}
|
||||
if (!state.allSuggestions?.length) return [];
|
||||
const root = linkRootId(s);
|
||||
return state.allSuggestions
|
||||
.filter((other) => linkRootId(other) === root && other.id !== s.id)
|
||||
.map((other) => other.name);
|
||||
}
|
||||
|
||||
export function isLinked(s) {
|
||||
return !!s?.parentSuggestionId || linkedPeerIds(s).length > 0;
|
||||
}
|
||||
|
||||
export function linkTooltip(s) {
|
||||
const peers = linkedPeerTitles(s);
|
||||
if (peers.length === 0) return t("card.linked");
|
||||
return t("card.linkedWith", { names: peers.join(", ") });
|
||||
}
|
||||
|
||||
export function renderLinkBadge(s) {
|
||||
if (!isLinked(s)) return "";
|
||||
return `<span class="chip icon link-chip" title="${escapeHtml(linkTooltip(s))}">🔗</span>`;
|
||||
}
|
||||
|
||||
export function buildLinkOptionLabel(s) {
|
||||
const author = s.author ? ` - ${s.author}` : "";
|
||||
const linked = isLinked(s) ? " 🔗" : "";
|
||||
return `${s.name}${author}${linked}`;
|
||||
}
|
||||
Reference in New Issue
Block a user