diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
new file mode 100644
index 0000000..c4278b2
--- /dev/null
+++ b/.github/workflows/macos.yml
@@ -0,0 +1,18 @@
+name: MacOS
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ build:
+
+ runs-on: macos-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js
+ uses: actions/setup-node@v2
+ - run: npm install
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
new file mode 100644
index 0000000..772cbf3
--- /dev/null
+++ b/.github/workflows/ubuntu.yml
@@ -0,0 +1,18 @@
+name: Ubuntu
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js
+ uses: actions/setup-node@v2
+ - run: npm install
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
new file mode 100644
index 0000000..c125980
--- /dev/null
+++ b/.github/workflows/windows.yml
@@ -0,0 +1,18 @@
+name: Windows
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ build:
+
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js
+ uses: actions/setup-node@v2
+ - run: npm install
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0125458
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,118 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+web_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+.env.production
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+out
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and not Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+.vscode-test
+
+# yarn v2
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f199ea1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 William Niemiec
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..61e4b8b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,73 @@
+![](https://github.com/wniemiec-component-react/remaining-time/blob/master/docs/img/logo/logo.jpg)
+
+
Remaining Time
+Displays how many hours, minutes and seconds are left until a certain hour, minute and second.
+
+
+
+
+
+
+
+
+
+
+## ❇ Introduction
+React component that displays the time remaining until a certain hour, minute and second.
+
+## 🖼 Gallery
+
+
+
+
+## ❓ How to use
+1. Install the component
+```
+$ npm install --save @wniemiec-component-react/remaining-time
+```
+
+2. Import the component
+```
+import RemainingTime from '@wniemiec-component-react/remaining-time';
+```
+
+3. Use it
+```
+[...]
+
+import React from 'react';
+
+[...]
+
+
+
+[...]
+```
+
+## 📖 Documentation
+| Property |Type|Description|Default|
+|----------------|-------------------------------|-----------------------------|--------|
+|hours |`number`|Hour | `23` |
+|minutes |`number`|Minute| `59` |
+|seconds |`number`|Second | `59` |
+|fgColor |`string`|Text color | `"#000000"` |
+|style |`object`|Custom style | `null` |
+
+## 🚩 Changelog
+Details about each version are documented in the [releases section](https://github.com/wniemiec-component-react/remaining-time/releases).
+
+## 🤝 Contribute!
+See the documentation on how you can contribute to the project [here](https://github.com/wniemiec-component-react/remaining-time/blob/master/CONTRIBUTING.md).
+
+## 📁 Files
+
+### /
+| Name |Type|Description|
+|----------------|-------------------------------|-----------------------------|
+|docs |`Directory`|Documentation files|
+|src |`Directory`| Source files|
diff --git a/docs/img/screens/img1.png b/docs/img/screens/img1.png
new file mode 100644
index 0000000..1a8c294
Binary files /dev/null and b/docs/img/screens/img1.png differ
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..a474479
--- /dev/null
+++ b/index.js
@@ -0,0 +1,3 @@
+import RemainingTime from './src/RemainingTime';
+
+export default RemainingTime;
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..795209e
--- /dev/null
+++ b/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@wniemiec-component-react/remaining-time",
+ "version": "1.0.0",
+ "description": "Displays how many hours, minutes and seconds are left until a certain hour, minute and second.",
+ "main": "index.js",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/wniemiec-components-react/remaining-time.git"
+ },
+ "keywords": [
+ "wniemiec-component-react"
+ "wniemiec",
+ "component",
+ "react",
+ "remaining-time",
+ "remaining",
+ "time"
+ ],
+ "author": "William Niemiec (https://www.linkedin.com/in/williamniemiec)",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/wniemiec-components-react/remaining-time/issues"
+ },
+ "homepage": "https://github.com/wniemiec-components-react/remaining-time#readme"
+}
diff --git a/src/RemainingTime/index.js b/src/RemainingTime/index.js
new file mode 100644
index 0000000..931e268
--- /dev/null
+++ b/src/RemainingTime/index.js
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) William Niemiec.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ * @format
+ */
+
+import React, { useState, useEffect } from 'react';
+
+/**
+ * Displays how many hours, minutes and seconds are left until a certain hour,
+ * minute and second.
+ *
+ * @param {number} [hours=23] Hour
+ * @param {number} [minutes=59] Minute
+ * @param {number} [seconds=59] Second
+ */
+export default function RemainingTime({ hours=23, minutes=59, seconds=59, fgColor='#FFFFFF', style=null }) {
+ const [timeLeft, setTimeLeft] = useState(0);
+
+ useEffect(() => {
+ let timerControl = startTimer(setTimeLeft, hours, minutes, seconds);
+
+ return stopTimer(timerControl); // Stops counting when destroying the
+ // component
+ }, []);
+
+ return timeLeft;
+}
+
+function startTimer(setTimeLeft, hours, minutes, seconds) {
+ return setInterval(
+ () => timer(setTimeLeft, hours, minutes, seconds),
+ 1000
+ );
+}
+
+function timer(setTimeLeft, hours, minutes, seconds) {
+ const begin = getCurrentTime();
+ const end = getCurrentTimeUsing(hours, minutes, seconds);
+
+ setTimeLeft(getRemainingTime(begin, end));
+}
+
+function getCurrentTime() {
+ return new Date().getTime();
+}
+
+function getCurrentTimeUsing(hours, minutes, seconds) {
+ let end = new Date();
+
+ end.setHours(hours);
+ end.setMinutes(minutes);
+ end.setSeconds(seconds);
+
+ return end.getTime();
+}
+
+function getRemainingTime(begin, end) {
+ const diff = end - begin;
+
+ const hours = Math.floor(diff / (1000 * 60 * 60));
+ const minutes = Math.floor((diff / (1000 * 60)) - (hours * 60));
+ const seconds = Math.floor((diff / (1000)) - (minutes * 60) - (hours * 60 * 60));
+
+ return formatTime(hours, minutes, seconds);
+}
+
+function formatTime(hours, minutes, seconds) {
+ let formatedHours = hours.toString();
+ let formatedMinutes = minutes.toString();
+ let formatedSeconds = seconds.toString();
+
+ if (hours < 10 && hours > 0)
+ formatedHours = '0' + formatedHours
+ if (minutes < 10 && minutes > 0)
+ formatedMinutes = '0' + formatedMinutes
+ if (seconds < 10 && seconds > 0)
+ formatedSeconds = '0' + formatedSeconds
+
+ return `${formatedHours}h ${formatedMinutes}min ${formatedSeconds}s`;
+}
+
+function stopTimer(timerControl) {
+ return () => clearInterval(timerControl);
+}