Files
brk/website_next/ask/sidebar/index.js
T
2026-07-22 16:50:27 +02:00

118 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { bindHold } from "../../utils/hold.js";
const SIDEBAR_ID = "ask-sidebar";
/** @typedef {import("../storage.js").ChatMeta} ChatMeta */
/**
* @typedef {Object} AskSidebarOptions
* @property {() => void} onNew
* @property {() => void} onToggle
* @property {(id: string) => void} onSelect
* @property {(id: string) => void} onRemove
*/
/**
* @param {string} label
* @param {string} glyph
*/
function createControl(label, glyph) {
const button = document.createElement("button");
const icon = document.createElement("span");
button.type = "button";
button.ariaLabel = label;
button.title = label;
icon.ariaHidden = "true";
icon.append(glyph);
button.append(icon);
return button;
}
/** @param {AskSidebarOptions} options */
export function createAskSidebar(options) {
const backdrop = document.createElement("button");
const aside = document.createElement("aside");
const chats = document.createElement("ol");
const controls = document.createElement("nav");
const newChat = createControl("New chat", "+");
const toggle = createControl("Show chats", "→");
let busy = false;
backdrop.type = "button";
backdrop.ariaLabel = "Close chats";
aside.id = SIDEBAR_ID;
chats.setAttribute("aria-label", "Chats");
controls.setAttribute("aria-label", "Chat controls");
toggle.setAttribute("aria-controls", SIDEBAR_ID);
toggle.setAttribute("aria-expanded", "false");
newChat.addEventListener("click", options.onNew);
toggle.addEventListener("click", options.onToggle);
controls.append(newChat, toggle);
aside.append(chats);
function syncDisabled() {
newChat.disabled = busy;
toggle.disabled = busy;
for (const button of chats.querySelectorAll("button")) {
button.disabled = busy;
}
}
return {
backdrop,
controls,
element: aside,
newChat,
toggle,
/** @param {boolean} value */
setBusy(value) {
busy = value;
syncDisabled();
},
/** @param {boolean} expanded */
setExpanded(expanded) {
const label = expanded ? "Hide chats" : "Show chats";
toggle.setAttribute("aria-expanded", String(expanded));
toggle.ariaLabel = label;
toggle.title = label;
},
/**
* @param {ChatMeta[]} items
* @param {string} activeId
*/
render(items, activeId) {
const scrollTop = chats.scrollTop;
const rows = items.filter((chat) => chat.messageCount > 0).map((chat) => {
const item = document.createElement("li");
const select = document.createElement("button");
const remove = document.createElement("button");
select.type = "button";
select.title = chat.title;
select.textContent = chat.title;
if (chat.id === activeId) select.setAttribute("aria-current", "page");
select.addEventListener("click", () => options.onSelect(chat.id));
remove.type = "button";
remove.ariaLabel = `Hold to delete ${chat.title}`;
remove.title = "Hold to delete chat";
const icon = document.createElement("span");
icon.ariaHidden = "true";
icon.append("×");
remove.append(icon);
bindHold(remove, () => options.onRemove(chat.id), 1_000);
item.append(select, remove);
return item;
});
chats.replaceChildren(...rows);
chats.scrollTop = scrollTop;
syncDisabled();
},
};
}