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:
		
					parent
					
						
							
								b97c462d2e
							
						
					
				
			
			
				commit
				
					
						9b6e3b61cf
					
				
			
		
					 2 changed files with 37 additions and 15 deletions
				
			
		| 
						 | 
				
			
			@ -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'];
 | 
			
		||||
 | 
			
		||||
  alive = false;
 | 
			
		||||
  contentSpan = null;
 | 
			
		||||
 | 
			
		||||
  update = (recurring) => {
 | 
			
		||||
    if (!this.alive) return;
 | 
			
		||||
 | 
			
		||||
    if (!this.shadowRoot) {
 | 
			
		||||
      this.attachShadow({mode: 'open'});
 | 
			
		||||
      this.contentSpan = document.createElement('span');
 | 
			
		||||
      this.shadowRoot.append(this.contentSpan);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
/** 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);
 | 
			
		||||
    const next = DoUpdateRelativeTime(this);
 | 
			
		||||
    if (recurring && next !== null) setTimeout(() => { this.update(true) }, next);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  connectedCallback() {
 | 
			
		||||
    this.alive = true;
 | 
			
		||||
    this.update(true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
  });
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue