forked from andyburke/autonomous.contact
feature: reactions
This commit is contained in:
parent
6500d9a9be
commit
b8467ec870
16 changed files with 2603 additions and 383 deletions
256
public/js/reactions.js
Normal file
256
public/js/reactions.js
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
const reactions_popup_width = 280;
|
||||
const reactions_popup_height = 280;
|
||||
|
||||
const reactions_popup_styling = `
|
||||
#reactionspopup {
|
||||
position: fixed;
|
||||
width: ${reactions_popup_width}px;
|
||||
height: ${reactions_popup_height}px;
|
||||
z-index: 100;
|
||||
background: inherit;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border-normal);
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#reactionspopup .icon.close {
|
||||
float: right;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
#reactionspopup input[name="search"] {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
#reactionspopup ul {
|
||||
margin-top: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
text-align: left;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
#reactionspopup ul li {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#reactionspopup ul[data-filtered] li {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#reactionspopup ul[data-filtered] li[data-filtered] {
|
||||
display: inline-block;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#reactionspopup #reactions-names-display {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: 2rem;
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
function get_best_coords_for_popup(target, offset = { x: 10, y: 10 }) {
|
||||
const target_x = target?.getBoundingClientRect().left ?? 0;
|
||||
const target_y = target?.getBoundingClientRect().top ?? 0;
|
||||
|
||||
const viewport_width = document.body.getBoundingClientRect().width;
|
||||
const viewport_height = document.body.getBoundingClientRect().height;
|
||||
|
||||
const best_coords = {
|
||||
x: target_x + offset.x,
|
||||
y: target_y + offset.y,
|
||||
};
|
||||
|
||||
if (target_x + offset.x + reactions_popup_width + offset.x > viewport_width) {
|
||||
best_coords.x = Math.max(0, target_x - reactions_popup_width);
|
||||
}
|
||||
|
||||
if (target_y + offset.y + reactions_popup_height + offset.y > viewport_height) {
|
||||
best_coords.y = Math.max(0, target_y - reactions_popup_height);
|
||||
}
|
||||
|
||||
return best_coords;
|
||||
}
|
||||
|
||||
let reactions_popup;
|
||||
let reactions_popup_form;
|
||||
let reactions_popup_parent_id_input;
|
||||
let reactions_popup_emojis_list;
|
||||
let reactions_popup_reaction_input;
|
||||
|
||||
function open_reactions_popup(event) {
|
||||
const parent_event_id = event.target?.closest("[data-event_id]")?.dataset?.event_id;
|
||||
reactions_popup_parent_id_input.value = parent_event_id ?? "";
|
||||
|
||||
const position = get_best_coords_for_popup(event.target.closest("[data-reactions]"), {
|
||||
x: 25,
|
||||
y: 25,
|
||||
});
|
||||
|
||||
reactions_popup.style.left = position.x + "px";
|
||||
reactions_popup.style.top = position.y + "px";
|
||||
|
||||
reactions_popup.style.visibility = "visible";
|
||||
reactions_popup.style.opacity = "1";
|
||||
reactions_popup.style.display = "block";
|
||||
}
|
||||
|
||||
function clear_reactions_popup() {
|
||||
if (!reactions_popup) {
|
||||
return;
|
||||
}
|
||||
|
||||
reactions_popup.style.visibility = "hidden";
|
||||
reactions_popup.style.opacity = "0";
|
||||
reactions_popup.style.display = "none";
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
if (!document.getElementById("reactions-styling")) {
|
||||
const style = document.createElement("style");
|
||||
style.id = "reactions-styling";
|
||||
style.innerHTML = reactions_popup_styling;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
reactions_popup = document.createElement("div");
|
||||
|
||||
reactions_popup.id = "reactionspopup";
|
||||
reactions_popup.innerHTML = `
|
||||
<div class="icon close" onclick="clear_reactions_popup()"></div>
|
||||
<form
|
||||
id="reactions-selection-form"
|
||||
data-smart="true"
|
||||
method="POST"
|
||||
on_reply="async (event) => { await document.querySelectorAll( '[data-feed]' ).forEach((feed) => feed.__render(event)); }"
|
||||
on_parsed="async (event) => { await document.querySelectorAll( '[data-feed]' ).forEach((feed) => feed.__render(event)); }"
|
||||
>
|
||||
<input id="reactions-search-input" name="search" type="text" placeholder="Search..." data-skip="true" />
|
||||
|
||||
<input type="hidden" name="type" value="reaction" />
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="id"
|
||||
generator="(_input, form) => 'TEMP-' + form.__submitted_at.toISOString()"
|
||||
reset-on-submit
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
name="meta.temp_id"
|
||||
generator="(_input, form) => 'TEMP-' + form.__submitted_at.toISOString()"
|
||||
reset-on-submit
|
||||
/>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="creator_id"
|
||||
generator="() => { return JSON.parse( document.body.dataset.user ?? '{}' ).id; }"
|
||||
/>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="timestamps.created"
|
||||
generator="(_input, form) => form.__submitted_at.toISOString()"
|
||||
reset-on-submit
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
name="timestamps.updated"
|
||||
generator="(_input, form) => form.__submitted_at.toISOString()"
|
||||
reset-on-submit
|
||||
/>
|
||||
|
||||
<input type="hidden" name="parent_id" reset-on-submit />
|
||||
|
||||
<input type="hidden" name="data.reaction" reset-on-submit />
|
||||
<ul id="reactions-emojis-list">
|
||||
${Object.keys(EMOJIS.MAP)
|
||||
.map(
|
||||
(emoji) =>
|
||||
`<li data-emoji="${emoji}" data-names="${EMOJIS.MAP[emoji].map((name) => `:${name}:`).join(" ")}">${emoji}</li>`,
|
||||
)
|
||||
.join("\n")}
|
||||
</ul>
|
||||
</form>
|
||||
<div id="reactions-names-display"></div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(reactions_popup);
|
||||
|
||||
reactions_popup_form = document.getElementById("reactions-selection-form");
|
||||
document.addEventListener("topic_changed", ({ detail: { topic_id } }) => {
|
||||
const reaction_topic_id = topic_id ?? document.body.dataset.topic;
|
||||
reactions_popup_form.action = reaction_topic_id
|
||||
? `/api/topics/${reaction_topic_id}/events`
|
||||
: "";
|
||||
});
|
||||
|
||||
reactions_popup_parent_id_input = reactions_popup_form.querySelector('[name="parent_id"]');
|
||||
reactions_popup_emojis_list = document.getElementById("reactions-emojis-list");
|
||||
reactions_popup_reaction_input = reactions_popup_form.querySelector('[name="data.reaction"]');
|
||||
reactions_popup_names_display = document.getElementById("reactions-names-display");
|
||||
|
||||
reactions_popup_emojis_list.querySelectorAll("li[data-emoji]").forEach((emoji_selector) => {
|
||||
emoji_selector.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const selector = event.target;
|
||||
const emoji = selector.dataset.emoji;
|
||||
|
||||
reactions_popup_reaction_input.value = emoji;
|
||||
reactions_popup_form.requestSubmit();
|
||||
});
|
||||
});
|
||||
|
||||
reactions_popup_form.addEventListener("mouseover", (event) => {
|
||||
reactions_popup_names_display.textContent = event.target.matches("li[data-names]")
|
||||
? event.target.dataset.names
|
||||
: "";
|
||||
});
|
||||
|
||||
const reactions_popup_search = debounce((event) => {
|
||||
const prompt = event.target?.value;
|
||||
const filtered = EMOJIS.autocomplete(prompt);
|
||||
|
||||
delete emojis_list.dataset.filtered;
|
||||
if (filtered.length) {
|
||||
emojis_list.dataset.filtered = true;
|
||||
emojis_list.querySelectorAll("li").forEach((li) => {
|
||||
if (filtered.some((entry) => entry[0] === li.dataset.emoji)) {
|
||||
li.dataset.filtered = true;
|
||||
} else {
|
||||
delete li.dataset.filtered;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 200);
|
||||
|
||||
document
|
||||
.getElementById("reactions-search-input")
|
||||
.addEventListener("input", reactions_popup_search);
|
||||
document
|
||||
.getElementById("reactions-search-input")
|
||||
.addEventListener("paste", reactions_popup_search);
|
||||
document
|
||||
.getElementById("reactions-search-input")
|
||||
.addEventListener("change", reactions_popup_search);
|
||||
|
||||
document.querySelector("body").addEventListener("click", (event) => {
|
||||
const is_a_data_reactions_child = event?.target?.closest("[data-reactions]");
|
||||
if (!is_a_data_reactions_child) {
|
||||
clear_reactions_popup();
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
open_reactions_popup(event);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue