histogram width divide by mode, not total #2
34 changed files with 1070 additions and 13262 deletions
16
.github/workflows/lint.yml
vendored
Normal file
16
.github/workflows/lint.yml
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
name: Lint
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Prettify code
|
||||||
|
uses: creyD/prettier_action@v4.2
|
||||||
|
with:
|
||||||
|
prettier_options: --check src
|
16
.github/workflows/test.yml
vendored
Normal file
16
.github/workflows/test.yml
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
name: Test
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- run: |
|
||||||
|
npm install
|
||||||
|
- run: |
|
||||||
|
npm run test
|
24
.gitlab-ci.yml
Normal file
24
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# This file is a template, and might need editing before it works on your project.
|
||||||
|
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||||
|
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||||
|
# This specific template is located at:
|
||||||
|
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml
|
||||||
|
|
||||||
|
# This is a sample GitLab CI/CD configuration file that should run without any modifications.
|
||||||
|
# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
|
||||||
|
# it uses echo commands to simulate the pipeline execution.
|
||||||
|
#
|
||||||
|
# A pipeline is composed of independent jobs that run scripts, grouped into stages.
|
||||||
|
# Stages run in sequential order, but jobs within stages run in parallel.
|
||||||
|
#
|
||||||
|
# For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages
|
||||||
|
image: docker:latest
|
||||||
|
stages: # List of stages for jobs, and their order of execution
|
||||||
|
- build
|
||||||
|
|
||||||
|
build-job: # This job runs in the build stage, which runs first.
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- echo "Compiling the code..."
|
||||||
|
- docker build -t honigle .
|
||||||
|
- echo "Compile complete."
|
1
.husky/.gitignore
vendored
Normal file
1
.husky/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
_
|
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npx lint-staged
|
44
README.md
44
README.md
|
@ -1,37 +1,35 @@
|
||||||
# Wordle Clone
|
Word Guessing Game
|
||||||
|
|
||||||
- Go play the real Wordle [here](https://www.powerlanguage.co.uk/wordle/)
|
This is a clone project of a popular word guessing game made using React, Typescript, and Tailwind.
|
||||||
- Read the story behind it [here](https://www.nytimes.com/2022/01/03/technology/wordle-word-game-creator.html)
|
|
||||||
- Try a demo of this clone project [here](https://wordle.hannahmariepark.com)
|
|
||||||
|
|
||||||
_Inspiration:_
|
|
||||||
This game is an open source clone of the immensely popular online word guessing game Wordle. Like many others all over the world, I saw the signature pattern of green, yellow, and white squares popping up all over social media and the web and had to check it out. After a few days of play, I decided it would be great for my learning to try to rebuild Wordle in React!
|
|
||||||
|
|
||||||
_Design Decisions:_
|
|
||||||
I used a combination of React, Typescript, and Tailwind to build this Wordle Clone. When examining the original Wordle, I assumed the list might come from an external API or database, but after investigating in chrome dev tools I found that the list of words is simply stored in an array on the front end. I'm using the same list as the OG Wordle uses, but watch out for spoilers if you go find the file in this repo! The word match functionality is simple: the word array index increments each day from a fixed game epoch timestamp (only one puzzle per day!) roughly like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
WORDS[Math.floor((NOW_IN_MS - GAME_EPOCH_IN_MS) / ONE_DAY_IN_MS)]
|
|
||||||
```
|
|
||||||
|
|
||||||
React enabled me to componentize the littlest parts of the game - keys and letter cells - and use them as the building blocks for the keyboard, word grid, and winning solution graphic. As for handling state, I used the built in useState and useEffect hooks to track guesses, whether the game is won, and to conditionally render popups.
|
|
||||||
|
|
||||||
In addition to other things, Typescript helped ensure type safety for the statuses of each guessed letter, which were used in many areas of the app and needed to be accurate for the game to work correctly.
|
|
||||||
|
|
||||||
I implemented Tailwind mostly because I wanted to learn how to use Tailwind CSS, but I also took advantage of [Tailwind UI](https://tailwindui.com/) with their [headless package](https://headlessui.dev/) to build the modals and notifications. This was such an easy way to build simple popups for how to play, winning the game, and invalid words.
|
|
||||||
|
|
||||||
_To Run Locally:_
|
_To Run Locally:_
|
||||||
Clone the repository and perform the following command line actions:
|
Clone the repository and perform the following command line actions:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ cd wordle
|
$ cd word-guessing-game
|
||||||
$ npm install
|
$ npm install
|
||||||
$ npm run start
|
$ npm run start
|
||||||
```
|
```
|
||||||
|
|
||||||
_To build/run docker container:_
|
_To build/run docker container:_
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ docker build -t notwordle .
|
$ docker build -t game .
|
||||||
$ docker run -d -p 3000:3000 notwordle
|
$ docker run -d -p 3000:3000 game
|
||||||
```
|
```
|
||||||
|
|
||||||
open http://localhost:3000 in browser.
|
open http://localhost:3000 in browser.
|
||||||
|
|
||||||
|
_To create a version in a different language:_
|
||||||
|
|
||||||
|
- Update the title, the description, and the "You need to enable JavaScript" message in `public/index.html`
|
||||||
|
- Update the language attribute in the HTML tag in `public/index.html`
|
||||||
|
- Update the name and short name in `public/manifest.json`
|
||||||
|
- Update the strings in `src/constants/strings.ts`
|
||||||
|
- Add all of the five letter words in the language to `src/constants/validGuesses.ts`, replacing the English words
|
||||||
|
- Add a list of goal words in the language to `src/constants/wordlist.ts`, replacing the English words
|
||||||
|
- Update the "About" modal in `src/components/modals/AboutModel.tsx`
|
||||||
|
- Update the "Info" modal in `src/components/modals/InfoModal.tsx`
|
||||||
|
- If the language has letters that are not present in English, add them to the `CharValue` type in `src/lib/statuses.ts` and update the keyboard in `src/lib/components/keyboard/Keyboard.tsx`
|
||||||
|
- If the language's letters are made of multiple unicode characters, use a grapheme splitter at various points throughout the app or normalize the input so that all of the letters are made of a single character
|
||||||
|
- If the language is written right-to-left, add `dir="rtl"` to the HTML tag in `public/index.html` and prepend `\u202E` (the unicode right-to-left override character) to the return statement of the inner function in `generateEmojiGrid` in `src/lib/share.ts`
|
||||||
|
|
706
package-lock.json
generated
706
package-lock.json
generated
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "wordle",
|
"name": "game",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "wordle",
|
"name": "game",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^1.4.2",
|
"@headlessui/react": "^1.4.2",
|
||||||
|
@ -19,6 +19,7 @@
|
||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
|
"react-countdown": "^2.3.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-scripts": "5.0.0",
|
"react-scripts": "5.0.0",
|
||||||
"typescript": "^4.5.4",
|
"typescript": "^4.5.4",
|
||||||
|
@ -26,8 +27,10 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
|
"husky": "^7.0.4",
|
||||||
|
"lint-staged": "^12.3.2",
|
||||||
"postcss": "^8.4.5",
|
"postcss": "^8.4.5",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "2.5.1",
|
||||||
"tailwindcss": "^3.0.12"
|
"tailwindcss": "^3.0.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -4263,6 +4266,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
|
||||||
"integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0="
|
"integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0="
|
||||||
},
|
},
|
||||||
|
"node_modules/astral-regex": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/async": {
|
"node_modules/async": {
|
||||||
"version": "2.6.3",
|
"version": "2.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||||
|
@ -5028,6 +5040,78 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cli-cursor": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"restore-cursor": "^3.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cli-truncate": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"slice-ansi": "^5.0.0",
|
||||||
|
"string-width": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cli-truncate/node_modules/ansi-regex": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cli-truncate/node_modules/string-width": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-7x54QnN21P+XL/v8SuNKvfgsUre6PXpN7mc77N3HlZv+f1SBRGmjxtOud2Z6FZ8DmdkD/IdjCaf9XXbnqmTZGQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"eastasianwidth": "^0.2.0",
|
||||||
|
"emoji-regex": "^9.2.2",
|
||||||
|
"strip-ansi": "^7.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cli-truncate/node_modules/strip-ansi": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cliui": {
|
"node_modules/cliui": {
|
||||||
"version": "7.0.4",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||||
|
@ -6116,6 +6200,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
||||||
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
|
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/eastasianwidth": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
@ -8078,6 +8168,21 @@
|
||||||
"node": ">=10.17.0"
|
"node": ">=10.17.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/husky": {
|
||||||
|
"version": "7.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz",
|
||||||
|
"integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"husky": "lib/bin.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/typicode"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
@ -10582,6 +10687,138 @@
|
||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/lint-staged": {
|
||||||
|
"version": "12.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.3.2.tgz",
|
||||||
|
"integrity": "sha512-gtw4Cbj01SuVSfAOXC6ivd/7VKHTj51yj5xV8TgktFmYNMsZzXuSd5/brqJEA93v63wL7R6iDlunMANOechC0A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"cli-truncate": "^3.1.0",
|
||||||
|
"colorette": "^2.0.16",
|
||||||
|
"commander": "^8.3.0",
|
||||||
|
"debug": "^4.3.3",
|
||||||
|
"execa": "^5.1.1",
|
||||||
|
"lilconfig": "2.0.4",
|
||||||
|
"listr2": "^4.0.1",
|
||||||
|
"micromatch": "^4.0.4",
|
||||||
|
"normalize-path": "^3.0.0",
|
||||||
|
"object-inspect": "^1.12.0",
|
||||||
|
"string-argv": "^0.3.1",
|
||||||
|
"supports-color": "^9.2.1",
|
||||||
|
"yaml": "^1.10.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"lint-staged": "bin/lint-staged.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/lint-staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lint-staged/node_modules/supports-color": {
|
||||||
|
"version": "9.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.1.tgz",
|
||||||
|
"integrity": "sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/listr2": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-D65Nl+zyYHL2jQBGmxtH/pU8koPZo5C8iCNE8EoB04RwPgQG1wuaKwVbeZv9LJpiH4Nxs0FCp+nNcG8OqpniiA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"cli-truncate": "^2.1.0",
|
||||||
|
"colorette": "^2.0.16",
|
||||||
|
"log-update": "^4.0.0",
|
||||||
|
"p-map": "^4.0.0",
|
||||||
|
"rfdc": "^1.3.0",
|
||||||
|
"rxjs": "^7.5.2",
|
||||||
|
"through": "^2.3.8",
|
||||||
|
"wrap-ansi": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"enquirer": ">= 2.3.0 < 3"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"enquirer": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/listr2/node_modules/ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/listr2/node_modules/cli-truncate": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"slice-ansi": "^3.0.0",
|
||||||
|
"string-width": "^4.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/listr2/node_modules/color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/listr2/node_modules/color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/listr2/node_modules/slice-ansi": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"astral-regex": "^2.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/loader-runner": {
|
"node_modules/loader-runner": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
|
||||||
|
@ -10647,6 +10884,88 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
||||||
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
|
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
|
||||||
},
|
},
|
||||||
|
"node_modules/log-update": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-escapes": "^4.3.0",
|
||||||
|
"cli-cursor": "^3.1.0",
|
||||||
|
"slice-ansi": "^4.0.0",
|
||||||
|
"wrap-ansi": "^6.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/log-update/node_modules/ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/log-update/node_modules/color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/log-update/node_modules/color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/log-update/node_modules/slice-ansi": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"astral-regex": "^2.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/log-update/node_modules/wrap-ansi": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/loose-envify": {
|
"node_modules/loose-envify": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
|
@ -12901,6 +13220,18 @@
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-countdown": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-countdown/-/react-countdown-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-Q4SADotHtgOxNWhDdvgupmKVL0pMB9DvoFcxv5AzjsxVhzOVxnttMbAywgqeOdruwEAmnPhOhNv/awAgkwru2w==",
|
||||||
|
"dependencies": {
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">= 15",
|
||||||
|
"react-dom": ">= 15"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dev-utils": {
|
"node_modules/react-dev-utils": {
|
||||||
"version": "12.0.0",
|
"version": "12.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz",
|
||||||
|
@ -13408,6 +13739,19 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/restore-cursor": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"onetime": "^5.1.0",
|
||||||
|
"signal-exit": "^3.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/retry": {
|
"node_modules/retry": {
|
||||||
"version": "0.13.1",
|
"version": "0.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
||||||
|
@ -13425,6 +13769,12 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rfdc": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/rimraf": {
|
"node_modules/rimraf": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
@ -13529,6 +13879,15 @@
|
||||||
"queue-microtask": "^1.2.2"
|
"queue-microtask": "^1.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rxjs": {
|
||||||
|
"version": "7.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz",
|
||||||
|
"integrity": "sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/safe-buffer": {
|
"node_modules/safe-buffer": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
@ -13823,6 +14182,46 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/slice-ansi": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^6.0.0",
|
||||||
|
"is-fullwidth-code-point": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/slice-ansi/node_modules/ansi-styles": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sockjs": {
|
"node_modules/sockjs": {
|
||||||
"version": "0.3.24",
|
"version": "0.3.24",
|
||||||
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
|
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
|
||||||
|
@ -14007,6 +14406,15 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/string-argv": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz",
|
||||||
|
"integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6.19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string-length": {
|
"node_modules/string-length": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
|
||||||
|
@ -14589,6 +14997,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
|
||||||
"integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w=="
|
"integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/through": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||||
|
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/thunky": {
|
"node_modules/thunky": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||||
|
@ -18902,6 +19316,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
|
||||||
"integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0="
|
"integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0="
|
||||||
},
|
},
|
||||||
|
"astral-regex": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"async": {
|
"async": {
|
||||||
"version": "2.6.3",
|
"version": "2.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||||
|
@ -19486,6 +19906,53 @@
|
||||||
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
||||||
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
|
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
|
||||||
},
|
},
|
||||||
|
"cli-cursor": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"restore-cursor": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cli-truncate": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"slice-ansi": "^5.0.0",
|
||||||
|
"string-width": "^5.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-7x54QnN21P+XL/v8SuNKvfgsUre6PXpN7mc77N3HlZv+f1SBRGmjxtOud2Z6FZ8DmdkD/IdjCaf9XXbnqmTZGQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"eastasianwidth": "^0.2.0",
|
||||||
|
"emoji-regex": "^9.2.2",
|
||||||
|
"strip-ansi": "^7.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^6.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"cliui": {
|
"cliui": {
|
||||||
"version": "7.0.4",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||||
|
@ -20290,6 +20757,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
||||||
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
|
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
|
||||||
},
|
},
|
||||||
|
"eastasianwidth": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"ee-first": {
|
"ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
@ -21710,6 +22183,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||||
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
|
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
|
||||||
},
|
},
|
||||||
|
"husky": {
|
||||||
|
"version": "7.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz",
|
||||||
|
"integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"iconv-lite": {
|
"iconv-lite": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
@ -23498,6 +23977,98 @@
|
||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||||
},
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"version": "12.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.3.2.tgz",
|
||||||
|
"integrity": "sha512-gtw4Cbj01SuVSfAOXC6ivd/7VKHTj51yj5xV8TgktFmYNMsZzXuSd5/brqJEA93v63wL7R6iDlunMANOechC0A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"cli-truncate": "^3.1.0",
|
||||||
|
"colorette": "^2.0.16",
|
||||||
|
"commander": "^8.3.0",
|
||||||
|
"debug": "^4.3.3",
|
||||||
|
"execa": "^5.1.1",
|
||||||
|
"lilconfig": "2.0.4",
|
||||||
|
"listr2": "^4.0.1",
|
||||||
|
"micromatch": "^4.0.4",
|
||||||
|
"normalize-path": "^3.0.0",
|
||||||
|
"object-inspect": "^1.12.0",
|
||||||
|
"string-argv": "^0.3.1",
|
||||||
|
"supports-color": "^9.2.1",
|
||||||
|
"yaml": "^1.10.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"supports-color": {
|
||||||
|
"version": "9.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.1.tgz",
|
||||||
|
"integrity": "sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"listr2": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-D65Nl+zyYHL2jQBGmxtH/pU8koPZo5C8iCNE8EoB04RwPgQG1wuaKwVbeZv9LJpiH4Nxs0FCp+nNcG8OqpniiA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"cli-truncate": "^2.1.0",
|
||||||
|
"colorette": "^2.0.16",
|
||||||
|
"log-update": "^4.0.0",
|
||||||
|
"p-map": "^4.0.0",
|
||||||
|
"rfdc": "^1.3.0",
|
||||||
|
"rxjs": "^7.5.2",
|
||||||
|
"through": "^2.3.8",
|
||||||
|
"wrap-ansi": "^7.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cli-truncate": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"slice-ansi": "^3.0.0",
|
||||||
|
"string-width": "^4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"slice-ansi": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"astral-regex": "^2.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"loader-runner": {
|
"loader-runner": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
|
||||||
|
@ -23551,6 +24122,66 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
||||||
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
|
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
|
||||||
},
|
},
|
||||||
|
"log-update": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-escapes": "^4.3.0",
|
||||||
|
"cli-cursor": "^3.1.0",
|
||||||
|
"slice-ansi": "^4.0.0",
|
||||||
|
"wrap-ansi": "^6.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"slice-ansi": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"astral-regex": "^2.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wrap-ansi": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"loose-envify": {
|
"loose-envify": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
|
@ -25058,6 +25689,14 @@
|
||||||
"whatwg-fetch": "^3.6.2"
|
"whatwg-fetch": "^3.6.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-countdown": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-countdown/-/react-countdown-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-Q4SADotHtgOxNWhDdvgupmKVL0pMB9DvoFcxv5AzjsxVhzOVxnttMbAywgqeOdruwEAmnPhOhNv/awAgkwru2w==",
|
||||||
|
"requires": {
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-dev-utils": {
|
"react-dev-utils": {
|
||||||
"version": "12.0.0",
|
"version": "12.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz",
|
||||||
|
@ -25430,6 +26069,16 @@
|
||||||
"resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz",
|
||||||
"integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ=="
|
"integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ=="
|
||||||
},
|
},
|
||||||
|
"restore-cursor": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"onetime": "^5.1.0",
|
||||||
|
"signal-exit": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"retry": {
|
"retry": {
|
||||||
"version": "0.13.1",
|
"version": "0.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
||||||
|
@ -25440,6 +26089,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
|
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
|
||||||
},
|
},
|
||||||
|
"rfdc": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"rimraf": {
|
"rimraf": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
@ -25508,6 +26163,15 @@
|
||||||
"queue-microtask": "^1.2.2"
|
"queue-microtask": "^1.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rxjs": {
|
||||||
|
"version": "7.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.2.tgz",
|
||||||
|
"integrity": "sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
@ -25744,6 +26408,30 @@
|
||||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
|
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
|
||||||
},
|
},
|
||||||
|
"slice-ansi": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^6.0.0",
|
||||||
|
"is-fullwidth-code-point": "^4.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"sockjs": {
|
"sockjs": {
|
||||||
"version": "0.3.24",
|
"version": "0.3.24",
|
||||||
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
|
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
|
||||||
|
@ -25889,6 +26577,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"string-argv": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz",
|
||||||
|
"integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"string-length": {
|
"string-length": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
|
||||||
|
@ -26302,6 +26996,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
|
||||||
"integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w=="
|
"integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w=="
|
||||||
},
|
},
|
||||||
|
"through": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||||
|
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"thunky": {
|
"thunky": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||||
|
|
17
package.json
17
package.json
|
@ -14,16 +14,20 @@
|
||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
|
"react-countdown": "^2.3.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-scripts": "5.0.0",
|
"react-scripts": "5.0.0",
|
||||||
"typescript": "^4.5.4",
|
"typescript": "^4.5.4",
|
||||||
"web-vitals": "^2.1.3"
|
"web-vitals": "^2.1.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
|
"eject": "react-scripts eject",
|
||||||
|
"fix": "prettier --write src",
|
||||||
|
"lint": "prettier --check src",
|
||||||
|
"start": "react-scripts start",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject"
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
|
@ -45,8 +49,13 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
|
"husky": "^7.0.4",
|
||||||
|
"lint-staged": "^12.3.2",
|
||||||
"postcss": "^8.4.5",
|
"postcss": "^8.4.5",
|
||||||
"tailwindcss": "^3.0.12",
|
"prettier": "2.5.1",
|
||||||
"prettier": "^2.5.1"
|
"tailwindcss": "^3.0.12"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"src/*.{ts,tsx,js,jsx,css,md}": "prettier --write"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,7 @@
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta name="description" content="Honigle" />
|
||||||
name="description"
|
|
||||||
content="Web site created using create-react-app"
|
|
||||||
/>
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
|
@ -24,7 +21,7 @@
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>Wordle Clone</title>
|
<title>Honigle</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"short_name": "React App",
|
"short_name": "Game",
|
||||||
"name": "Create React App Sample",
|
"name": "Game",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "favicon.ico",
|
"src": "favicon.ico",
|
||||||
|
|
39
src/App.css
39
src/App.css
|
@ -1,38 +1,3 @@
|
||||||
.App {
|
html.dark {
|
||||||
text-align: center;
|
background-color: rgb(15, 23, 42);
|
||||||
}
|
|
||||||
|
|
||||||
.App-logo {
|
|
||||||
height: 40vmin;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
.App-logo {
|
|
||||||
animation: App-logo-spin infinite 20s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-header {
|
|
||||||
background-color: #282c34;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: calc(10px + 2vmin);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-link {
|
|
||||||
color: #61dafb;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes App-logo-spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,26 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render, screen } from '@testing-library/react'
|
import { render, screen } from '@testing-library/react'
|
||||||
import App from './App'
|
import App from './App'
|
||||||
|
import { GAME_TITLE } from './constants/strings'
|
||||||
|
|
||||||
test('renders learn react link', () => {
|
beforeEach(() => {
|
||||||
|
Object.defineProperty(window, 'matchMedia', {
|
||||||
|
writable: true,
|
||||||
|
value: jest.fn().mockImplementation((query) => ({
|
||||||
|
matches: false,
|
||||||
|
media: query,
|
||||||
|
onchange: null,
|
||||||
|
addListener: jest.fn(), // deprecated
|
||||||
|
removeListener: jest.fn(), // deprecated
|
||||||
|
addEventListener: jest.fn(),
|
||||||
|
removeEventListener: jest.fn(),
|
||||||
|
dispatchEvent: jest.fn(),
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('renders App component', () => {
|
||||||
render(<App />)
|
render(<App />)
|
||||||
const linkElement = screen.getByText(/learn react/i)
|
const linkElement = screen.getByText(GAME_TITLE)
|
||||||
expect(linkElement).toBeInTheDocument()
|
expect(linkElement).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
117
src/App.tsx
117
src/App.tsx
|
@ -1,13 +1,24 @@
|
||||||
import { InformationCircleIcon } from '@heroicons/react/outline'
|
import {
|
||||||
import { ChartBarIcon } from '@heroicons/react/outline'
|
InformationCircleIcon,
|
||||||
|
ChartBarIcon,
|
||||||
|
SunIcon,
|
||||||
|
} from '@heroicons/react/outline'
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { Alert } from './components/alerts/Alert'
|
import { Alert } from './components/alerts/Alert'
|
||||||
import { Grid } from './components/grid/Grid'
|
import { Grid } from './components/grid/Grid'
|
||||||
import { Keyboard } from './components/keyboard/Keyboard'
|
import { Keyboard } from './components/keyboard/Keyboard'
|
||||||
import { AboutModal } from './components/modals/AboutModal'
|
import { AboutModal } from './components/modals/AboutModal'
|
||||||
import { InfoModal } from './components/modals/InfoModal'
|
import { InfoModal } from './components/modals/InfoModal'
|
||||||
import { WinModal } from './components/modals/WinModal'
|
|
||||||
import { StatsModal } from './components/modals/StatsModal'
|
import { StatsModal } from './components/modals/StatsModal'
|
||||||
|
import {
|
||||||
|
GAME_TITLE,
|
||||||
|
WIN_MESSAGES,
|
||||||
|
GAME_COPIED_MESSAGE,
|
||||||
|
ABOUT_GAME_MESSAGE,
|
||||||
|
NOT_ENOUGH_LETTERS_MESSAGE,
|
||||||
|
WORD_NOT_FOUND_MESSAGE,
|
||||||
|
CORRECT_WORD_MESSAGE,
|
||||||
|
} from './constants/strings'
|
||||||
import { isWordInWordList, isWinningWord, solution } from './lib/words'
|
import { isWordInWordList, isWinningWord, solution } from './lib/words'
|
||||||
import { addStatsForCompletedGame, loadStats } from './lib/stats'
|
import { addStatsForCompletedGame, loadStats } from './lib/stats'
|
||||||
import {
|
import {
|
||||||
|
@ -15,17 +26,31 @@ import {
|
||||||
saveGameStateToLocalStorage,
|
saveGameStateToLocalStorage,
|
||||||
} from './lib/localStorage'
|
} from './lib/localStorage'
|
||||||
|
|
||||||
|
import './App.css'
|
||||||
|
|
||||||
|
const ALERT_TIME_MS = 2000
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const prefersDarkMode = window.matchMedia(
|
||||||
|
'(prefers-color-scheme: dark)'
|
||||||
|
).matches
|
||||||
|
|
||||||
const [currentGuess, setCurrentGuess] = useState('')
|
const [currentGuess, setCurrentGuess] = useState('')
|
||||||
const [isGameWon, setIsGameWon] = useState(false)
|
const [isGameWon, setIsGameWon] = useState(false)
|
||||||
const [isWinModalOpen, setIsWinModalOpen] = useState(false)
|
|
||||||
const [isInfoModalOpen, setIsInfoModalOpen] = useState(false)
|
const [isInfoModalOpen, setIsInfoModalOpen] = useState(false)
|
||||||
const [isAboutModalOpen, setIsAboutModalOpen] = useState(false)
|
const [isAboutModalOpen, setIsAboutModalOpen] = useState(false)
|
||||||
const [isNotEnoughLetters, setIsNotEnoughLetters] = useState(false)
|
const [isNotEnoughLetters, setIsNotEnoughLetters] = useState(false)
|
||||||
const [isStatsModalOpen, setIsStatsModalOpen] = useState(false)
|
const [isStatsModalOpen, setIsStatsModalOpen] = useState(false)
|
||||||
const [isWordNotFoundAlertOpen, setIsWordNotFoundAlertOpen] = useState(false)
|
const [isWordNotFoundAlertOpen, setIsWordNotFoundAlertOpen] = useState(false)
|
||||||
const [isGameLost, setIsGameLost] = useState(false)
|
const [isGameLost, setIsGameLost] = useState(false)
|
||||||
const [shareComplete, setShareComplete] = useState(false)
|
const [isDarkMode, setIsDarkMode] = useState(
|
||||||
|
localStorage.getItem('theme')
|
||||||
|
? localStorage.getItem('theme') === 'dark'
|
||||||
|
: prefersDarkMode
|
||||||
|
? true
|
||||||
|
: false
|
||||||
|
)
|
||||||
|
const [successAlert, setSuccessAlert] = useState('')
|
||||||
const [guesses, setGuesses] = useState<string[]>(() => {
|
const [guesses, setGuesses] = useState<string[]>(() => {
|
||||||
const loaded = loadGameStateFromLocalStorage()
|
const loaded = loadGameStateFromLocalStorage()
|
||||||
if (loaded?.solution !== solution) {
|
if (loaded?.solution !== solution) {
|
||||||
|
@ -43,15 +68,39 @@ function App() {
|
||||||
|
|
||||||
const [stats, setStats] = useState(() => loadStats())
|
const [stats, setStats] = useState(() => loadStats())
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isDarkMode) {
|
||||||
|
document.documentElement.classList.add('dark')
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove('dark')
|
||||||
|
}
|
||||||
|
}, [isDarkMode])
|
||||||
|
|
||||||
|
const handleDarkMode = (isDark: boolean) => {
|
||||||
|
setIsDarkMode(isDark)
|
||||||
|
localStorage.setItem('theme', isDark ? 'dark' : 'light')
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
saveGameStateToLocalStorage({ guesses, solution })
|
saveGameStateToLocalStorage({ guesses, solution })
|
||||||
}, [guesses])
|
}, [guesses])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isGameWon) {
|
if (isGameWon) {
|
||||||
setIsWinModalOpen(true)
|
setSuccessAlert(
|
||||||
|
WIN_MESSAGES[Math.floor(Math.random() * WIN_MESSAGES.length)]
|
||||||
|
)
|
||||||
|
setTimeout(() => {
|
||||||
|
setSuccessAlert('')
|
||||||
|
setIsStatsModalOpen(true)
|
||||||
|
}, ALERT_TIME_MS)
|
||||||
}
|
}
|
||||||
}, [isGameWon])
|
if (isGameLost) {
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsStatsModalOpen(true)
|
||||||
|
}, ALERT_TIME_MS)
|
||||||
|
}
|
||||||
|
}, [isGameWon, isGameLost])
|
||||||
|
|
||||||
const onChar = (value: string) => {
|
const onChar = (value: string) => {
|
||||||
if (currentGuess.length < 5 && guesses.length < 6 && !isGameWon) {
|
if (currentGuess.length < 5 && guesses.length < 6 && !isGameWon) {
|
||||||
|
@ -64,18 +113,21 @@ function App() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const onEnter = () => {
|
const onEnter = () => {
|
||||||
if (!(currentGuess.length === 5) && !isGameLost) {
|
if (isGameWon || isGameLost) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!(currentGuess.length === 5)) {
|
||||||
setIsNotEnoughLetters(true)
|
setIsNotEnoughLetters(true)
|
||||||
return setTimeout(() => {
|
return setTimeout(() => {
|
||||||
setIsNotEnoughLetters(false)
|
setIsNotEnoughLetters(false)
|
||||||
}, 2000)
|
}, ALERT_TIME_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isWordInWordList(currentGuess)) {
|
if (!isWordInWordList(currentGuess)) {
|
||||||
setIsWordNotFoundAlertOpen(true)
|
setIsWordNotFoundAlertOpen(true)
|
||||||
return setTimeout(() => {
|
return setTimeout(() => {
|
||||||
setIsWordNotFoundAlertOpen(false)
|
setIsWordNotFoundAlertOpen(false)
|
||||||
}, 2000)
|
}, ALERT_TIME_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
const winningWord = isWinningWord(currentGuess)
|
const winningWord = isWinningWord(currentGuess)
|
||||||
|
@ -98,14 +150,18 @@ function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-8 max-w-7xl mx-auto sm:px-6 lg:px-8">
|
<div className="py-8 max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||||
<div className="flex w-80 mx-auto items-center mb-8">
|
<div className="flex w-80 mx-auto items-center mb-8 mt-12">
|
||||||
<h1 className="text-xl grow font-bold">Not Wordle</h1>
|
<h1 className="text-xl grow font-bold dark:text-white">{GAME_TITLE}</h1>
|
||||||
|
<SunIcon
|
||||||
|
className="h-6 w-6 cursor-pointer dark:stroke-white"
|
||||||
|
onClick={() => handleDarkMode(!isDarkMode)}
|
||||||
|
/>
|
||||||
<InformationCircleIcon
|
<InformationCircleIcon
|
||||||
className="h-6 w-6 cursor-pointer"
|
className="h-6 w-6 cursor-pointer dark:stroke-white"
|
||||||
onClick={() => setIsInfoModalOpen(true)}
|
onClick={() => setIsInfoModalOpen(true)}
|
||||||
/>
|
/>
|
||||||
<ChartBarIcon
|
<ChartBarIcon
|
||||||
className="h-6 w-6 cursor-pointer"
|
className="h-6 w-6 cursor-pointer dark:stroke-white"
|
||||||
onClick={() => setIsStatsModalOpen(true)}
|
onClick={() => setIsStatsModalOpen(true)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -116,18 +172,6 @@ function App() {
|
||||||
onEnter={onEnter}
|
onEnter={onEnter}
|
||||||
guesses={guesses}
|
guesses={guesses}
|
||||||
/>
|
/>
|
||||||
<WinModal
|
|
||||||
isOpen={isWinModalOpen}
|
|
||||||
handleClose={() => setIsWinModalOpen(false)}
|
|
||||||
guesses={guesses}
|
|
||||||
handleShare={() => {
|
|
||||||
setIsWinModalOpen(false)
|
|
||||||
setShareComplete(true)
|
|
||||||
return setTimeout(() => {
|
|
||||||
setShareComplete(false)
|
|
||||||
}, 2000)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<InfoModal
|
<InfoModal
|
||||||
isOpen={isInfoModalOpen}
|
isOpen={isInfoModalOpen}
|
||||||
handleClose={() => setIsInfoModalOpen(false)}
|
handleClose={() => setIsInfoModalOpen(false)}
|
||||||
|
@ -135,7 +179,14 @@ function App() {
|
||||||
<StatsModal
|
<StatsModal
|
||||||
isOpen={isStatsModalOpen}
|
isOpen={isStatsModalOpen}
|
||||||
handleClose={() => setIsStatsModalOpen(false)}
|
handleClose={() => setIsStatsModalOpen(false)}
|
||||||
|
guesses={guesses}
|
||||||
gameStats={stats}
|
gameStats={stats}
|
||||||
|
isGameLost={isGameLost}
|
||||||
|
isGameWon={isGameWon}
|
||||||
|
handleShare={() => {
|
||||||
|
setSuccessAlert(GAME_COPIED_MESSAGE)
|
||||||
|
return setTimeout(() => setSuccessAlert(''), ALERT_TIME_MS)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<AboutModal
|
<AboutModal
|
||||||
isOpen={isAboutModalOpen}
|
isOpen={isAboutModalOpen}
|
||||||
|
@ -147,18 +198,18 @@ function App() {
|
||||||
className="mx-auto mt-8 flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 select-none"
|
className="mx-auto mt-8 flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 select-none"
|
||||||
onClick={() => setIsAboutModalOpen(true)}
|
onClick={() => setIsAboutModalOpen(true)}
|
||||||
>
|
>
|
||||||
About this game
|
{ABOUT_GAME_MESSAGE}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Alert message="Not enough letters" isOpen={isNotEnoughLetters} />
|
<Alert message={NOT_ENOUGH_LETTERS_MESSAGE} isOpen={isNotEnoughLetters} />
|
||||||
<Alert message="Word not found" isOpen={isWordNotFoundAlertOpen} />
|
|
||||||
<Alert
|
<Alert
|
||||||
message={`You lost, the word was ${solution}`}
|
message={WORD_NOT_FOUND_MESSAGE}
|
||||||
isOpen={isGameLost}
|
isOpen={isWordNotFoundAlertOpen}
|
||||||
/>
|
/>
|
||||||
|
<Alert message={CORRECT_WORD_MESSAGE(solution)} isOpen={isGameLost} />
|
||||||
<Alert
|
<Alert
|
||||||
message="Game copied to clipboard"
|
message={successAlert}
|
||||||
isOpen={shareComplete}
|
isOpen={successAlert !== ''}
|
||||||
variant="success"
|
variant="success"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,10 +10,10 @@ type Props = {
|
||||||
|
|
||||||
export const Alert = ({ isOpen, message, variant = 'warning' }: Props) => {
|
export const Alert = ({ isOpen, message, variant = 'warning' }: Props) => {
|
||||||
const classes = classNames(
|
const classes = classNames(
|
||||||
'fixed top-20 left-1/2 transform -translate-x-1/2 max-w-sm w-full shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden',
|
'fixed top-5 left-1/2 transform -translate-x-1/2 max-w-sm w-full shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden',
|
||||||
{
|
{
|
||||||
'bg-rose-200': variant === 'warning',
|
'bg-rose-200': variant === 'warning',
|
||||||
'bg-green-200': variant === 'success',
|
'bg-blue-200 z-20': variant === 'success',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,16 @@ type Props = {
|
||||||
|
|
||||||
export const Cell = ({ value, status }: Props) => {
|
export const Cell = ({ value, status }: Props) => {
|
||||||
const classes = classnames(
|
const classes = classnames(
|
||||||
'w-14 h-14 border-solid border-2 flex items-center justify-center mx-0.5 text-lg font-bold rounded',
|
'w-14 h-14 border-solid border-2 flex items-center justify-center mx-0.5 text-lg font-bold rounded dark:text-white',
|
||||||
{
|
{
|
||||||
'bg-white border-slate-200': !status,
|
'bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-600':
|
||||||
'border-black': value && !status,
|
!status,
|
||||||
'bg-slate-400 text-white border-slate-400': status === 'absent',
|
'border-black dark:border-slate-100': value && !status,
|
||||||
'bg-green-500 text-white border-green-500': status === 'correct',
|
'bg-slate-400 dark:bg-slate-700 text-white border-slate-400 dark:border-slate-700':
|
||||||
'bg-yellow-500 text-white border-yellow-500': status === 'present',
|
status === 'absent',
|
||||||
|
'bg-blue-500 text-white border-blue-500': status === 'correct',
|
||||||
|
'bg-orange-500 dark:bg-orange-700 text-white border-orange-500 dark:border-orange-700':
|
||||||
|
status === 'present',
|
||||||
'cell-animation': !!value,
|
'cell-animation': !!value,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,13 +19,14 @@ export const Key = ({
|
||||||
onClick,
|
onClick,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const classes = classnames(
|
const classes = classnames(
|
||||||
'flex items-center justify-center rounded mx-0.5 text-xs font-bold cursor-pointer select-none',
|
'flex items-center justify-center rounded mx-0.5 text-xs font-bold cursor-pointer select-none dark:text-white',
|
||||||
{
|
{
|
||||||
'bg-slate-200 hover:bg-slate-300 active:bg-slate-400': !status,
|
'bg-slate-200 dark:bg-slate-600 hover:bg-slate-300 active:bg-slate-400':
|
||||||
|
!status,
|
||||||
'bg-slate-400 text-white': status === 'absent',
|
'bg-slate-400 text-white': status === 'absent',
|
||||||
'bg-green-500 hover:bg-green-600 active:bg-green-700 text-white':
|
'bg-blue-500 hover:bg-blue-600 active:bg-blue-700 text-white':
|
||||||
status === 'correct',
|
status === 'correct',
|
||||||
'bg-yellow-500 hover:bg-yellow-600 active:bg-yellow-700 text-white':
|
'bg-orange-500 hover:bg-orange-600 active:bg-orange-700 dark:bg-orange-700 text-white':
|
||||||
status === 'present',
|
status === 'present',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { KeyValue } from '../../lib/keyboard'
|
||||||
import { getStatuses } from '../../lib/statuses'
|
import { getStatuses } from '../../lib/statuses'
|
||||||
import { Key } from './Key'
|
import { Key } from './Key'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
import { ENTER_TEXT, DELETE_TEXT } from '../../constants/strings'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onChar: (value: string) => void
|
onChar: (value: string) => void
|
||||||
|
@ -69,7 +70,7 @@ export const Keyboard = ({ onChar, onDelete, onEnter, guesses }: Props) => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<Key width={65.4} value="ENTER" onClick={onClick}>
|
<Key width={65.4} value="ENTER" onClick={onClick}>
|
||||||
Enter
|
{ENTER_TEXT}
|
||||||
</Key>
|
</Key>
|
||||||
<Key value="Z" onClick={onClick} status={charStatuses['Z']} />
|
<Key value="Z" onClick={onClick} status={charStatuses['Z']} />
|
||||||
<Key value="X" onClick={onClick} status={charStatuses['X']} />
|
<Key value="X" onClick={onClick} status={charStatuses['X']} />
|
||||||
|
@ -79,7 +80,7 @@ export const Keyboard = ({ onChar, onDelete, onEnter, guesses }: Props) => {
|
||||||
<Key value="N" onClick={onClick} status={charStatuses['N']} />
|
<Key value="N" onClick={onClick} status={charStatuses['N']} />
|
||||||
<Key value="M" onClick={onClick} status={charStatuses['M']} />
|
<Key value="M" onClick={onClick} status={charStatuses['M']} />
|
||||||
<Key width={65.4} value="DELETE" onClick={onClick}>
|
<Key width={65.4} value="DELETE" onClick={onClick}>
|
||||||
Delete
|
{DELETE_TEXT}
|
||||||
</Key>
|
</Key>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
import { CharStatus } from '../../lib/statuses'
|
|
||||||
import classnames from 'classnames'
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
status: CharStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
export const MiniCell = ({ status }: Props) => {
|
|
||||||
const classes = classnames(
|
|
||||||
'w-10 h-10 border-solid border-2 border-slate-200 flex items-center justify-center mx-0.5 text-lg font-bold rounded',
|
|
||||||
{
|
|
||||||
'bg-white': status === 'absent',
|
|
||||||
'bg-green-500': status === 'correct',
|
|
||||||
'bg-yellow-500': status === 'present',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={classes}></div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { getGuessStatuses } from '../../lib/statuses'
|
|
||||||
import { MiniCell } from './MiniCell'
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
guess: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const MiniCompletedRow = ({ guess }: Props) => {
|
|
||||||
const statuses = getGuessStatuses(guess)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex justify-center mb-1">
|
|
||||||
{guess.split('').map((letter, i) => (
|
|
||||||
<MiniCell key={i} status={statuses[i]} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
import { MiniCompletedRow } from './MiniCompletedRow'
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
guesses: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const MiniGrid = ({ guesses }: Props) => {
|
|
||||||
return (
|
|
||||||
<div className="pb-6">
|
|
||||||
{guesses.map((guess, i) => (
|
|
||||||
<MiniCompletedRow key={i} guess={guess} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -8,21 +8,20 @@ type Props = {
|
||||||
export const AboutModal = ({ isOpen, handleClose }: Props) => {
|
export const AboutModal = ({ isOpen, handleClose }: Props) => {
|
||||||
return (
|
return (
|
||||||
<BaseModal title="About" isOpen={isOpen} handleClose={handleClose}>
|
<BaseModal title="About" isOpen={isOpen} handleClose={handleClose}>
|
||||||
<p className="text-sm text-gray-500">
|
This is a honey powered fork of an open source word guessing game -{' '}
|
||||||
This is an open source clone of the game Wordle -{' '}
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/hannahcode/wordle"
|
href="https://git.k-fortytwo.de/christofsteel/honigle"
|
||||||
className="underline font-bold"
|
className="underline font-bold"
|
||||||
>
|
>
|
||||||
check out the code here
|
check out the code here
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
and{' '}
|
and{' '}
|
||||||
<a
|
<a
|
||||||
href="https://www.powerlanguage.co.uk/wordle/"
|
href="https://github.com/hannahcode/word-guessing-game"
|
||||||
className="underline font-bold"
|
className="underline font-bold"
|
||||||
>
|
>
|
||||||
play the original here
|
the original code here
|
||||||
</a>
|
</a>{' '}
|
||||||
</p>
|
</p>
|
||||||
</BaseModal>
|
</BaseModal>
|
||||||
)
|
)
|
||||||
|
|
|
@ -46,10 +46,10 @@ export const BaseModal = ({ title, children, isOpen, handleClose }: Props) => {
|
||||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||||
>
|
>
|
||||||
<div className="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6">
|
<div className="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6 dark:bg-gray-800">
|
||||||
<div className="absolute right-4 top-4">
|
<div className="absolute right-4 top-4">
|
||||||
<XCircleIcon
|
<XCircleIcon
|
||||||
className="h-6 w-6 cursor-pointer"
|
className="h-6 w-6 cursor-pointer dark:stroke-white"
|
||||||
onClick={() => handleClose()}
|
onClick={() => handleClose()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,13 +57,11 @@ export const BaseModal = ({ title, children, isOpen, handleClose }: Props) => {
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Dialog.Title
|
<Dialog.Title
|
||||||
as="h3"
|
as="h3"
|
||||||
className="text-lg leading-6 font-medium text-gray-900"
|
className="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100"
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<div className="mt-2">
|
<div className="mt-2">{children}</div>
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,42 +9,42 @@ type Props = {
|
||||||
export const InfoModal = ({ isOpen, handleClose }: Props) => {
|
export const InfoModal = ({ isOpen, handleClose }: Props) => {
|
||||||
return (
|
return (
|
||||||
<BaseModal title="How to play" isOpen={isOpen} handleClose={handleClose}>
|
<BaseModal title="How to play" isOpen={isOpen} handleClose={handleClose}>
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500 dark:text-gray-300">
|
||||||
Guess the WORDLE in 6 tries. After each guess, the color of the tiles
|
Rate das 🍯le in 6 Versuchen. Nach jedem Versuch ändert sich die Farbe des Kästchens
|
||||||
will change to show how close your guess was to the word.
|
um anzuzeigen, wie nah man an der korrekten Lösung ist.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex justify-center mb-1 mt-4">
|
<div className="flex justify-center mb-1 mt-4">
|
||||||
<Cell value="W" status="correct" />
|
<Cell value="I" status="correct" />
|
||||||
|
<Cell value="M" />
|
||||||
|
<Cell value="K" />
|
||||||
<Cell value="E" />
|
<Cell value="E" />
|
||||||
<Cell value="A" />
|
|
||||||
<Cell value="R" />
|
<Cell value="R" />
|
||||||
<Cell value="Y" />
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500 dark:text-gray-300">
|
||||||
The letter W is in the word and in the correct spot.
|
Der Buchstabe I ist an der korrekten Stelle.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex justify-center mb-1 mt-4">
|
<div className="flex justify-center mb-1 mt-4">
|
||||||
<Cell value="P" />
|
<Cell value="B" />
|
||||||
<Cell value="I" />
|
<Cell value="L" />
|
||||||
<Cell value="L" status="present" />
|
<Cell value="U" status="present" />
|
||||||
<Cell value="O" />
|
<Cell value="M" />
|
||||||
<Cell value="T" />
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-gray-500">
|
|
||||||
The letter L is in the word but in the wrong spot.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="flex justify-center mb-1 mt-4">
|
|
||||||
<Cell value="V" />
|
|
||||||
<Cell value="A" />
|
|
||||||
<Cell value="G" />
|
|
||||||
<Cell value="U" status="absent" />
|
|
||||||
<Cell value="E" />
|
<Cell value="E" />
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500 dark:text-gray-300">
|
||||||
The letter U is not in the word in any spot.
|
Der Buchstabe U ist in dem Wort enthalten, aber an der falschen Stelle.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex justify-center mb-1 mt-4">
|
||||||
|
<Cell value="W" />
|
||||||
|
<Cell value="A" />
|
||||||
|
<Cell value="B" />
|
||||||
|
<Cell value="E" status="absent" />
|
||||||
|
<Cell value="N" />
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-500 dark:text-gray-300">
|
||||||
|
Der Buchstabe E ist nicht in dem Wort enthalten.
|
||||||
</p>
|
</p>
|
||||||
</BaseModal>
|
</BaseModal>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,22 +1,80 @@
|
||||||
|
import Countdown from 'react-countdown'
|
||||||
import { StatBar } from '../stats/StatBar'
|
import { StatBar } from '../stats/StatBar'
|
||||||
import { Histogram } from '../stats/Histogram'
|
import { Histogram } from '../stats/Histogram'
|
||||||
import { GameStats } from '../../lib/localStorage'
|
import { GameStats } from '../../lib/localStorage'
|
||||||
|
import { shareStatus } from '../../lib/share'
|
||||||
|
import { tomorrow } from '../../lib/words'
|
||||||
import { BaseModal } from './BaseModal'
|
import { BaseModal } from './BaseModal'
|
||||||
|
import {
|
||||||
|
STATISTICS_TITLE,
|
||||||
|
GUESS_DISTRIBUTION_TEXT,
|
||||||
|
NEW_WORD_TEXT,
|
||||||
|
SHARE_TEXT,
|
||||||
|
} from '../../constants/strings'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
handleClose: () => void
|
handleClose: () => void
|
||||||
|
guesses: string[]
|
||||||
gameStats: GameStats
|
gameStats: GameStats
|
||||||
|
isGameLost: boolean
|
||||||
|
isGameWon: boolean
|
||||||
|
handleShare: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StatsModal = ({ isOpen, handleClose, gameStats }: Props) => {
|
export const StatsModal = ({
|
||||||
|
isOpen,
|
||||||
|
handleClose,
|
||||||
|
guesses,
|
||||||
|
gameStats,
|
||||||
|
isGameLost,
|
||||||
|
isGameWon,
|
||||||
|
handleShare,
|
||||||
|
}: Props) => {
|
||||||
|
if (gameStats.totalGames <= 0) {
|
||||||
|
return (
|
||||||
|
<BaseModal
|
||||||
|
title={STATISTICS_TITLE}
|
||||||
|
isOpen={isOpen}
|
||||||
|
handleClose={handleClose}
|
||||||
|
>
|
||||||
|
<StatBar gameStats={gameStats} />
|
||||||
|
</BaseModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<BaseModal title="Statistics" isOpen={isOpen} handleClose={handleClose}>
|
<BaseModal
|
||||||
|
title={STATISTICS_TITLE}
|
||||||
|
isOpen={isOpen}
|
||||||
|
handleClose={handleClose}
|
||||||
|
>
|
||||||
<StatBar gameStats={gameStats} />
|
<StatBar gameStats={gameStats} />
|
||||||
<h4 className="text-lg leading-6 font-medium text-gray-900">
|
<h4 className="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">
|
||||||
Guess Distribution
|
{GUESS_DISTRIBUTION_TEXT}
|
||||||
</h4>
|
</h4>
|
||||||
<Histogram gameStats={gameStats} />
|
<Histogram gameStats={gameStats} />
|
||||||
|
{(isGameLost || isGameWon) && (
|
||||||
|
<div className="mt-5 sm:mt-6 columns-2 dark:text-white">
|
||||||
|
<div>
|
||||||
|
<h5>{NEW_WORD_TEXT}</h5>
|
||||||
|
<Countdown
|
||||||
|
className="text-lg font-medium text-gray-900 dark:text-gray-100"
|
||||||
|
date={tomorrow}
|
||||||
|
daysInHours={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="mt-2 w-full rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:text-sm"
|
||||||
|
onClick={() => {
|
||||||
|
shareStatus(guesses, isGameLost)
|
||||||
|
handleShare()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{SHARE_TEXT}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</BaseModal>
|
</BaseModal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
import { Dialog } from '@headlessui/react'
|
|
||||||
import { CheckIcon } from '@heroicons/react/outline'
|
|
||||||
import { MiniGrid } from '../mini-grid/MiniGrid'
|
|
||||||
import { shareStatus } from '../../lib/share'
|
|
||||||
import { BaseModal } from './BaseModal'
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
isOpen: boolean
|
|
||||||
handleClose: () => void
|
|
||||||
guesses: string[]
|
|
||||||
handleShare: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const WinModal = ({
|
|
||||||
isOpen,
|
|
||||||
handleClose,
|
|
||||||
guesses,
|
|
||||||
handleShare,
|
|
||||||
}: Props) => {
|
|
||||||
return (
|
|
||||||
<BaseModal title="You won!" isOpen={isOpen} handleClose={handleClose}>
|
|
||||||
<div>
|
|
||||||
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100">
|
|
||||||
<CheckIcon className="h-6 w-6 text-green-600" aria-hidden="true" />
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 text-center sm:mt-5">
|
|
||||||
<Dialog.Title
|
|
||||||
as="h3"
|
|
||||||
className="text-lg leading-6 font-medium text-gray-900"
|
|
||||||
>
|
|
||||||
You won!
|
|
||||||
</Dialog.Title>
|
|
||||||
<div className="mt-2">
|
|
||||||
<MiniGrid guesses={guesses} />
|
|
||||||
<p className="text-sm text-gray-500">Great job.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-5 sm:mt-6">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="inline-flex justify-center w-full rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:text-sm"
|
|
||||||
onClick={() => {
|
|
||||||
shareStatus(guesses)
|
|
||||||
handleShare()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Share
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</BaseModal>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -6,15 +6,16 @@ type Props = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Histogram = ({ gameStats }: Props) => {
|
export const Histogram = ({ gameStats }: Props) => {
|
||||||
const { totalGames, winDistribution } = gameStats
|
const winDistribution = gameStats.winDistribution
|
||||||
|
const maxValue = Math.max(...winDistribution)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="columns-1 justify-left m-2 text-sm">
|
<div className="columns-1 justify-left m-2 text-sm dark:text-white">
|
||||||
{winDistribution.map((value, i) => (
|
{winDistribution.map((value, i) => (
|
||||||
<Progress
|
<Progress
|
||||||
key={i}
|
key={i}
|
||||||
index={i}
|
index={i}
|
||||||
size={95 * (value / totalGames)}
|
size={90 * (value / maxValue)}
|
||||||
label={String(value)}
|
label={String(value)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
import { GameStats } from '../../lib/localStorage'
|
import { GameStats } from '../../lib/localStorage'
|
||||||
|
import {
|
||||||
|
TOTAL_TRIES_TEXT,
|
||||||
|
SUCCESS_RATE_TEXT,
|
||||||
|
CURRENT_STREAK_TEXT,
|
||||||
|
BEST_STREAK_TEXT,
|
||||||
|
} from '../../constants/strings'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
gameStats: GameStats
|
gameStats: GameStats
|
||||||
|
@ -12,7 +18,7 @@ const StatItem = ({
|
||||||
value: string | number
|
value: string | number
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="items-center justify-center m-1 w-1/4">
|
<div className="items-center justify-center m-1 w-1/4 dark:text-white">
|
||||||
<div className="text-3xl font-bold">{value}</div>
|
<div className="text-3xl font-bold">{value}</div>
|
||||||
<div className="text-xs">{label}</div>
|
<div className="text-xs">{label}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,10 +28,10 @@ const StatItem = ({
|
||||||
export const StatBar = ({ gameStats }: Props) => {
|
export const StatBar = ({ gameStats }: Props) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-center my-2">
|
<div className="flex justify-center my-2">
|
||||||
<StatItem label="Total tries" value={gameStats.totalGames} />
|
<StatItem label={TOTAL_TRIES_TEXT} value={gameStats.totalGames} />
|
||||||
<StatItem label="Success rate" value={`${gameStats.successRate}%`} />
|
<StatItem label={SUCCESS_RATE_TEXT} value={`${gameStats.successRate}%`} />
|
||||||
<StatItem label="Current streak" value={gameStats.currentStreak} />
|
<StatItem label={CURRENT_STREAK_TEXT} value={gameStats.currentStreak} />
|
||||||
<StatItem label="Best streak" value={gameStats.bestStreak} />
|
<StatItem label={BEST_STREAK_TEXT} value={gameStats.bestStreak} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
19
src/constants/strings.ts
Normal file
19
src/constants/strings.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
export const GAME_TITLE = 'Honigle'
|
||||||
|
|
||||||
|
export const WIN_MESSAGES = ['Uhuhuh Richtig!', 'Hmmmm, ich mag Honig', 'Nomnomnomnom!']
|
||||||
|
export const GAME_COPIED_MESSAGE = 'In die Zwischenablage kopiert'
|
||||||
|
export const ABOUT_GAME_MESSAGE = 'Über dieses Spiel'
|
||||||
|
export const NOT_ENOUGH_LETTERS_MESSAGE = 'Nicht genug Buchstaben'
|
||||||
|
export const WORD_NOT_FOUND_MESSAGE = 'Wort nicht erkannt'
|
||||||
|
export const CORRECT_WORD_MESSAGE = (solution: string) =>
|
||||||
|
`Oh Nein! Das Wort war ${solution}`
|
||||||
|
export const ENTER_TEXT = 'Enter'
|
||||||
|
export const DELETE_TEXT = 'Delete'
|
||||||
|
export const STATISTICS_TITLE = 'Statistiken'
|
||||||
|
export const GUESS_DISTRIBUTION_TEXT = 'Rateverteilung'
|
||||||
|
export const NEW_WORD_TEXT = 'Nächstes Wort in'
|
||||||
|
export const SHARE_TEXT = 'Teilen'
|
||||||
|
export const TOTAL_TRIES_TEXT = 'Anzahl Versuche'
|
||||||
|
export const SUCCESS_RATE_TEXT = 'Erfolgsquote'
|
||||||
|
export const CURRENT_STREAK_TEXT = 'Siegserie'
|
||||||
|
export const BEST_STREAK_TEXT = 'Beste Siegserie'
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,11 @@
|
||||||
import { getGuessStatuses } from './statuses'
|
import { getGuessStatuses } from './statuses'
|
||||||
import { solutionIndex } from './words'
|
import { solutionIndex } from './words'
|
||||||
|
import { GAME_TITLE } from '../constants/strings'
|
||||||
|
|
||||||
export const shareStatus = (guesses: string[]) => {
|
export const shareStatus = (guesses: string[], lost: boolean) => {
|
||||||
navigator.clipboard.writeText(
|
navigator.clipboard.writeText(
|
||||||
`Not Wordle ${solutionIndex} ${guesses.length}/6\n\n` +
|
`${GAME_TITLE} ${solutionIndex} ${lost ? 'X' : guesses.length}/6\n\n` +
|
||||||
generateEmojiGrid(guesses)
|
generateEmojiGrid(guesses)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,15 +14,17 @@ export const isWinningWord = (word: string) => {
|
||||||
|
|
||||||
export const getWordOfDay = () => {
|
export const getWordOfDay = () => {
|
||||||
// January 1, 2022 Game Epoch
|
// January 1, 2022 Game Epoch
|
||||||
const epochMs = 1641013200000
|
const epochMs = new Date('January 1, 2022 00:00:00').valueOf()
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
const msInDay = 86400000
|
const msInDay = 86400000
|
||||||
const index = Math.floor((now - epochMs) / msInDay)
|
const index = Math.floor((now - epochMs) / msInDay)
|
||||||
|
const nextday = (index + 1) * msInDay + epochMs
|
||||||
|
|
||||||
return {
|
return {
|
||||||
solution: WORDS[index].toUpperCase(),
|
solution: WORDS[0].toUpperCase(),
|
||||||
solutionIndex: index,
|
solutionIndex: index,
|
||||||
|
tomorrow: nextday,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const { solution, solutionIndex } = getWordOfDay()
|
export const { solution, solutionIndex, tomorrow } = getWordOfDay()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: ['./src/**/*.{js,jsx,ts,tsx}'],
|
content: ['./src/**/*.{js,jsx,ts,tsx}'],
|
||||||
|
darkMode: 'class',
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Reference in a new issue