@lumberyard/react-doomsday

A nifty countdown component for React

Usage no npm install needed!

<script type="module">
  import lumberyardReactDoomsday from 'https://cdn.skypack.dev/@lumberyard/react-doomsday';
</script>

README

react-doomsday

npm bundle size NPM npm

A nifty countdown component for React


Doomsday is a component written originally for one of my projects. I needed something that would be very flexible at displaying time countdown and existing solutions required hacking around to make the countdown behave as I intended, so I wrote my own and decided to publish it. Maybe someone finds it useful.

react-doomsday is written with TypeScript and uses dayjs in the background. It's sole purpose is to count time from now until some date in the future.

Menu


Usage

import * as React from 'react'

import Doomsday from '@lumberyard/react-doomsday'

const Example: React.FC = () => {
  const date = "2029-05-25T02:23:35.000Z" // or use dayjs or native or timestamp
  return (
    ...
    <Doomsday date={date} />
    ...
  )
}

Install

yarn add @lumberyard/react-doomsday

npm install --save @lumberyard/react-doomsday

Demo

Codesandbox


Doomsday component

<Doomsday/> is a <div> wrapper around logic based on dayjs. The component gives you a bunch of ways of displaying the date. Each unit of time (eg. month, minute, etc.) is separate from the rest and works independently.

Props

All <Doomsday/> props are optional, however, bear in mind that this component has only bare minimum of styling. You can use style, className or any other popular CSS-in-JS solution to style it. DoomsdayProps are extended by <div>'s native props, so whatever <div> takes, so does <Doomsday/>

Default styles of the component can be overwritten.

const defaultStyles = {
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center'
}

See DoomsdayProps


Examples

import * as React from 'react'

import Doomsday from '@lumberyard/react-doomsday'

const Example: React.FC = () => {
  return (
    ...
    <Doomsday
      date="2029-05-25T02:23:35.000Z"
      format="[YYYYescape] YYYY-MM-DDTHH:mm:ssZ[Z]"
      render={(doom) => ({
        // `doom.date` will be in a format passed above
        <div>
          <h3>{doom.date}</h3>
          <p>{doom.endOfTimeSequence.months} months</p>
          <p>{doom.endOfTimeSequence.minutes} minutes</p>
          <p>{doom.endOfTimeSequence.seconds} seconds</p>
        </div>
      })}
    />
    ...
  )
}
import * as React from 'react'

import Doomsday from '@lumberyard/react-doomsday'

const Example: React.FC = () => {
  const [play, setPlay] = React.useState(true)

  return (
    ...
    <Doomsday
      play={play}
      showDefaults={false} // hides default units
      goodbye={<div>KAPUT!</div>}
      date="2029-05-25T02:23:35.000Z"
      years={({ label, endOfTimeSequence }) => (
        // label() displays the unit's label ('years' in this case) and cuts the plural form when necessary
        <div>
          <span>{endOfTimeSequence}</span>
          <span>{label(endOfTimeSequence)}</span>
        </div>
      )}
      months={({ type, endOfTimeSequence }) => (
        // label() also takes a second string argument that gets singu/pluralized
        <div>
          <span>{endOfTimeSequence}</span>
          <span>{label(endOfTimeSequence, 'pidgeons')}</span>
        </div>
      )}
      days={(to) => (
        // type shows the unit's label
        <div>
          <span>{to.endOfTimeSequence}</span>
          <span>{type}</span>
        </div>
      )}
      hours={(to) => (
        <div>
          <span>{to.endOfTimeSequence}</span>
          <span>Is this a real life...</span>
        </div>
      )}
      minutes={(to) => (
        <div>
          <span>{to.endOfTimeSequence}</span>
          <span>...or just a fantasy?</span>
        </div>
      )}
      seconds={(to) => (
        <div>
          <span>{to.endOfTimeSequence}</span>
          <span>What does the fox say?</span>
        </div>
      )}
    />
    ...
  )
}

Doomsday hook

The hook is used inside the <Doomsday/> wrapper, so don't use both in the same component.

Use the hook in the very last child of the parent tree

If you want more freedom (but keep the default calculations), you can import a useDoomsday hook instead of <Doomsday/> component. This function is the ticker written using setInterval(). I didn't use window.requestAnimationFrame() because I know setInterval() better and didn't have time to learn and understand how the other works - I'll probably rewrite it at some point. There shouldn't be any breaking changes.

The hook takes a date and an optional play argument and returns a doomsday object and an isHere flag.

prop type description
doomsday DoomsdayCreator object containing default doomsday's calculations
isHere boolean flag indicating countdown's completion

import * as React from 'react'

import { useDoomsday } from '@lumberyard/react-doomsday'

const Example: React.FC = () => {
  const [play, setPlay] = React.useState(true);
  const date = "2029-05-25T02:23:35.000Z";
  const { doomsday, isHere } = useDoomsday(date, play);

  if (isHere) return <div>KAPUT!</div>

  return (
    ...
    <div>{doomsday.endOfTime.months}</div>
    ...
  )
}

Doomsday types

Since react-doomsday is written with TypeScript I am going to list typings instead of your regular props.

DoomsdayProps

prop type default description
date dayjs.ConfigType dayjs().endOf('year') This prop takes any date you might want to throw into dayjs(). It only takes dates from the future
format string 'DD/MM/YYYY HH:mm:ss' You can set the date's format using strings from dayjs
play boolean true This prop lets you programatically decide when to initialise the countdown
showDefaults boolean true If you use one of the RenderUnit props, a corresponding default will be overwritten. This turns the defaults off
goodbye React.ReactElement undefined goodbye renders the component you wish to show when the countdown is finished
render RenderDoomsday undefined This prop returns DoomsdayCreator object. It basically is an inner wraper around plugin's logic that gives you access to all date calculations. It overwrites RenderUnits, so it's either this or the rest...
renderAll boolean false ...unless this prop is set to true, which will display component passed with render as a last child (after) seconds
years, months, days, hours, minutes, seconds RenderUnit undefined returns a function that passes DoomsdayUnit object as its prop and takes a JSX component that gets rendered inside <Doomsday/>'s wrapper

DoomsdayCreator

prop type default description
now dayjs.Dayjs dayjs() now captured by dayjs
target dayjs.Dayjs dayjs(date) date passed as a prop used as an argument for dayjs
nowTimestamp number now.valueOf() timestamp of now
targetTimestamp number target.valueOf() timestamp of target
diffTimestamp number target - now timestamp of a difference between targetTimestamp and nowTimestamp
date string 'DD/MM/YYYY HH:mm:ss' date in a given dayjs format
endOfTimeSequence Units - returns an object with calculations per unit indicating logical sequence of time units until target is met
endOfTimeFloat Units - returns time left per unit after subtracting endOfTime years
endOfTime Units - returns units left until target
endOfYear Units - returns units left until end of year, if it's short, it returns 0
endOfMonth Units - same story, different end
endOfDay Units - you get the gist
endOfHour Units - ...
endOfMinute Units - ...

Units

prop type description
years number years it will take to reach the end of unit
months number months until the end of unit
days number ...
hours number ...
minutes number ...
seconds number ...

RenderUnit

(to: DoomsdayUnit) => React.ReactElement


RenderDoomsday

(doom: DoomsdayCreator) => React.ReactElement


DoomsdayUnit

This is DoomsdayCreator in (sort of) reverse. Depending on which RenderUnit you render in <Doomsday/> instead of returning the entire DoomsdayCreator, you get only endOfs for that specific unit.

prop type description
type UnitType string indicating which unit is selected
label (endOf: number, text?: string) => string; slice(0, -1) on UnitType or text removing plural 's'
endOfTimeSequence number endOfTimeSequence in selected UnitType
endOfTimeFloat number ...
endOfTime number ...
endOfYear number ...
endOfMonth number ...
endOfDay number ...
endOfHour number ...
endOfMinute number ...

UnitType

"seconds" | "minutes" | "hours" | "days" | "months" | "years"


Stuff

If you feel the docs are missing something, or some stuff isn't clear, feel free to open an issue.
I feel this plugin is simple enough to get by without unit tests. TS is here to keep an eye on things.
As for the Code of Conduct, there is none. Just be humane to humans and non-humans alike.

License

MIT © MatulaDesign