-
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
…words between the current time and a specific UNIX timestamp. Updates intelligently according to time distance from current. (#24)
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { BaseController } from "../../utilities/base_controller"; | ||
import { Duration } from "date-fns"; | ||
export declare class TimeDistanceController extends BaseController { | ||
static values: { | ||
timestamp: NumberConstructor; | ||
}; | ||
timestampValue: number; | ||
readonly hasTimestampValue: boolean; | ||
_timeout: number | null; | ||
_timestamp: Date; | ||
get _duration(): Duration; | ||
get _nextUpdate(): number | null; | ||
timestampValueChanged(): void; | ||
initialize(): void; | ||
connect(): void; | ||
disconnect(): void; | ||
_update(): void; | ||
} | ||
//# sourceMappingURL=time_distance_controller.d.ts.map |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
--- | ||
id: TimeDistanceController | ||
title: TimeDistanceController | ||
--- | ||
|
||
import NoEvents from "../_partials/no-events.md"; | ||
import NoActions from "../_partials/no-actions.md"; | ||
import NoClasses from "../_partials/no-classes.md"; | ||
import NoTargets from "../_partials/no-targets.md"; | ||
|
||
|
||
## Purpose | ||
|
||
A controller that displays the distance in words between the specified time and the current time. Works for both past and future dates. | ||
|
||
Inspired by https://apidock.com/rails/ActionView/Helpers/DateHelper/distance_of_time_in_words, but updates in real time. | ||
|
||
Implemented using https://date-fns.org/v2.21.1/docs/intervalToDuration, wrapped in intelligent timing code that automatically works out how often to update the API. | ||
|
||
Fire and forget. The only configuration this controller takes is the timestamp. | ||
|
||
## [Actions](https://stimulus.hotwire.dev/reference/actions) | ||
|
||
<NoActions/> | ||
|
||
|
||
## [Targets](https://stimulus.hotwire.dev/reference/targets) | ||
|
||
<NoTargets/> | ||
|
||
|
||
## [Classes](https://stimulus.hotwire.dev/reference/classes) | ||
|
||
<NoClasses/> | ||
|
||
|
||
## [Values](https://stimulus.hotwire.dev/reference/values) | ||
|
||
| Value | Type | Description | Default | | ||
| --- | --- | --- | --- | | ||
| `timestamp` | String | The UNIX timestamp in seconds of the date/time to show "time ago" for | - | | ||
|
||
## Events | ||
|
||
<NoEvents/> | ||
|
||
## Side Effects | ||
|
||
The controller will set increasingly infrequent timeouts using `setTimeout` according to the distance to the timestamp in `timestampValue`. As the time gets further away, the controller updates the text lest frequently. | ||
|
||
- If the timestamp was seconds/minutes ago, the controller will set timeouts to update the text every ~30 seconds. | ||
- If the timestamp was hours ago, the controller will set timeouts to update the text every ~30 minutes. | ||
- If the timestamp was days or more ago, the controller will not set any timeouts. | ||
|
||
All timeouts are cleaned up when the controller disconnects. | ||
|
||
## How to Use | ||
|
||
<iframe | ||
src="https://codesandbox.io/embed/timedistancecontroller-r3m5e?fontsize=14&hidenavigation=1&theme=dark" | ||
style={{width: "100%", height: "500px", border: "0", borderRadius: "4px", overflow: "hidden"}} | ||
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" | ||
/> | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import {BaseController} from "../../utilities/base_controller"; | ||
import {Duration, formatDistanceToNow, intervalToDuration, isPast, toDate} from "date-fns"; | ||
|
||
export class TimeDistanceController extends BaseController { | ||
static values = { | ||
timestamp: Number, | ||
}; | ||
|
||
declare timestampValue: number; | ||
declare readonly hasTimestampValue: boolean; | ||
|
||
_timeout: number | null = null; | ||
declare _timestamp: Date; | ||
|
||
get _duration(): Duration { | ||
return isPast(this._timestamp) ? intervalToDuration({start: this._timestamp, end: new Date()}) : intervalToDuration({start: new Date(), end: this._timestamp}); | ||
} | ||
|
||
get _nextUpdate(): number | null { | ||
let duration = this._duration; | ||
|
||
if (duration.years && duration.years > 0) { | ||
return null; | ||
} else if (duration.months && duration.months > 0) { | ||
return null; | ||
} else if (duration.days && duration.days > 0) { | ||
return null; | ||
} else if (duration.hours && duration.hours > 0) { | ||
return 1800000; // Update every 30 mins | ||
} else { | ||
return 30000; // Update every 30 seconds | ||
} | ||
} | ||
|
||
timestampValueChanged() { | ||
this._timestamp = toDate(this.timestampValue * 1000); | ||
} | ||
|
||
initialize() { | ||
this._update = this._update.bind(this); | ||
} | ||
|
||
connect() { | ||
if (!this.hasTimestampValue) { | ||
throw new Error("Expected `timestampValue` to be present"); | ||
} | ||
this._update(); | ||
} | ||
|
||
disconnect() { | ||
if (this._timeout) { | ||
window.clearTimeout(this._timeout); | ||
} | ||
} | ||
|
||
_update() { | ||
this.el.innerHTML = formatDistanceToNow(this._timestamp, { | ||
addSuffix: true, | ||
includeSeconds: true, | ||
}); | ||
|
||
if (this._nextUpdate) { | ||
this._timeout = window.setTimeout(this._update, this._nextUpdate); | ||
} | ||
} | ||
} |