Add relative-date-time component
This commit is contained in:
parent
52ec3f819c
commit
516a903565
@ -36,7 +36,9 @@ class Component
|
|||||||
<section id=journalItems class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 g-3" hx-target=this
|
<section id=journalItems class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 g-3" hx-target=this
|
||||||
hx-swap=outerHTML aria-label="Prayer Requests"><?php
|
hx-swap=outerHTML aria-label="Prayer Requests"><?php
|
||||||
$spacer = '<span> </span>';
|
$spacer = '<span> </span>';
|
||||||
foreach ($reqs->items as /** @var Request $req */ $req) { ?>
|
foreach ($reqs->items as /** @var Request $req */ $req) {
|
||||||
|
$lastPrayed = $req->lastPrayed;
|
||||||
|
$lastActivity = $lastPrayed->getOrDefault($req->history[0]->asOf); ?>
|
||||||
<div class=col>
|
<div class=col>
|
||||||
<div class="card h-100">
|
<div class="card h-100">
|
||||||
<div class="card-header p-0 d-flex" role=toolbar>
|
<div class="card-header p-0 d-flex" role=toolbar>
|
||||||
@ -58,10 +60,8 @@ class Component
|
|||||||
<p class=request-text><?=htmlentities($req->currentText);?>
|
<p class=request-text><?=htmlentities($req->currentText);?>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer text-end text-muted px-1 py-0">
|
<div class="card-footer text-end text-muted px-1 py-0">
|
||||||
<em><?php
|
<em>last <?=$lastPrayed->map(fn() => 'prayed')->getOrDefault('activity')?>
|
||||||
$lastPrayed = $req->lastPrayed;
|
<?=self::relativeDate($lastPrayed->getOrDefault($req->history[0]->asOf))?>
|
||||||
echo 'last ' . $lastPrayed->map(fn() => 'prayed')->getOrDefault('activity') . ' '
|
|
||||||
. self::relativeDate($lastPrayed->getOrDefault($req->history[0]->asOf)); ?>
|
|
||||||
</em>
|
</em>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -115,9 +115,9 @@ class Component
|
|||||||
public static function relativeDate(string $date): string
|
public static function relativeDate(string $date): string
|
||||||
{
|
{
|
||||||
$parsed = new DateTimeImmutable($date);
|
$parsed = new DateTimeImmutable($date);
|
||||||
$inZone = $parsed->setTimezone(new DateTimeZone($_REQUEST['time_zone']));
|
$iso = $parsed->format('c');
|
||||||
return sprintf('<span title="%s">%s</span>', date_format($inZone, 'l, F j, Y \a\t g:ia T'),
|
$title = $parsed->setTimezone(new DateTimeZone($_REQUEST['time_zone']))->format('l, F j, Y \a\t g:ia T');
|
||||||
RelativeDate::between('now', $parsed));
|
return "<relative-date-time title=\"$title\" interval=5000>$iso</relative-date-time>";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
/** myPrayerJournal script */
|
/** myPrayerJournal script */
|
||||||
this.mpj = {
|
window.mpj = {
|
||||||
/**
|
/**
|
||||||
* Show a message via toast
|
* Show a message via toast
|
||||||
* @param {string} message The message to show
|
* @param {string} message The message to show
|
||||||
@ -102,3 +102,112 @@ htmx.on("htmx:configRequest", function (evt) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
mpj.deriveTimeZone()
|
mpj.deriveTimeZone()
|
||||||
|
|
||||||
|
class RelativeDateTime extends HTMLElement {
|
||||||
|
static observedAttributes = ["interval"]
|
||||||
|
|
||||||
|
static #LessThanXMinutes = Symbol()
|
||||||
|
static #XMinutes = Symbol()
|
||||||
|
static #AboutXHours = Symbol()
|
||||||
|
static #XDays = Symbol()
|
||||||
|
static #AboutXMonths = Symbol()
|
||||||
|
static #XMonths = Symbol()
|
||||||
|
static #AboutXYears = Symbol()
|
||||||
|
static #OverXYears = Symbol()
|
||||||
|
static #AlmostXYears = Symbol()
|
||||||
|
|
||||||
|
static #messages = new Map([
|
||||||
|
[RelativeDateTime.#LessThanXMinutes, ['less than a minute', 'less than %d minutes']],
|
||||||
|
[RelativeDateTime.#XMinutes, ['a minute', '%d minutes']],
|
||||||
|
[RelativeDateTime.#AboutXHours, ['about an hour', 'about %d hours']],
|
||||||
|
[RelativeDateTime.#XDays, ['a day', '%d days']],
|
||||||
|
[RelativeDateTime.#AboutXMonths, ['about a month', 'about %d months']],
|
||||||
|
[RelativeDateTime.#XMonths, ['a month', '%d months']],
|
||||||
|
[RelativeDateTime.#AboutXYears, ['about a year', 'about %d years']],
|
||||||
|
[RelativeDateTime.#OverXYears, ['over a year', 'over %d years']],
|
||||||
|
[RelativeDateTime.#AlmostXYears, ['almost a year', 'almost %d years']],
|
||||||
|
])
|
||||||
|
|
||||||
|
static #aDay = 1440.0
|
||||||
|
static #almost2Days = 2520.0
|
||||||
|
static #aMonth = 43200.0
|
||||||
|
static #twoMonths = 86400.0
|
||||||
|
|
||||||
|
/** @type Date The date/time for this relative date */
|
||||||
|
#jsDate
|
||||||
|
/** @type ?number The interval timer for this element */
|
||||||
|
#timeOut = null
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
#update() {
|
||||||
|
|
||||||
|
const now = new Date()
|
||||||
|
const minutes = Math.abs((this.#jsDate.getTime() - now.getTime()) / 60 / 1000);
|
||||||
|
const months = Math.round(minutes / RelativeDateTime.#aMonth);
|
||||||
|
const years = Math.floor(months / 12);
|
||||||
|
|
||||||
|
/** @type symbol */
|
||||||
|
let typ
|
||||||
|
/** @type number */
|
||||||
|
let nbr
|
||||||
|
|
||||||
|
if (minutes < 1.0) {
|
||||||
|
typ = RelativeDateTime.#LessThanXMinutes
|
||||||
|
nbr = 1
|
||||||
|
} else if (minutes < 45.0) {
|
||||||
|
typ = RelativeDateTime.#XMinutes
|
||||||
|
nbr = Math.round(minutes)
|
||||||
|
} else if (minutes < 90.0) {
|
||||||
|
typ = RelativeDateTime.#AboutXHours
|
||||||
|
nbr = 1
|
||||||
|
} else if (minutes < RelativeDateTime.#aDay) {
|
||||||
|
typ = RelativeDateTime.#AboutXHours
|
||||||
|
nbr = Math.round(minutes / 60)
|
||||||
|
} else if (minutes < RelativeDateTime.#almost2Days) {
|
||||||
|
typ = RelativeDateTime.#XDays
|
||||||
|
nbr = 1
|
||||||
|
} else if (minutes < RelativeDateTime.#aMonth) {
|
||||||
|
typ = RelativeDateTime.#XDays
|
||||||
|
nbr = Math.round(minutes / RelativeDateTime.#aDay)
|
||||||
|
} else if (minutes < RelativeDateTime.#twoMonths) {
|
||||||
|
typ = RelativeDateTime.#AboutXMonths
|
||||||
|
nbr = Math.round(minutes / RelativeDateTime.#aMonth)
|
||||||
|
} else if (months < 12) {
|
||||||
|
typ = RelativeDateTime.#XMonths
|
||||||
|
nbr = Math.round(minutes / RelativeDateTime.#aMonth)
|
||||||
|
} else if (months % 12 < 3) {
|
||||||
|
typ = RelativeDateTime.#AboutXYears
|
||||||
|
nbr = years
|
||||||
|
} else if (months % 12 < 9) {
|
||||||
|
typ = RelativeDateTime.#OverXYears
|
||||||
|
nbr = years
|
||||||
|
} else {
|
||||||
|
typ = RelativeDateTime.#AlmostXYears
|
||||||
|
nbr = years + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmpl = RelativeDateTime.#messages.get(typ)
|
||||||
|
const message = nbr === 1 ? tmpl[0] : tmpl[1].replace("%d", nbr.toString())
|
||||||
|
this.innerText = this.#jsDate < now ? `${message} ago` : `in ${message}`
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.#jsDate = new Date(this.innerText)
|
||||||
|
this.#update()
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
if (this.#timeOut) clearInterval(this.#timeOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
if (this.#timeOut) clearInterval(this.#timeOut)
|
||||||
|
if (this.#jsDate) this.#update()
|
||||||
|
this.#timeOut = setInterval(() => this.#update, parseInt(newValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("relative-date-time", RelativeDateTime)
|
||||||
|
Loading…
Reference in New Issue
Block a user