Skip to content

Commit

Permalink
feat: TimeDistanceController - Controller that shows the distance in …
Browse files Browse the repository at this point in the history
…words between the current time and a specific UNIX timestamp. Updates intelligently according to time distance from current. (#24)
  • Loading branch information
Sub-Xaero authored Apr 26, 2021
1 parent 4a6eb05 commit 91269ea
Show file tree
Hide file tree
Showing 16 changed files with 163 additions and 9 deletions.
19 changes: 19 additions & 0 deletions dist/controllers/visual/time_distance_controller.d.ts
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
1 change: 1 addition & 0 deletions dist/controllers/visual/time_distance_controller.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export { StickyController } from "./controllers/sticky_controller";
export { TableSortController } from "./controllers/tables/table_sort_controller";
export { TableTruncateController } from "./controllers/tables/table_truncate_controller";
export { TeleportController } from "./controllers/teleport_controller";
export { TimeDistanceController } from "./controllers/visual/time_distance_controller";
export { TimeoutController } from "./controllers/utility/timeout_controller";
export { ToggleClassController } from "./controllers/toggle_class_controller";
export { TurboFrameRCController } from "./controllers/turbo_frame_rc_controller";
Expand Down
2 changes: 1 addition & 1 deletion dist/index.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/stimulus-library.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.modern.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.modern.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.module.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.module.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.umd.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.umd.js.map

Large diffs are not rendered by default.

65 changes: 65 additions & 0 deletions docs/docs/controllers/time_distance_controller.mdx
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"
/>


1 change: 1 addition & 0 deletions docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ module.exports = {
{
"Visual Controllers": [
"controllers/CountdownController",
"controllers/TimeDistanceController",
],
},
],
Expand Down
66 changes: 66 additions & 0 deletions src/controllers/visual/time_distance_controller.ts
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);
}
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export {StickyController} from "./controllers/sticky_controller";
export {TableSortController} from "./controllers/tables/table_sort_controller";
export {TableTruncateController} from "./controllers/tables/table_truncate_controller";
export {TeleportController} from "./controllers/teleport_controller";
export {TimeDistanceController} from "./controllers/visual/time_distance_controller";
export {TimeoutController} from "./controllers/utility/timeout_controller";
export {ToggleClassController} from "./controllers/toggle_class_controller";
export {TurboFrameRCController} from "./controllers/turbo_frame_rc_controller";
Expand Down

0 comments on commit 91269ea

Please sign in to comment.