This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Työprojektissa olen käyttänyt ja käyttänyt väärin HTMX:ää admin-UI:n rakentamiseen. Osana tätä käytän ihanaa SweetAlert2 Javascript-kirjasto Vahvistusikkunoilleni...................................................................................................................................... Se toimii hyvin, mutta halusin käyttää niitä myös HTMX-latausmittareideni korvaamiseen.
Tämä osoittautui haasteeksi, joten ajattelin dokumentoida sen täällä säästääkseni sinulta saman tuskan.
Warning I'm a C# coder my Javascript is likely horrible.
[TOC]Joten HTMX on erittäin fiksu, se on hx-indicator
Normaalisti voit asettaa latausmittarin HTMX-pyyntöihisi. Tavallisesti tämä on HTML-elementti sivullasi, kuten
<div id="loading-modal" class="modal htmx-indicator">
<div class="modal-box flex flex-col items-center justify-center bg-base-200 border border-base-300 shadow-xl rounded-xl text-base-content dark text-center ">
<div class="flex flex-col items-center space-y-4">
<h2 class="text-lg font-semibold tracking-wide">Loading...</h2>
<span class="loading loading-dots loading-xl text-4xl text-stone-200"></span>
</div>
</div>
</div>
Kun haluat käyttää sitä, koristelet HTMX-pyyntösi hx-indicator="#loading-modal"
ja se näyttää liikennemuodon, kun pyyntö on käynnissä (katso tarkemmat tiedot täältä).
Nyt HTMX tekee nokkelaa taikaa käyttäen request
esine se seuraa sisäisesti
function addRequestIndicatorClasses(elt) {
let indicators = /** @type Element[] */ (findAttributeTargets(elt, 'hx-indicator'))
if (indicators == null) {
indicators = [elt]
}
forEach(indicators, function(ic) {
const internalData = getInternalData(ic)
internalData.requestCount = (internalData.requestCount || 0) + 1
ic.classList.add.call(ic.classList, htmx.config.requestClass)
})
return indicators
}
Siksi näiden korvaaminen on hieman haasteellista. Miten voit seurata pyyntöjä ja sitten näyttää SweetAlert2-moodin, kun pyyntö on käynnissä, ja piilottaa sen, kun se on valmis.
Niinpä ryhdyin (ei siksi, että minun oli pakko, koska tarvitsin :) korvaamaan HTMX-latausmittarin SweetAlert2-modaalilla. Tässä on koodi, jonka keksin.
Aloita tuomalla SweetAlert2 HTML:ssäsi (skripti- ja tyylitageina) tai tuomalla se webpackille tai vastaavalle (katso tästä heidän dokumenttejaan).
Kun npm on asennettu, voit tuoda sen näin JS-tiedostoosi.
import Swal from 'sweetalert2';
Sitten pääkoodini näyttää tältä:
import Swal from 'sweetalert2';
const SWEETALERT_PATH_KEY = 'swal-active-path'; // Stores the path of the current SweetAlert
const SWEETALERT_HISTORY_RESTORED_KEY = 'swal-just-restored'; // Flag for navigation from browser history
const SWEETALERT_TIMEOUT_MS = 10000; // Timeout for automatically closing the loader
let swalTimeoutHandle = null;
export function registerSweetAlertHxIndicator() {
document.body.addEventListener('htmx:configRequest', function (evt) {
const trigger = evt.detail.elt;
const indicatorAttrSource = getIndicatorSource(trigger);
if (!indicatorAttrSource) return;
const path = getRequestPath(evt.detail);
if (!path) return;
const currentPath = sessionStorage.getItem(SWEETALERT_PATH_KEY);
// Show SweetAlert only if the current request path differs from the previous one
if (currentPath !== path) {
closeSweetAlertLoader();
sessionStorage.setItem(SWEETALERT_PATH_KEY, path);
evt.detail.indicator = null; // Disable HTMX's default indicator behavior
Swal.fire({
title: 'Loading...',
allowOutsideClick: false,
allowEscapeKey: false,
showConfirmButton: false,
theme: 'dark',
didOpen: () => {
// Cancel immediately if restored from browser history
if (sessionStorage.getItem(SWEETALERT_HISTORY_RESTORED_KEY) === 'true') {
sessionStorage.removeItem(SWEETALERT_HISTORY_RESTORED_KEY);
Swal.close();
return;
}
Swal.showLoading();
document.dispatchEvent(new CustomEvent('sweetalert:opened'));
// Set timeout to auto-close if something hangs
clearTimeout(swalTimeoutHandle);
swalTimeoutHandle = setTimeout(() => {
if (Swal.isVisible()) {
console.warn('SweetAlert loading modal closed after timeout.');
closeSweetAlertLoader();
}
}, SWEETALERT_TIMEOUT_MS);
},
didClose: () => {
document.dispatchEvent(new CustomEvent('sweetalert:closed'));
sessionStorage.removeItem(SWEETALERT_PATH_KEY);
clearTimeout(swalTimeoutHandle);
swalTimeoutHandle = null;
}
});
} else {
// Suppress HTMX indicator if the path is already being handled
evt.detail.indicator = null;
}
});
//Add events to close the loader
document.body.addEventListener('htmx:afterRequest', maybeClose);
document.body.addEventListener('htmx:responseError', maybeClose);
document.body.addEventListener('sweetalert:close', closeSweetAlertLoader);
// Set a flag so we can suppress the loader immediately if navigating via browser history
document.body.addEventListener('htmx:historyRestore', () => {
sessionStorage.setItem(SWEETALERT_HISTORY_RESTORED_KEY, 'true');
});
window.addEventListener('popstate', () => {
sessionStorage.setItem(SWEETALERT_HISTORY_RESTORED_KEY, 'true');
});
}
// Returns the closest element with an indicator attribute
function getIndicatorSource(el) {
return el.closest('[hx-indicator], [data-hx-indicator]');
}
// Determines the request path, including query string if appropriate
function getRequestPath(detail) {
const responsePath =
typeof detail?.pathInfo?.responsePath === 'string'
? detail.pathInfo.responsePath
: (typeof detail?.pathInfo?.path === 'string'
? detail.pathInfo.path
: (typeof detail?.path === 'string' ? detail.path : '')
);
const elt = detail.elt;
// If not a form and has an hx-indicator, use the raw path
if (elt.hasAttribute("hx-indicator") && elt.tagName !== "FORM") {
return responsePath;
}
const isGet = (detail.verb ?? '').toUpperCase() === 'GET';
const form = elt.closest('form');
// Append query params for GET form submissions
if (isGet && form) {
const params = new URLSearchParams();
for (const el of form.elements) {
if (!el.name || el.disabled) continue;
const type = el.type;
if ((type === 'checkbox' || type === 'radio') && !el.checked) continue;
if (type === 'submit') continue;
params.append(el.name, el.value);
}
const queryString = params.toString();
return queryString ? `${responsePath}?${queryString}` : responsePath;
}
return responsePath;
}
// Closes the SweetAlert loader if the path matches
function maybeClose(evt) {
const activePath = sessionStorage.getItem(SWEETALERT_PATH_KEY);
const path = getRequestPath(evt.detail);
if (activePath && path && activ