async function CachedWidget(
name,
input,
{
storageName = "CachedWidgetDB",
showControls = true,
template = (input, controls) => htl.html`<div>${input}${controls}</div>`,
loadFromCacheCondition = (v) =>
v === "" ||
v === undefined ||
v === null ||
(Array.isArray(v) && v?.length === 0),
debug = false,
blob = false,
schema = ""
} = {}
) {
if (
!input?.addEventListener ||
typeof input?.addEventListener !== "function"
) {
throw new Error(
"second parameter should be an input and support addEventListener"
);
}
let value = input?.value;
let originalValue = value;
let loadedFromCache = false;
const db = new Dexie(storageName);
db.version(1).stores({ data: schema });
const btnClear = htl.html`<button>Clear cache</button>`;
const info = htl.html`<output></output>`;
// if (loadFromCacheCondition(value)) {
debug &&
console.log("🤔 Value seems empty, trying to load from cache", value);
// try to load from cache
await loadFromCache();
// }
// *** Load from Cache ***
async function loadFromCache() {
const before = performance.now();
debug && console.log("Loading from cache", name);
try {
const res = await db.data.get(name);
if (!res) {
debug && console.log("No value found", name);
info.innerText = "🕳️";
return;
}
loadedFromCache = true;
debug &&
console.log(
"data loaded from cache",
res,
` in ${(performance.now() - before) / 1000}`
);
info.innerText = "🛟";
value = blob ? fromBlob(res) : res;
input.value = value;
input.dispatchEvent(new Event("input", { bubbles: true }));
} catch (err) {
info.innerText = "❌";
console.error(err.message);
}
}
function showAndCacheValue() {
input.value = value;
const before = performance.now();
debug && console.log("🛟 Caching data", value);
info.innerText = "⏳";
const valueToStore = blob
? toBlob(value)
: value;
db.data
.put(valueToStore, name)
.then((res) => {
debug &&
console.log(
"data stored",
name,
value,
`in ${(performance.now() - before) / 1000}`
);
info.innerText = "✅";
})
.catch((err) => {
console.error(err.message);
info.innerText = "❌";
});
}
btnClear.addEventListener("click", () => {
db.data.delete(name).then(() => {
value = originalValue;
info.innerText = "🕳️";
debug && console.log("Data Cleared");
});
});
const controls = showControls
? htl.html`<div>
${info}
${btnClear}</div>`
: htl.html``;
const wrappedInput = template(input, controls);
const widget = ReactiveWidget(wrappedInput, {
value,
showValue: showAndCacheValue
});
if (!loadedFromCache) showAndCacheValue();
// Listen for changes on the input to store them in the cache
input.addEventListener("input", (evt) => {
debug && console.log("✉️ Input value changed", input.value, value);
evt.stopPropagation();
info.innerText = "Input value changed";
value = input.value;
// showAndCacheValue();
widget.setValue(input.value);
});
widget.addEventListener("input", (evt) => {
debug &&
console.log("❤️ Input value changed", input.value, value, widget.value);
value = widget.value;
showAndCacheValue();
});
widget.clearCache = () => {
db.data.delete(name);
widget.setValue(originalValue);
};
return widget;
}