// retoor <retoor@molodetz.nl>
import { OptimisticAction } from "./OptimisticAction.js";
export class ReactionBar extends OptimisticAction {
constructor() {
super();
document.addEventListener("click", (event) => this.onClick(event));
}
async onClick(event) {
const addButton = event.target.closest(".reaction-add-btn");
if (addButton) {
event.preventDefault();
this.togglePalette(addButton);
return;
}
const trigger = event.target.closest("[data-reaction-emoji]");
if (trigger && !trigger.disabled) {
event.preventDefault();
await this.react(trigger);
return;
}
this.closeAllPalettes();
}
togglePalette(addButton) {
const palette = addButton.parentElement.querySelector(".reaction-palette");
const isOpen = palette.hidden === false;
this.closeAllPalettes();
palette.hidden = isOpen;
if (!palette.hidden) {
this.clampToViewport(palette);
this.ensureInView(palette);
}
}
clampToViewport(palette) {
const margin = 8;
palette.style.left = "";
const rect = palette.getBoundingClientRect();
const overflowRight = rect.right - (window.innerWidth - margin);
if (overflowRight > 0) {
palette.style.left = `${-overflowRight}px`;
}
const adjusted = palette.getBoundingClientRect();
if (adjusted.left < margin) {
const current = parseFloat(palette.style.left || "0");
palette.style.left = `${current + (margin - adjusted.left)}px`;
}
}
ensureInView(palette) {
const margin = 8;
const rect = palette.getBoundingClientRect();
const extra = rect.height * 0.3;
if (rect.top < margin) {
window.scrollBy({ top: rect.top - margin - extra, behavior: "smooth" });
} else if (rect.bottom > window.innerHeight - margin) {
window.scrollBy({ top: rect.bottom - (window.innerHeight - margin) + extra, behavior: "smooth" });
}
}
closeAllPalettes() {
document.querySelectorAll(".reaction-palette").forEach((palette) => {
palette.hidden = true;
});
}
async react(trigger) {
const bar = trigger.closest(".reaction-bar");
if (!bar) {
return;
}
const type = bar.dataset.reactionType;
const uid = bar.dataset.reactionUid;
const emoji = trigger.dataset.reactionEmoji;
await this.submit(`/reactions/${type}/${uid}`, { emoji }, null, (result) => this.render(bar, result));
this.closeAllPalettes();
}
render(bar, result) {
const counts = result.counts || {};
const mine = new Set(result.mine || []);
bar.querySelectorAll(".reaction-list .reaction-chip").forEach((chip) => {
const emoji = chip.dataset.reactionEmoji;
const count = counts[emoji] || 0;
chip.querySelector(".reaction-count").textContent = count;
chip.hidden = count === 0 && !mine.has(emoji);
chip.classList.toggle("reacted", mine.has(emoji));
});
}
}