jam-cloud/jam-ui/node_modules/react-timeago/lib/index.js.flow

168 lines
4.4 KiB
Plaintext

// @flow
import * as React from 'react'
import { useCallback, useEffect, useState } from 'react'
import defaultFormatter from './defaultFormatter'
import dateParser from './dateParser'
export type Unit =
| 'second'
| 'minute'
| 'hour'
| 'day'
| 'week'
| 'month'
| 'year'
export type Suffix = 'ago' | 'from now'
export type Formatter = (
value: number,
unit: Unit,
suffix: Suffix,
epochMilliseconds: number,
nextFormatter: () => React.Node,
now: () => number,
) => React.Node
export type Props = $ReadOnly<{
/** If the component should update itself over time */
live?: boolean,
/** minimum amount of time in seconds between re-renders */
minPeriod?: number,
/** Maximum time between re-renders in seconds. The component should update at least once every `x` seconds */
maxPeriod?: number,
/** The container to render the string into. You could use a string like `span` or a custom component */
component?: string | React.ComponentType<mixed>,
/**
* A title used for setting the title attribute if a <time> HTML Element is used.
*/
title?: string,
/** A function to decide how to format the date.
* If you use this, react-timeago is basically acting like a glorified setInterval for you.
*/
formatter?: Formatter,
/** The Date to display. An actual Date object or something that can be fed to new Date */
date: string | number | Date,
/** A function that returns what Date.now would return. Primarily for server
* date: string | number | Date + * rendering.
*/
now?: () => number,
...
}>
// Just some simple constants for readability
const MINUTE = 60
const HOUR = MINUTE * 60
const DAY = HOUR * 24
const WEEK = DAY * 7
const MONTH = DAY * 30
const YEAR = DAY * 365
export default function TimeAgo({
date,
formatter = defaultFormatter,
component = 'time',
live = true,
minPeriod = 0,
maxPeriod = WEEK,
title,
now = () => Date.now(),
...passDownProps
}: Props): null | React.MixedElement {
const forceUpdate = useUpdate()
useEffect(() => {
if (!live) {
return
}
let timeoutId
const tick = (refresh: ?boolean): void => {
const then = dateParser(date).valueOf()
if (!then) {
console.warn('[react-timeago] Invalid Date provided')
return
}
const timeNow = now()
const seconds = Math.round(Math.abs(timeNow - then) / 1000)
const unboundPeriod =
seconds < MINUTE
? 1000
: seconds < HOUR
? 1000 * MINUTE
: seconds < DAY
? 1000 * HOUR
: 1000 * WEEK
const period = Math.min(
Math.max(unboundPeriod, minPeriod * 1000),
maxPeriod * 1000,
)
if (period) {
if (timeoutId) {
clearTimeout(timeoutId)
}
timeoutId = setTimeout(tick, period)
}
if (!refresh) {
forceUpdate()
}
}
tick(true)
return () => {
clearTimeout(timeoutId)
}
}, [date, forceUpdate, live, maxPeriod, minPeriod, now])
const Komponent = component
const then = dateParser(date).valueOf()
if (!then) {
return null
}
const timeNow = now()
const seconds = Math.round(Math.abs(timeNow - then) / 1000)
const suffix = then < timeNow ? 'ago' : 'from now'
const [value, unit] =
seconds < MINUTE
? [Math.round(seconds), 'second']
: seconds < HOUR
? [Math.round(seconds / MINUTE), 'minute']
: seconds < DAY
? [Math.round(seconds / HOUR), 'hour']
: seconds < WEEK
? [Math.round(seconds / DAY), 'day']
: seconds < MONTH
? [Math.round(seconds / WEEK), 'week']
: seconds < YEAR
? [Math.round(seconds / MONTH), 'month']
: [Math.round(seconds / YEAR), 'year']
const passDownTitle =
typeof title === 'undefined'
? typeof date === 'string'
? date
: dateParser(date).toISOString().substr(0, 16).replace('T', ' ')
: title
const spreadProps =
Komponent === 'time'
? { ...passDownProps, dateTime: dateParser(date).toISOString() }
: passDownProps
const nextFormatter = defaultFormatter.bind(null, value, unit, suffix)
return (
<Komponent {...spreadProps} title={passDownTitle}>
{formatter(value, unit, suffix, then, nextFormatter, now)}
</Komponent>
)
}
function useUpdate(): () => void {
const [_, setCount] = useState(0)
return useCallback(() => {
setCount((num) => num + 1)
}, [])
}