bring back the badge editor
This commit is contained in:
162
data/static/js/bits/badge-editor.js
Normal file
162
data/static/js/bits/badge-editor.js
Normal file
@@ -0,0 +1,162 @@
|
||||
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);
|
||||
|
||||
return { body: await res.text(), status: res.status };
|
||||
}
|
||||
|
||||
const validateBase64Img = dataURL => new Promise(resolve => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
resolve(img.width === 88 && img.height === 31);
|
||||
};
|
||||
img.src = dataURL;
|
||||
});
|
||||
|
||||
export const b = {
|
||||
init: 'badgeEditorInit',
|
||||
}
|
||||
|
||||
const badgeEditorEndpoint = '/hyperapi/badges/editor/'
|
||||
const MAX_BADGES = 10;
|
||||
let badgesCount = 0;
|
||||
let customImageDatas = {};
|
||||
|
||||
export async function badgeEditorInit(_, __, el) {
|
||||
const res = await getHTML(badgeEditorEndpoint);
|
||||
if (res.status != 200) {
|
||||
return;
|
||||
}
|
||||
el.innerHTML = res.body;
|
||||
badgesCount = el.querySelectorAll('.sortable-item').length;
|
||||
b.trigger('badgeEditorAssignImgId');
|
||||
b.trigger('setBadgeCount');
|
||||
}
|
||||
|
||||
export function badgeEditorAssignImgId(_, __, el) {
|
||||
if (el.dataset.imgId) return;
|
||||
|
||||
const id = b.uuid();
|
||||
const filePicker = el.querySelector('input[type=file]');
|
||||
const img = el.querySelector('img.badge-button');
|
||||
console.log(img);
|
||||
el.dataset.imgId = id;
|
||||
filePicker.dataset.imgId = id;
|
||||
img.dataset.imgId = id;
|
||||
}
|
||||
|
||||
export function badgeEditorSetPreview(ev, sender, el) {
|
||||
if (!sender.parentNode.contains(el)) return;
|
||||
|
||||
const selectedItem = sender.selectedOptions[0];
|
||||
if (selectedItem.value !== 'custom') {
|
||||
el.src = selectedItem.dataset.filePath;
|
||||
} else if (customImageDatas[el.dataset.imgId]) {
|
||||
el.src = customImageDatas[el.dataset.imgId];
|
||||
} else {
|
||||
el.removeAttribute('src');
|
||||
}
|
||||
}
|
||||
|
||||
export function badgeEditorSetPreviewCustom(payload, _, el) {
|
||||
if (!payload.badge.contains(el)) return;
|
||||
if (!customImageDatas[el.dataset.imgId]) {
|
||||
el.removeAttribute('src');
|
||||
} else {
|
||||
el.src = customImageDatas[el.dataset.imgId];
|
||||
}
|
||||
}
|
||||
|
||||
export function badgeEditorToggleFilePicker(ev, sender, el) {
|
||||
if (!sender.parentNode.parentNode.contains(el)) return;
|
||||
|
||||
const selectedItem = sender.selectedOptions[0];
|
||||
const picker = el.querySelector('input[type=file]');
|
||||
if (selectedItem.value !== 'custom') {
|
||||
el.classList.add('hidden');
|
||||
picker.required = false;
|
||||
picker.setCustomValidity('');
|
||||
} else {
|
||||
el.classList.remove('hidden');
|
||||
picker.required = true;
|
||||
picker.setCustomValidity(picker.dataset.validity || '');
|
||||
}
|
||||
}
|
||||
|
||||
export function badgeEditorAddBadge(ev, sender, el) {
|
||||
// TODO: page templates do not get updated on mutation
|
||||
const badge = document.getElementById('badge-template').innerText;
|
||||
el.innerHTML += badge;
|
||||
b.trigger('badgeEditorAssignImgId');
|
||||
badgesCount++;
|
||||
b.trigger('setBadgeCount');
|
||||
}
|
||||
|
||||
export function badgeEditorDelete(ev, sender, el) {
|
||||
if (!el.contains(sender)) return;
|
||||
el.remove();
|
||||
badgesCount--;
|
||||
b.trigger('setBadgeCount');
|
||||
}
|
||||
|
||||
export function badgeEditorShowFilePicker(ev, sender, el) {
|
||||
if (sender.nextElementSibling !== el) return;
|
||||
el.showPicker();
|
||||
}
|
||||
|
||||
export async function badgeEditorFileSelected(ev, sender, el) {
|
||||
const file = sender.files[0];
|
||||
const badge = sender.parentNode.parentNode;
|
||||
|
||||
if (
|
||||
!['image/png', 'image/jpeg', 'image/jpg', 'image/webp'].includes(file.type)
|
||||
) {
|
||||
sender.dataset.validity = 'The badge file must be an image.';
|
||||
sender.setCustomValidity(sender.dataset.validity);
|
||||
sender.reportValidity();
|
||||
customImageDatas[sender.dataset.imgId] = null;
|
||||
b.send({ badge: badge }, 'badgeEditorSetPreviewCustom');
|
||||
return;
|
||||
}
|
||||
if (file.size >= 1000 * 500) {
|
||||
sender.dataset.validity = 'The badge image must be smaller than 500KB.';
|
||||
sender.setCustomValidity(sender.dataset.validity);
|
||||
sender.reportValidity();
|
||||
customImageDatas[sender.dataset.imgId] = null;
|
||||
b.send({ badge: badge }, 'badgeEditorSetPreviewCustom');
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = async e => {
|
||||
const dimsValid = await validateBase64Img(e.target.result);
|
||||
if (!dimsValid) {
|
||||
sender.setCustomValidity('The badge image must be exactly 88x31 pixels.');
|
||||
sender.reportValidity();
|
||||
customImageDatas[sender.dataset.imgId] = null;
|
||||
b.send({ badge: badge }, 'badgeEditorSetPreviewCustom');
|
||||
return;
|
||||
}
|
||||
customImageDatas[sender.dataset.imgId] = e.target.result;
|
||||
|
||||
sender.dataset.validity = '';
|
||||
sender.setCustomValidity('');
|
||||
b.send({ badge: badge }, 'badgeEditorSetPreviewCustom');
|
||||
}
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
export function setBadgeCount(_, __, el) {
|
||||
if (el instanceof HTMLButtonElement) {
|
||||
el.disabled = badgesCount === MAX_BADGES;
|
||||
} else {
|
||||
el.innerText = `${badgesCount}/${MAX_BADGES}`;
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,9 @@
|
||||
if (!target || target === draggedItem) {
|
||||
return;
|
||||
}
|
||||
if (draggedItem === null) {
|
||||
return;
|
||||
}
|
||||
const inSameList = draggedItem.dataset.sortableListKey === target.dataset.sortableListKey;
|
||||
if (!inSameList) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user