diff options
Diffstat (limited to 'src/components/DateString')
| -rw-r--r-- | src/components/DateString/DateString.tsx | 41 | ||||
| -rw-r--r-- | src/components/DateString/compactDateString.ts | 35 | 
2 files changed, 76 insertions, 0 deletions
diff --git a/src/components/DateString/DateString.tsx b/src/components/DateString/DateString.tsx new file mode 100644 index 0000000..7c824cd --- /dev/null +++ b/src/components/DateString/DateString.tsx @@ -0,0 +1,41 @@ +import React, { useState, useMemo, useCallback } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import compactDateString from './compactDateString'; + +interface PropTypes { +  value: Date | string; +} + +const useStyles = makeStyles({ +  root: { +    display: 'block', +    cursor: 'pointer' +  } +}); + +const DATE_FORMAT = { +  month: 'long', +  day: 'numeric', +  year: 'numeric', +  hour: '2-digit', +  minute: '2-digit' +}; + +const DateString: React.FC<PropTypes> = ({ value }) => { +  const [isCompact, setIsCompact] = useState<boolean>(true); +  const classes = useStyles(); + +  const toggleScompact = useCallback(() => setIsCompact(v => !v), [setIsCompact]); + +  const date = useMemo(() => new Date(value), [value]); +  const formatted = useMemo(() => date.toLocaleString('default', DATE_FORMAT), [date]); +  const compact = useMemo(() => compactDateString(date), [date]); + +  return ( +    <div role="presentation" className={classes.root} onClick={toggleScompact}> +      {isCompact ? compact : formatted} +    </div> +  ); +}; + +export default DateString; diff --git a/src/components/DateString/compactDateString.ts b/src/components/DateString/compactDateString.ts new file mode 100644 index 0000000..8bff4b7 --- /dev/null +++ b/src/components/DateString/compactDateString.ts @@ -0,0 +1,35 @@ +const metrics = [ +  { name: 'minute', ratio: 60 }, +  { name: 'hour', ratio: 60 }, +  { name: 'day', ratio: 24 }, +  { name: 'week', ratio: 7 }, +  { name: 'month', ratio: 4 }, +  { name: 'year', ratio: 12 } +]; + +const PRECISION = 0.85; + +const resolve = (value: number, metricIndex = 0): string => { +  // Recursively divide value until it's ready to be presented as a string +  const metric = metrics[metricIndex]; +  const nextMetric = metrics[metricIndex + 1]; +  const newValue = value / metric.ratio; + +  if (newValue < nextMetric.ratio * PRECISION) { +    const rounded = Math.round(newValue); +    const isPlural = rounded > 1; +    const count = isPlural ? rounded : 'a'; +    const ending = isPlural ? 's' : ''; +    return `${count} ${metric.name}${ending} ago`; +  } +  return resolve(newValue, metricIndex + 1); +}; + +const compactDateString = (date: Date): string => { +  const now = new Date(); +  const diff = (now.valueOf() - date.valueOf()) / 1000; +  if (diff < 60) return 'just now'; +  return resolve(diff); +}; + +export default compactDateString;  |