backend for bookmark menu
This commit is contained in:
121
data/static/js/bits/bookmark-menu.js
Normal file
121
data/static/js/bits/bookmark-menu.js
Normal file
@@ -0,0 +1,121 @@
|
||||
async function getHTML(endpoint, options = {}) {
|
||||
let query = {};
|
||||
if (options._query !== undefined) {
|
||||
query = options._query;
|
||||
delete options._query;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(query);
|
||||
const res = await fetch(`${endpoint}?${params}`, options);
|
||||
if (!res.ok) {
|
||||
console.error(res);
|
||||
}
|
||||
|
||||
return { body: await res.text(), status: res.status };
|
||||
}
|
||||
|
||||
export const b = {
|
||||
bookmarksCollectionEndpoint: '/hyperapi/bookmarks/dropdown/',
|
||||
bookmarkMenuState: {},
|
||||
}
|
||||
|
||||
export async function showBookmarkMenu(ev, sender, el) {
|
||||
if (b.bookmarkMenuState.state === undefined) {
|
||||
el.addEventListener('toggle', e => {
|
||||
if (e.newState === 'closed') {
|
||||
b.bookmarkMenuState.state = 'closed';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// dismiss if open and last invoker is the same button that opened it
|
||||
if (b.bookmarkMenuState.state === 'open' && b.bookmarkMenuState.invoker === sender) {
|
||||
el.hidePopover();
|
||||
return;
|
||||
}
|
||||
|
||||
b.bookmarkMenuState.invoker = sender;
|
||||
b.bookmarkMenuState.state = 'open';
|
||||
b.send({ 'plain': 'Loading…' }, 'fillBookmarkMenu');
|
||||
el.showPopover();
|
||||
const bRect = sender.getBoundingClientRect();
|
||||
const menuRect = el.getBoundingClientRect();
|
||||
const preferredLeft = bRect.right - menuRect.width;
|
||||
const enoughSpace = preferredLeft >= 0;
|
||||
|
||||
const scrollY = window.scrollY;
|
||||
|
||||
if (enoughSpace) {
|
||||
el.style.left = `${preferredLeft}px`;
|
||||
} else {
|
||||
el.style.left = `${bRect.left}px`;
|
||||
}
|
||||
el.style.top = `${bRect.bottom + scrollY}px`;
|
||||
|
||||
b.bookmarkMenuState.kind = sender.dataset.conceptKind;
|
||||
b.bookmarkMenuState.id = sender.dataset.conceptId;
|
||||
|
||||
const bookmarkCollections = await getHTML(b.bookmarksCollectionEndpoint, {
|
||||
_query: {
|
||||
concept_kind: b.bookmarkMenuState.kind,
|
||||
concept_id: b.bookmarkMenuState.id,
|
||||
}
|
||||
});
|
||||
b.send({ 'html': bookmarkCollections.body }, 'fillBookmarkMenu');
|
||||
}
|
||||
|
||||
export function fillBookmarkMenu(payload, __, el) {
|
||||
if (payload.plain) {
|
||||
el.innerText = payload.plain;
|
||||
return;
|
||||
}
|
||||
|
||||
el.innerHTML = payload.html;
|
||||
}
|
||||
|
||||
export async function bookmarkMenuSubmit(ev, _, el) {
|
||||
ev.preventDefault();
|
||||
const url = el.action;
|
||||
const body = new URLSearchParams(new FormData(el));
|
||||
const options = { body: body, method: 'POST' };
|
||||
const status = (await getHTML(url, options)).status;
|
||||
|
||||
if (status !== 204) {
|
||||
b.trigger('bookmarkMenuShowError');
|
||||
return;
|
||||
}
|
||||
|
||||
const newCollections = await getHTML(b.bookmarksCollectionEndpoint, {
|
||||
_query: {
|
||||
concept_kind: b.bookmarkMenuState.kind,
|
||||
concept_id: b.bookmarkMenuState.id,
|
||||
saved: true,
|
||||
}
|
||||
});
|
||||
b.send({ 'html': newCollections.body }, 'fillBookmarkMenu');
|
||||
}
|
||||
|
||||
export function bookmarkMenuResetSavedButton(_, __, el) {
|
||||
el.value = 'Save';
|
||||
}
|
||||
|
||||
export function bookmarkMenuShowError(_, __, el) {
|
||||
if (el === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (el.classList.contains('hidden')) {
|
||||
el.classList.remove('hidden');
|
||||
setTimeout(() => { b.trigger('bookmarkMenuHideError') }, 4000);
|
||||
}
|
||||
}
|
||||
|
||||
export function bookmarkMenuHideError(_, __, el) {
|
||||
if (el === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!el.classList.contains('hidden')) {
|
||||
el.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
export const b = {
|
||||
babycodePreviewEndpoint: '/api/babycode-preview/',
|
||||
bookmarksCollectionEndpoint: '/hyperapi/bookmarks/dropdown/',
|
||||
init: 'babycodeEditorCharCountInit localizeTimestamps',
|
||||
|
||||
bookmarkState: {},
|
||||
}
|
||||
|
||||
const getThreadId = () => {
|
||||
@@ -14,22 +11,6 @@ const getThreadId = () => {
|
||||
return parseInt(scheme[2]);
|
||||
}
|
||||
|
||||
async function getHTML(endpoint, options = {}) {
|
||||
let query = {};
|
||||
if (options._query !== undefined) {
|
||||
query = options._query;
|
||||
delete options._query;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(query);
|
||||
const res = await fetch(`${endpoint}?${params}`, options);
|
||||
if (!res.ok) {
|
||||
console.error(res);
|
||||
}
|
||||
|
||||
return { body: await res.text(), status: res.status };
|
||||
}
|
||||
|
||||
export function setTab(_, sender, el) {
|
||||
if (sender.ariaSelected === 'true') {
|
||||
return;
|
||||
@@ -242,101 +223,3 @@ export function localizeTimestamps(_, __, el) {
|
||||
const d = new Date(el.dateTime);
|
||||
el.innerText = d.toLocaleString();
|
||||
}
|
||||
|
||||
export async function showBookmarkMenu(ev, sender, el) {
|
||||
if (b.bookmarkState.state === undefined) {
|
||||
el.addEventListener('toggle', e => {
|
||||
if (e.newState === 'closed') {
|
||||
b.bookmarkState.state = 'closed';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// dismiss if open and last invoker is the same button that opened it
|
||||
if (b.bookmarkState.state === 'open' && b.bookmarkState.invoker === sender) {
|
||||
el.hidePopover();
|
||||
return;
|
||||
}
|
||||
|
||||
b.bookmarkState.invoker = sender;
|
||||
b.bookmarkState.state = 'open';
|
||||
b.send({ 'plain': 'Loading…' }, 'fillBookmarkMenu');
|
||||
el.showPopover();
|
||||
const bRect = sender.getBoundingClientRect();
|
||||
const menuRect = el.getBoundingClientRect();
|
||||
const preferredLeft = bRect.right - menuRect.width;
|
||||
const enoughSpace = preferredLeft >= 0;
|
||||
|
||||
const scrollY = window.scrollY;
|
||||
|
||||
if (enoughSpace) {
|
||||
el.style.left = `${preferredLeft}px`;
|
||||
} else {
|
||||
el.style.left = `${bRect.left}px`;
|
||||
}
|
||||
el.style.top = `${bRect.bottom + scrollY}px`;
|
||||
|
||||
const conceptKind = sender.prop('conceptKind');
|
||||
const conceptId = sender.prop('conceptId');
|
||||
|
||||
const bookmarkCollections = await getHTML(b.bookmarksCollectionEndpoint, {
|
||||
_query: {
|
||||
concept_kind: conceptKind,
|
||||
concept_id: conceptId,
|
||||
}
|
||||
});
|
||||
b.send({ 'html': bookmarkCollections.body }, 'fillBookmarkMenu');
|
||||
}
|
||||
|
||||
|
||||
export function fillBookmarkMenu(payload, __, el) {
|
||||
if (payload.plain) {
|
||||
el.innerText = payload.plain;
|
||||
return;
|
||||
}
|
||||
|
||||
el.innerHTML = payload.html;
|
||||
}
|
||||
|
||||
export async function bookmarkMenuSubmit(ev, _, el) {
|
||||
ev.preventDefault();
|
||||
const url = el.action;
|
||||
const body = new URLSearchParams(new FormData(el));
|
||||
const options = { body: body, method: 'POST' };
|
||||
const status = (await getHTML(url, options)).status;
|
||||
|
||||
if (status !== 204) {
|
||||
b.trigger('bookmarkMenuShowError');
|
||||
return;
|
||||
}
|
||||
b.trigger('bookmarkMenuShowSavedButton');
|
||||
}
|
||||
|
||||
export function bookmarkMenuShowSavedButton(_, __, el) {
|
||||
el.value = 'Saved!';
|
||||
}
|
||||
|
||||
export function bookmarkMenuResetSavedButton(_, __, el) {
|
||||
el.value = 'Save';
|
||||
}
|
||||
|
||||
export function bookmarkMenuShowError(_, __, el) {
|
||||
if (el === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (el.classList.contains('hidden')) {
|
||||
el.classList.remove('hidden');
|
||||
setTimeout(() => { b.trigger('bookmarkMenuHideError') }, 4000);
|
||||
}
|
||||
}
|
||||
|
||||
export function bookmarkMenuHideError(_, __, el) {
|
||||
if (el === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!el.classList.contains('hidden')) {
|
||||
el.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user