Skip to content

Commit

Permalink
feat: DurationController - A controller to display the duration that …
Browse files Browse the repository at this point in the history
…has elapsed since a given timestamp. Similar to TimeDistanceController, but with numbers instead of words.
  • Loading branch information
Sub-Xaero committed May 21, 2021
1 parent ac122ae commit b634a3c
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 0 deletions.
59 changes: 59 additions & 0 deletions docs/docs/controllers/duration_controller.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
id: DurationController
title: DurationController
---

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 numbers between the specified time and the current time. Works for past dates.
Similar to TimeDistance controller, but outputs fully qualified numbers for the duration rather than trying to construct a sentence.

Implemented using https://date-fns.org/v2.21.1/docs/formatDuration.

Fire and forget. The only configuration this controller takes is the timestamp, and whether to show seconds/minutes.

## [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 elapsed" for | - |
| `minutes` (Optional) | Number | Whether or not to show minutes in the outputted string | true |
| `seconds` (Optional) | Number | Whether or not to show seconds in the outputted string | true |

## Events

<NoEvents/>

## Side Effects

The controller will intelligently set a `setTimeout` according to the fastest unit of time shown. If minutes/seconds are hidden, then the controller will update less frequently.

All timeouts are cleaned up when the controller disconnects.

## How to Use

<iframe
src="https://codesandbox.io/embed/durationcontroller-r8qxb?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 @@ -108,6 +108,7 @@ module.exports = {
"Visual/Presentational Controllers": [
"controllers/ClockController",
"controllers/CountdownController",
"controllers/DurationController",
"controllers/TabsController",
"controllers/TimeDistanceController",
],
Expand Down
106 changes: 106 additions & 0 deletions src/controllers/visual/duration_controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {Duration, formatDuration, intervalToDuration, toDate} from "date-fns";
import {BaseController} from "../../utilities/base_controller";

export class DurationController extends BaseController {
static values = {
timestamp: Number,
minutes: Boolean,
seconds: Boolean,
};

declare readonly minutesValue: boolean;
declare readonly hasMinutesValue: boolean;
declare readonly secondsValue: boolean;
declare readonly hasSecondsValue: boolean;
declare timestampValue: number;
declare readonly hasTimestampValue: boolean;
_intervalHandle: number | null = null;

get _format(): string[] {
return [
"years",
"months",
"weeks",
"days",
"hours",
...(this._minutes ? ["minutes"] : []),
...(this._seconds ? ["seconds"] : []),
];
}

get _output(): string {
let {years, months, weeks, days, hours, minutes, seconds} = this._duration;

years ||= 0;
months ||= 0;
weeks ||= 0;
days ||= 0;
hours ||= 0;
minutes ||= 0;
seconds ||= 0;

let largeDenominators = [years, months, weeks, days, hours];

if (!this._minutes && !this._seconds && largeDenominators.every((x) => x === 0)) {
minutes = minutes + seconds / 60.0;
return `${(minutes / 60).toFixed(1)} hours`;
}

return formatDuration(this._duration, {format: this._format, delimiter: ", "});
}

get _seconds(): boolean {
return this.hasSecondsValue ? this.secondsValue : true;
}

get _minutes(): boolean {
return this.hasMinutesValue ? this.minutesValue : true;
}

get _timestamp(): Date {
if (this.hasTimestampValue) {
return toDate(this.timestampValue * 1000);
} else {
throw new Error("Expected `timestampValue` to be present");
}
}

get _duration(): Duration {
return intervalToDuration({start: new Date(), end: this._timestamp});
}

get _tickInterval() {
if (this._seconds) {
return 1000; // 1 seconds
} else if (this._minutes) {
return 15000; // 15 seconds
} else {
return 120000; // 2 minutes
}
}

initialize() {
this._update = this._update.bind(this);
}

connect() {
this._intervalHandle = window.setInterval(this._update, this._tickInterval);
this._update();
}

disconnect() {
if (this._intervalHandle) {
window.clearInterval(this._intervalHandle);
}
}

_update() {
try {
this.el.innerHTML = this._output;
} catch {
if (this._intervalHandle) {
window.clearInterval(this._intervalHandle);
}
}
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export {DetectDirtyFormController, isFormDirty} from "./controllers/forms/detect
export {DisableInputsController} from "./controllers/forms/disable_inputs_controller";
export {DisableWithController} from "./controllers/disable_with_controller";
export {DismissableController} from "./controllers/dismissable_controller";
export {DurationController} from "./controllers/visual/duration_controller";
export {ElementSaveController} from "./controllers/element_save_controller";
export {EmptyDomController} from "./controllers/empty_dom_controller";
export {EnableInputsController} from "./controllers/forms/enable_inputs_controller";
Expand Down

0 comments on commit b634a3c

Please sign in to comment.