fix: use leading edge debounce to sync up transitions

This commit is contained in:
dragonwocky 2024-01-24 12:50:13 +11:00
parent 26c9638013
commit ff002044aa
Signed by: dragonwocky
GPG Key ID: 7998D08F7D7BD7A8
2 changed files with 31 additions and 17 deletions

View File

@ -6,19 +6,28 @@
"use strict"; "use strict";
// convenient util: batch event handling // batch event callbacks to avoid over-handling
// every ___ ms to avoid over-handling & // and any conflicts / perf.issues that may
// any conflicts / perf.issues that may // otherwise result. initial call is immediate,
// otherwise result. a wait time of ~200ms // following calls are delayed. a wait time of
// is recommended (the avg. human visual // ~200ms is recommended (the avg. human visual
// reaction time is ~180-200ms) // reaction time is ~180-200ms)
const debounce = (callback, wait = 200) => { const sleep = async (ms) => {
let timeoutId; return new Promise((res, rej) => setTimeout(res, ms));
return (...args) => { },
clearTimeout(timeoutId); debounce = (callback, ms = 200) => {
timeoutId = setTimeout(() => callback(...args), wait); let delay, update;
const next = () =>
sleep(ms).then(() => {
if (!update) return (delay = undefined);
update(), (update = undefined);
delay = next();
});
return (...args) => {
if (delay) update = callback.bind(this, ...args);
return delay || ((delay = next()), callback(...args));
};
}; };
};
// provides basic key/value reactivity: // provides basic key/value reactivity:
// this is shared between all active mods, // this is shared between all active mods,
@ -156,6 +165,7 @@ document.addEventListener("keydown", (event) => {
}); });
Object.assign((globalThis.__enhancerApi ??= {}), { Object.assign((globalThis.__enhancerApi ??= {}), {
sleep,
debounce, debounce,
setState, setState,
useState, useState,

View File

@ -8,7 +8,7 @@
import { Button } from "./Button.mjs"; import { Button } from "./Button.mjs";
function Footer({ categories }) { function Footer({ categories, transitionDuration = 150 }) {
const { html, setState, useState, reloadApp } = globalThis.__enhancerApi, const { html, setState, useState, reloadApp } = globalThis.__enhancerApi,
$reload = html`<${Button} $reload = html`<${Button}
class="ml-auto" class="ml-auto"
@ -33,12 +33,16 @@ function Footer({ categories }) {
useState(["view"], ([view]) => { useState(["view"], ([view]) => {
let [footerOpen] = useState(["databaseUpdated"]); let [footerOpen] = useState(["databaseUpdated"]);
for (const [ids, $btn] of $categories) { footerOpen ||= $categories.some(([ids]) => ids.some((id) => id === view));
const modInCategory = ids.some((id) => id === view);
if (modInCategory) footerOpen = true;
$btn.style.display = modInCategory ? "" : "none";
}
setState({ footerOpen }); setState({ footerOpen });
if (!footerOpen) return;
// only toggle buttons if footer is open,
// otherwise leave as is during transition
for (const [ids, $btn] of $categories) {
const viewInCategory = ids.some((id) => id === view);
$btn.style.display = viewInCategory ? "" : "none";
}
}); });
useState(["databaseUpdated"], ([databaseUpdated]) => { useState(["databaseUpdated"], ([databaseUpdated]) => {
$reload.style.display = databaseUpdated ? "" : "none"; $reload.style.display = databaseUpdated ? "" : "none";