@solid-primitives/date
Version:
Collection of reactive primitives and utility functions, providing easier ways to deal with dates in SolidJS
163 lines (162 loc) • 6.56 kB
JavaScript
import { access, accessWith } from "@solid-primitives/utils";
import { createWritableMemo } from "@solid-primitives/memo";
import { createPolled } from "@solid-primitives/timer";
import { createComputed, createMemo, createSignal } from "solid-js";
import { createStore } from "solid-js/store";
import { DEFAULT_MESSAGES, HOUR, MINUTE } from "./variables.js";
import { formatDate, formatDateRelative, getCountdown, getDate, getDateDifference, } from "./utils.js";
/**
* Creates a reactive `Date` signal.
*
* @param init timestamp `number` | date `string` | `Date` instance; *May be a reactive signal*
* @returns [`Date` signal, setter function]
*/
export const createDate = (init) => {
const [date, setDate] = createWritableMemo(() => getDate(access(init)));
const setter = input => setDate(prev => getDate(accessWith(input, prev)));
return [date, setter];
};
/**
* Creates an autoupdating and reactive `new Date()`
*
* @param interval delay in ms for updating the date. Set to `false` to disable autoupdating. *Default = `30000`*
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/date#createDateNow
*
* @example
* ```ts
* // updates every second:
* const [now] = createDateNow(1000);
*
* // reactive timeout value
* const [timeout, setTimeout] = createSignal(500);
* const [now] = createDateNow(timeout);
*
* // won't autoupdate:
* const [now, update] = createDateNow(() => false);
*
* // update manually:
* update()
* ```
*/
export function createDateNow(interval = MINUTE / 2) {
const [track, trigger] = createSignal(undefined, { equals: false });
const memo = createPolled(() => {
track();
return new Date();
}, interval, undefined, { equals: (a, b) => a.getTime() === b.getTime() });
return [memo, trigger];
}
/**
* Provides a reactive time difference *(in ms)* signal.
*
* @param from timestamp `number` | date `string` | `Date` instance;
* *May be a reactive signal*
* @param to timestamp `number` | date `string` | `Date` instance;
* *May be a reactive signal*
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/date#createTimeDifference
*
* @example
* const [target, setTarget] = createSignal(1641408329089)
* const [diff, { from, to }] = createTimeDifference("2020 1 11", target)
* diff() // T: number
* from() // T: Date
* to() // T: Date
*/
export function createTimeDifference(from, to) {
const [fromDate] = createDate(from), [toDate] = createDate(to);
const diff = createMemo(() => getDateDifference(fromDate(), toDate()));
return [diff, { from: fromDate, to: toDate }];
}
/**
* Provides a autoupdating, reactive time difference *(in ms)* from today *(now)* as a signal.
*
* @param to a target timestamp `number` | date `string` | `Date` instance;
* *May be a reactive signal*
* @param updateInterval number or a function returning a number of ms to wait before updating the **now**
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/date#createTimeDifferenceFromNow
*
* @example
* const [target, setTarget] = createSignal(1641408329089)
* const [diff, { target, now, update }] = createTimeDifferenceFromNow(target)
* diff() // T: number
* target() // T: Date
* now() // T: Date
* // manual update (automatic one can be disabled by passing false)
* update()
*/
export function createTimeDifferenceFromNow(to, updateInterval = diff => Math.abs(diff) <= HOUR ? MINUTE / 2 : HOUR / 2) {
const interval = typeof updateInterval === "function" ? () => updateInterval(diff()) : updateInterval;
const [now, update] = createDateNow(interval);
const [diff, { to: target }] = createTimeDifference(now, to);
return [diff, { update, target, now }];
}
/**
* Provides a reactive, formatted, autoupdating date difference in relation to **now**.
*
* @param to a target timestamp `number` | date `string` | `Date` instance;
* *May be a reactive signal*
* @param options
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/date#createTimeAgo
*
* @example
* ```ts
* const [myDate, setMyDate] = createDate('Jun 28, 2021')
* const [timeago, { target, now, update, difference }] = createTimeAgo(myDate);
*
* timeago() // => 5 months ago
* difference() // T: number
* target() // T: Date
* now() // T: Date
* // manual update (automatic one can be disabled by passing false)
* update()
* ```
*/
export function createTimeAgo(to, options = {}) {
const { min = MINUTE, max = Infinity, dateFormatter = formatDate, messages, relativeFormatter = (a, b, diff) => formatDateRelative(diff, messages), } = options;
const [difference, extra] = createTimeDifferenceFromNow(to, options.interval);
const timeAgo = createMemo(() => {
const absDiff = Math.abs(difference());
if (absDiff < min)
return messages?.justNow ?? DEFAULT_MESSAGES.justNow;
if (absDiff > max)
return dateFormatter(extra.target());
return relativeFormatter(extra.now(), extra.target(), difference());
});
return [timeAgo, { ...extra, difference }];
}
export function createCountdown(a, b) {
let difference;
if (b !== undefined)
difference = createTimeDifference(a, b)[0];
else
difference = a;
const [countdown, setCountdown] = createStore(getCountdown(difference()));
createComputed(() => setCountdown(getCountdown(difference())));
return countdown;
}
/**
* Provides a reactive, autoupdating *(from **now**)*, broken down "time remaining" as a Store.
* @param to a target timestamp `number` | date `string` | `Date`;
* *May be a reactive signal*
* @param updateInterval number or a function returning a number of ms to wait before updating the **now**. Defaults to one second.
*
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/date#createCountdownFromNow
* @example
* // target date may be reactive
* const [to, setTo] = createSignal(1641408329089)
* const [countdown, { now, target, update }] = createCountdownFromNow(to, 500)
*
* countdown.minutes // e.g. 5
* countdown.hours // e.g. 1
* countdown.seconds // e.g. 48
*
* target() // T: Date
* now() // T: Date
* // manual update (automatic one can be disabled by passing false)
* update()
*/
export function createCountdownFromNow(to, updateInterval = 1000) {
const [difference, extra] = createTimeDifferenceFromNow(to, updateInterval);
const countdown = createCountdown(difference);
return [countdown, extra];
}