1
0
mirror of https://github.com/coalaura/whiskr.git synced 2025-09-09 09:19:54 +00:00

dropdowns

This commit is contained in:
Laura
2025-08-09 22:14:35 +02:00
parent 9db37f64cb
commit 91d9ce4449
7 changed files with 163 additions and 117 deletions

View File

@@ -585,5 +585,8 @@
scroll(!interacted);
});
dropdown($role);
dropdown($prompt);
loadModels().then(restore);
})();

View File

@@ -3,17 +3,22 @@
#_select;
#_dropdown;
#_selected;
#_search;
#search = false;
#selected = false;
#options = [];
constructor(el) {
this.#_select = el;
this.#search = "searchable" in el.dataset;
this.#_select.querySelectorAll("option").forEach((option) => {
this.#options.push({
value: option.value,
label: option.textContent.trim(),
label: option.textContent,
search: searchable(option.textContent),
});
});
@@ -47,33 +52,72 @@
// dropdown
this.#_dropdown = make("div", "dropdown");
this.#_dropdown.addEventListener("click", () => {
this.#_dropdown.classList.add("open");
});
// selected item
this.#_selected = make("div", "selected");
this.#_selected.addEventListener("click", () => {
this.#_dropdown.classList.toggle("open");
});
this.#_dropdown.appendChild(this.#_selected);
// option wrapper
const _options = make("div", "options");
// content
const _content = make("div", "cont");
this.#_dropdown.appendChild(_options);
this.#_dropdown.appendChild(_content);
// option wrapper
const _options = make("div", "opts");
_content.appendChild(_options);
// options
for (const option of this.#options) {
const _opt = make("div", "option");
const _opt = make("div", "opt");
_opt.textContent = option.label;
_opt.addEventListener("click", () => {
this.#set(option.value);
this.#_dropdown.classList.remove("open");
});
_options.appendChild(_opt);
option.el = _opt;
}
// live search (if enabled)
if (this.#search) {
this.#_search = make("input", "search");
this.#_search.type = "text";
this.#_search.placeholder = "Search...";
this.#_search.addEventListener("input", () => {
this.#filter();
});
this.#_search.addEventListener("keydown", (event) => {
if (event.key !== "Escape") {
return;
}
if (this.#_search.value) {
this.#_search.value = "";
this.#_search.dispatchEvent(new Event("input"));
return;
}
this.#_dropdown.classList.remove("open");
});
_content.appendChild(this.#_search);
}
// add to dom
this.#_select.after(this.#_dropdown);
@@ -92,9 +136,23 @@
this.#_selected.textContent = selection.label;
}
#set(value) {
console.log("value", value);
#filter() {
if (!this.#_search) {
return;
}
const query = searchable(this.#_search.value);
for (const option of this.#options) {
if (query && !option.search.includes(query)) {
option.el.classList.add("filtered");
} else {
option.el.classList.remove("filtered");
}
}
}
#set(value) {
const index = this.#options.findIndex((option) => option.value === value);
if (this.#selected === index) {
@@ -107,6 +165,16 @@
}
}
function searchable(text) {
// lowercase
text = text.toLowerCase();
// only alpha-num
text = text.replace(/[^\w]/g, "");
return text.trim();
}
document.body.addEventListener("click", (event) => {
const clicked = event.target.closest(".dropdown");

View File

@@ -1,105 +1,3 @@
(() => {
const timeouts = new WeakMap(),
images = {};
marked.use({
async: false,
breaks: false,
gfm: true,
pedantic: false,
walkTokens: (token) => {
const { type, lang, text } = token;
if (type !== "code") {
return;
}
let code;
if (lang && hljs.getLanguage(lang)) {
code = hljs.highlight(text, {
language: lang,
});
} else {
code = hljs.highlightAuto(text);
}
token.escaped = true;
token.text = code.value;
},
renderer: {
code(code) {
const header = `<div class="pre-header">${escapeHtml(code.lang)}<button class="pre-copy" title="Copy code contents"></button></div>`;
return `<pre>${header}<code>${code.text}</code></pre>`;
},
image(image) {
const { href } = image;
const id = `i_${btoa(href).replace(/=/g, "")}`,
style = prepareImage(id, href) || "";
return `<div class="image ${id}" style="${style}"></div>`;
},
},
});
function prepareImage(id, href) {
if (href in images) {
return images[href];
}
images[href] = false;
const image = new Image();
image.addEventListener("load", () => {
const style = `aspect-ratio:${image.naturalWidth}/${image.naturalHeight};width:${image.naturalWidth}px;background-image:url(${href})`;
images[href] = style;
document.querySelectorAll(`.image.${id}`).forEach((img) => {
img.setAttribute("style", style);
});
});
image.addEventListener("error", () => {
console.error(`Failed to load image: ${href}`);
});
image.src = href;
return false;
}
document.body.addEventListener("click", (event) => {
const button = event.target,
header = button.closest(".pre-header"),
pre = header?.closest("pre"),
code = pre?.querySelector("code");
if (!code) {
return;
}
clearTimeout(timeouts.get(pre));
navigator.clipboard.writeText(code.textContent.trim());
button.classList.add("copied");
timeouts.set(
pre,
setTimeout(() => {
button.classList.remove("copied");
}, 1000),
);
});
})();
function storeValue(key, value) {
if (!value) {
localStorage.removeItem(key);