camper/web/static/camper.js

219 lines
6.8 KiB
JavaScript

/**
* SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
* SPDX-License-Identifier: AGPL-3.0-only
*/
function ready(fn) {
if (document.readyState !== 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(function () {
const snackBar = Object.assign(document.body.appendChild(document.createElement('section')), {
id: 'snackbar',
});
const errorMessage = snackBar.appendChild(document.createElement('div'));
errorMessage.setAttribute('role', 'alert');
const openClass = 'open';
const toasts = [];
let timeoutId = null;
function showError(message) {
toasts.push(message);
popUp();
}
function popUp() {
if (toasts.length === 0) {
return;
}
if (errorMessage.classList.contains(openClass)) {
dismiss();
return;
}
if (errorMessage.innerText !== "") {
// it will show after remove calls popUp again.
return;
}
errorMessage.innerText = toasts[0];
errorMessage.classList.add(openClass);
timeoutId = setTimeout(dismiss, 4000);
}
function dismiss() {
if (!errorMessage.classList.contains(openClass)) {
// already dismissed
return;
}
errorMessage.classList.remove(openClass);
clearTimeout(timeoutId);
timeoutId = setTimeout(remove, 350);
}
function remove() {
clearTimeout(timeoutId);
toasts.splice(0, 1);
errorMessage.innerText = "";
popUp();
}
document.body.addEventListener('htmx:error', function (evt) {
const errorInfo = evt.detail.errorInfo;
const error = errorInfo.xhr && errorInfo.xhr.responseText || errorInfo.error;
showError(error);
});
})
ready(function () {
const textareas = document.querySelectorAll('textarea.html');
if (textareas.length > 0) {
const language = document.documentElement.getAttribute('lang');
const script = document.head.appendChild(Object.assign(document.createElement('script'), {
src: '/static/ckeditor5@39.0.1/ckeditor.js',
}));
document.head.appendChild(Object.assign(document.createElement('style'), {
innerHTML: '.ck-content { margin-left: 5rem; }',
}));
if (language !== 'en') {
document.head.appendChild(Object.assign(document.createElement('script'), {
src: '/static/ckeditor5@39.0.1/translations/' + language + '.js',
}));
}
script.addEventListener('load', function () {
for (const textarea of textareas) {
const canvas = document.createElement('div');
textarea.parentNode.insertBefore(canvas, textarea.nextSibling);
textarea.style.display = 'none';
BalloonEditor
.create(canvas, {
language,
})
.then(editor => {
const xml = document.createElement('div');
const serializer = new XMLSerializer();
editor.setData(textarea.value);
editor.ui.focusTracker.on('change:isFocused', (event, name, focused) => {
if (!focused) {
xml.innerHTML = editor.getData();
textarea.value = serializer.serializeToString(xml).replace('&nbsp;', '&#xA0;');
}
});
})
.catch(error => {
console.error(error);
});
}
});
}
})
export function camperUploadForm(el) {
const progress = el.querySelector('progress');
htmx.on(el, 'drop', function (evt) {
evt.preventDefault();
[...evt.dataTransfer.items].forEach(function (i) {
console.log(i);
i.getAsString(console.log)
});
});
htmx.on(el, 'dragover', function (evt) {
evt.preventDefault();
});
htmx.on(el, 'dragleave', function (evt) {
evt.preventDefault();
});
htmx.on(el, 'htmx:xhr:progress', function (evt) {
if (progress && evt.detail.lengthComputable) {
progress.setAttribute('value', evt.detail.loaded / evt.detail.total * 100);
}
});
}
export function setupCampgroundMap(map) {
if (!map) {
return;
}
const prefix = "cp_";
for (const campsite of Array.from(map.querySelectorAll(`[id^="${prefix}"]`))) {
const label = campsite.id.substring(prefix.length);
if (!label) {
continue;
}
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a');
link.setAttributeNS('http://www.w3.org/1999/xlink', 'href', '/admin/campsites/' + label);
link.append(...campsite.childNodes);
campsite.appendChild(link);
}
}
export function setupIconInput(icon) {
if (!icon) {
return;
}
const input = icon.querySelector('input[type="hidden"]')
if (!input) {
return;
}
const buttons = Array.from(icon.querySelectorAll('button[data-icon-name]'));
const updateValue = function (iconName) {
input.value = iconName;
for (const button of buttons) {
button.setAttribute('aria-pressed', button.dataset.iconName === iconName);
}
}
for (const button of buttons) {
button.addEventListener('click', function (e) {
updateValue(e.target.dataset.iconName);
})
}
updateValue(input.value);
}
export function setupCalendar(calendar) {
const startDate = calendar.querySelector('input[name="start_date"]');
const endDate = calendar.querySelector('input[name="end_date"]');
const days = Array.from(calendar.querySelectorAll('time'));
const selectDate = function (e) {
e.preventDefault();
const date = e.currentTarget.dateTime;
if (!date) {
return;
}
if (!startDate.value) {
startDate.value = date;
e.currentTarget.setAttribute('aria-checked', true);
return;
} else if (startDate.value > date) {
endDate.value = startDate.value;
startDate.value = date;
} else {
endDate.value = date;
}
for (const day of days) {
if (day.dateTime >= startDate.value && day.dateTime <= endDate.value) {
day.setAttribute('aria-checked', true);
} else {
day.removeAttribute('aria-checked');
}
}
}
for (const day of days) {
day.addEventListener('click', selectDate);
}
}
htmx.onLoad((target) => {
if (target.tagName === 'DIALOG') {
target.showModal();
}
})