Make relative-time a self-maintaining custom element (#8134)

Fixes #8124
Replaces #8130

Use a custom element for relative-time. Thanks to @Beowulf for suggesting this approach.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8134
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: Benedikt Straub <benedikt-straub@web.de>
Co-committed-by: Benedikt Straub <benedikt-straub@web.de>
This commit is contained in:
Benedikt Straub 2025-06-10 20:12:21 +02:00 committed by Earl Warren
parent b97c462d2e
commit 9b6e3b61cf
2 changed files with 37 additions and 15 deletions

View file

@ -130,21 +130,42 @@ export function DoUpdateRelativeTime(object, now) {
return HALF_MINUTE;
}
/** Update the displayed text of one relative-time DOM element with its human-readable, localized relative time string. */
function UpdateRelativeTime(object) {
const next = DoUpdateRelativeTime(object);
if (next !== null) setTimeout(() => { UpdateRelativeTime(object) }, next);
}
window.customElements.define('relative-time', class extends HTMLElement {
static observedAttributes = ['datetime'];
/** Update the displayed text of all relative-time DOM elements with their respective human-readable, localized relative time string. */
function UpdateAllRelativeTimes() {
for (const object of document.querySelectorAll('relative-time')) UpdateRelativeTime(object);
}
alive = false;
contentSpan = null;
document.addEventListener('DOMContentLoaded', () => {
UpdateAllRelativeTimes();
// Also update relative-time DOM elements after htmx swap events.
document.body.addEventListener('htmx:afterSwap', () => {
for (const object of document.querySelectorAll('relative-time')) DoUpdateRelativeTime(object);
});
update = (recurring) => {
if (!this.alive) return;
if (!this.shadowRoot) {
this.attachShadow({mode: 'open'});
this.contentSpan = document.createElement('span');
this.shadowRoot.append(this.contentSpan);
}
const next = DoUpdateRelativeTime(this);
if (recurring && next !== null) setTimeout(() => { this.update(true) }, next);
};
connectedCallback() {
this.alive = true;
this.update(true);
}
disconnectedCallback() {
this.alive = false;
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'datetime' && oldValue !== newValue) this.update(false);
}
set textContent(value) {
if (this.contentSpan) this.contentSpan.textContent = value;
}
get textContent() {
return this.contentSpan?.textContent;
}
});

View file

@ -23,6 +23,7 @@ test('CalculateRelativeTimes', () => {
'relativetime.years': ['%d year ago', '%d years ago'],
};
const mock = document.createElement('relative-time');
document.body.append(mock);
const now = Date.parse('2024-10-27T04:05:30+01:00'); // One hour after DST switchover, CET.