mirror of
https://github.com/Vikeo/LifeTrinket.git
synced 2025-11-18 08:48:00 +00:00
Compare commits
204 Commits
tailwind-w
...
fix/analyt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
969b4f01ff | ||
|
|
d5aa60f0b3 | ||
|
|
58fb63eef1 | ||
|
|
b6a402bc9f | ||
|
|
ea5da632a8 | ||
|
|
ca4e3edb5f | ||
|
|
c6039c2a53 | ||
|
|
6d6da2ad79 | ||
|
|
51acebb50e | ||
|
|
35c1cac691 | ||
|
|
f9ad21b72e | ||
|
|
be2d76fbc2 | ||
|
|
0db1ed2144 | ||
|
|
890a095eb4 | ||
|
|
a96b6bc340 | ||
|
|
a1f8745509 | ||
|
|
1dc984e7b3 | ||
|
|
df3c30da31 | ||
|
|
5fff18e079 | ||
|
|
7203f0170a | ||
|
|
63fbceafe2 | ||
|
|
2ec9998a2a | ||
|
|
4b7b06ded3 | ||
|
|
25794fe763 | ||
|
|
7e69d4fff0 | ||
|
|
273b69b7a1 | ||
|
|
286239f6c9 | ||
|
|
7aa04820e6 | ||
|
|
b19749adcc | ||
|
|
c74dcf7675 | ||
|
|
b52c160905 | ||
|
|
980d5203a9 | ||
|
|
05bfc8c3d1 | ||
|
|
fb91ef3224 | ||
|
|
c3e8326be2 | ||
|
|
9bbe6cbb3b | ||
|
|
739048044c | ||
|
|
4656757554 | ||
|
|
9a6bfe49a1 | ||
|
|
e6e562a0c6 | ||
|
|
3e38332a68 | ||
|
|
c23509ae26 | ||
|
|
9f1cc35eab | ||
|
|
fe43e464de | ||
|
|
b074499c1f | ||
|
|
5953cf6b49 | ||
|
|
acefbb8846 | ||
|
|
adb79bf0e8 | ||
|
|
92be2e56d4 | ||
|
|
854ca81a40 | ||
|
|
9119ca5518 | ||
|
|
2837bb59b3 | ||
|
|
623aec29f8 | ||
|
|
63aace2b07 | ||
|
|
ba9ca354fc | ||
|
|
e79c728e6a | ||
|
|
97f9bb75e5 | ||
|
|
341cb3cde0 | ||
|
|
ce9c9ca997 | ||
|
|
ad485f059d | ||
|
|
92f954130f | ||
|
|
112023bdd5 | ||
|
|
4e6dc56d99 | ||
|
|
e427bfd0cf | ||
|
|
ed10edc6d2 | ||
|
|
7696b357b4 | ||
|
|
a7b78b8e7a | ||
|
|
fa95d171b7 | ||
|
|
00a556be0e | ||
|
|
3276dc81fc | ||
|
|
28c2ff536f | ||
|
|
6beddf06e2 | ||
|
|
2a885f9a43 | ||
|
|
9c27f34261 | ||
|
|
fa5829b402 | ||
|
|
71f26d0dc5 | ||
|
|
3a568fc3ab | ||
|
|
355f4bd4cd | ||
|
|
17e174bfe1 | ||
|
|
e1e8da858b | ||
|
|
e02f071415 | ||
|
|
e04f31bb67 | ||
|
|
e5386d08a4 | ||
|
|
d6cd678e9f | ||
|
|
334b46db6e | ||
|
|
e03ecc6f51 | ||
|
|
d4dc44076d | ||
|
|
a1b5cfd871 | ||
|
|
f11eea5e53 | ||
|
|
905912a7fd | ||
|
|
a90dd7c9ea | ||
|
|
ef1310d674 | ||
|
|
fe3bb6c78c | ||
|
|
6d2b3b6a6f | ||
|
|
0f86928cb3 | ||
|
|
efbfb7719c | ||
|
|
71e5614f52 | ||
|
|
677fd79bee | ||
|
|
1bff41bc10 | ||
|
|
7852520f8e | ||
|
|
04c3d60967 | ||
|
|
664e2e5688 | ||
|
|
6eb7ac9f50 | ||
|
|
ef06e0d125 | ||
|
|
ae9f5707b2 | ||
|
|
a18c253624 | ||
|
|
3f319c4f3c | ||
|
|
8b33a2a38a | ||
|
|
cc915dff36 | ||
|
|
db80e563f2 | ||
|
|
573af42b75 | ||
|
|
89e1eaff4e | ||
|
|
0f4e896342 | ||
|
|
dc1d5fe01d | ||
|
|
41e73d2c0c | ||
|
|
724dcf086c | ||
|
|
51f9c4d20e | ||
|
|
354c0dbbb2 | ||
|
|
3770d13beb | ||
|
|
13733242a2 | ||
|
|
81f3891b20 | ||
|
|
e153de9093 | ||
|
|
07775f85d2 | ||
|
|
10039175a1 | ||
|
|
bcf2a0a840 | ||
|
|
d25da5d97b | ||
|
|
f5a80e573e | ||
|
|
1f36264e39 | ||
|
|
d615cfd3ba | ||
|
|
4453b12ce6 | ||
|
|
d601a820f8 | ||
|
|
0455f43794 | ||
|
|
f94103fe51 | ||
|
|
c36668b933 | ||
|
|
f9d0346300 | ||
|
|
2f3ee74c74 | ||
|
|
f8f0788b97 | ||
|
|
bfe25eacb7 | ||
|
|
7b0965c0dd | ||
|
|
e55ea6a83a | ||
|
|
481196de9b | ||
|
|
a136dbd3f9 | ||
|
|
8d23349dac | ||
|
|
a7caa46156 | ||
|
|
39cd3faae2 | ||
|
|
bdaa8e602f | ||
|
|
26490103a9 | ||
|
|
56b07784d5 | ||
|
|
4544c689a5 | ||
|
|
8a7a4b4127 | ||
|
|
391e654779 | ||
|
|
f79a0d3e7e | ||
|
|
0664e340a0 | ||
|
|
dcb98aeac6 | ||
|
|
89b62ddac4 | ||
|
|
c704e3c7f4 | ||
|
|
69a71e2d6e | ||
|
|
18945204bf | ||
|
|
495e731636 | ||
|
|
67b231f0d4 | ||
|
|
9d42fb1635 | ||
|
|
38ad046344 | ||
|
|
bc87f073af | ||
|
|
da46c25944 | ||
|
|
104f54f5b7 | ||
|
|
101a055694 | ||
|
|
38e4cb8e8c | ||
|
|
4ecb83060d | ||
|
|
4f231ba6f4 | ||
|
|
3cd982c643 | ||
|
|
1013914cdf | ||
|
|
db85fc2102 | ||
|
|
2b0d8102d8 | ||
|
|
35e0224066 | ||
|
|
1fa433a38f | ||
|
|
26821273d7 | ||
|
|
7f19214624 | ||
|
|
8b2cd43a96 | ||
|
|
23e18f8f41 | ||
|
|
23b844c47e | ||
|
|
6ade1998f6 | ||
|
|
cc98a1b84a | ||
|
|
2ca6b91d09 | ||
|
|
00bda4fb68 | ||
|
|
d09d992535 | ||
|
|
e96e4f3aa9 | ||
|
|
cb132360a9 | ||
|
|
66b0892461 | ||
|
|
fdab09d598 | ||
|
|
ec030e7076 | ||
|
|
9812c6737c | ||
|
|
e8528f46ae | ||
|
|
4ff7f67484 | ||
|
|
bc97e459cd | ||
|
|
866dca8e41 | ||
|
|
5859bb5a49 | ||
|
|
20fb2153b3 | ||
|
|
75038212c5 | ||
|
|
b712fb6e03 | ||
|
|
3d27335fd0 | ||
|
|
18b53669d2 | ||
|
|
28954eb948 | ||
|
|
3c59d5d05b | ||
|
|
22b58c74d6 |
@@ -1,18 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: { browser: true, es2020: true },
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:react-hooks/recommended',
|
|
||||||
],
|
|
||||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
plugins: ['react-refresh'],
|
|
||||||
rules: {
|
|
||||||
'react-refresh/only-export-components': [
|
|
||||||
'warn',
|
|
||||||
{ allowConstantExport: true },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
robots.txt,1693082171694,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2
|
index.html,1716800495173,a60a0d61bc25aa1eb4446d628875d96224860fbd767a11682a6c1db79b9403e2
|
||||||
manifest.json,1693082171694,91ce94afb71f33a477f5d8d48c3f98bd7de422279c74f17b6500eec72003ac1a
|
manifest.webmanifest,1716800495173,10e89b44378da695cb672bf7d801a4ade909383751f1665416f561bbe1434e5d
|
||||||
assets/index-5265c558.css,1693082171837,08c4451946bbdf520fe337edb365417a8bbf91914c018b83866723ef52d57b43
|
manifest.json,1716800495051,91ce94afb71f33a477f5d8d48c3f98bd7de422279c74f17b6500eec72003ac1a
|
||||||
index.html,1693082171837,09e1919fbaaa3a0bf08f43eb46c29136d62a7747b41f8b5d0f4a7ed23337c344
|
registerSW.js,1716800495173,8db45a5ae8765ce12ec241d6c5bd5d30eb81dd9163b2685c5e1b867a0e487018
|
||||||
logo192.png,1693082171693,4309255bccbdbb341b5ab88708677e3d43b9e171d2666528ff932295a8257e4e
|
robots.txt,1716800495051,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2
|
||||||
favicon.ico,1693082171692,48d8c1b9714dbc9bcb012d9c9f04112d229f20e6c889bda588ac159f973e6a8d
|
sw.js,1716800496019,609c6e27a6431ece2081013efab535a218046977d97b8600a6f58ba7589373c1
|
||||||
logo512.png,1693082171694,92c7c05dc98170596d04f48e5e60eaae9535f409bcaeff129fd98fef8aba9f4e
|
logo192.png,1716800495050,14ac21c3975e11951c1eb7793eec18e1cc3274bfe7cf7858636d547a9a4efc1c
|
||||||
assets/index-5023e89e.js,1693082171838,8a6177168e95e1ca90e5ad8774252a8a02a9a78765bd329b7deae729c01aedf3
|
workbox-3e911b1d.js,1716800496020,666146b896084273226c83dca0b93f99accb195688330d6aa5c8c570bd48a4ac
|
||||||
|
assets/index-B0S3b36T.css,1716800495173,1eb1cb3d1dacc339354071ee052cdacc07d1c831c61e08b082518436f3463d83
|
||||||
|
favicon.ico,1716800495050,c3d2b7ac7f6263cca7ee26f91725eb32e7539bf0564f3b31a1bfc23cc88e9739
|
||||||
|
logo512.png,1716800495051,a9ebde1252bb76a5b474130ef07a7ed744448fde84221f715f3fec849eccbcd2
|
||||||
|
assets/index-2MMQ0HyH.js,1716800495173,f14516d3e15bb8fa5079d40fb7c1a7e0974d336340936bbed066d792ee1021e5
|
||||||
|
|||||||
20
.github/workflows/firebase-hosting-mege.yml
vendored
20
.github/workflows/firebase-hosting-mege.yml
vendored
@@ -1,20 +0,0 @@
|
|||||||
# This file was auto-generated by the Firebase CLI
|
|
||||||
# https://github.com/firebase/firebase-tools
|
|
||||||
name: Deploy to Firebase Hosting on merge
|
|
||||||
'on':
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
jobs:
|
|
||||||
build_and_deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: oven-sh/setup-bun@v1
|
|
||||||
- run: bun install && bun run build && bun run lint
|
|
||||||
- uses: FirebaseExtended/action-hosting-deploy@v0
|
|
||||||
with:
|
|
||||||
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
|
||||||
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_LIFE_TRINKET }}'
|
|
||||||
channelId: live
|
|
||||||
projectId: life-trinket
|
|
||||||
67
.github/workflows/firebase-release.yml
vendored
Normal file
67
.github/workflows/firebase-release.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
name: Deploy to Firebase Hosting
|
||||||
|
'on':
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
jobs:
|
||||||
|
build_and_deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
VITE_REPO_READ_ACCESS_TOKEN: ${{ secrets.REPO_READ_ACCESS_TOKEN }}
|
||||||
|
VITE_FIREBASE_ANALYTICS_API_KEY: ${{ secrets.FIREBASE_ANALYTICS_API_KEY }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- name: Build, lint, and deploy
|
||||||
|
run: |
|
||||||
|
pnpm install
|
||||||
|
pnpm run build
|
||||||
|
pnpm run lint
|
||||||
|
- name: Deploy to Firebase Hosting
|
||||||
|
uses: FirebaseExtended/action-hosting-deploy@v0
|
||||||
|
with:
|
||||||
|
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
|
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_LIFE_TRINKET }}'
|
||||||
|
channelId: live
|
||||||
|
projectId: life-trinket
|
||||||
|
|
||||||
|
release:
|
||||||
|
needs: build_and_deploy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: get version
|
||||||
|
id: version
|
||||||
|
uses: notiz-dev/github-action-json-property@v0.2.0
|
||||||
|
|
||||||
|
with:
|
||||||
|
path: 'package.json'
|
||||||
|
prop_path: 'version'
|
||||||
|
|
||||||
|
- name: Create Release Note
|
||||||
|
id: create_release_note
|
||||||
|
run: echo "Release Note for version ${{ steps.version.outputs.prop }}" > release_note.txt
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: ncipollo/release-action@v1.13.0
|
||||||
|
with:
|
||||||
|
bodyFile: release_note.txt
|
||||||
|
commit: ${{ github.sha }}
|
||||||
|
tag: '${{ steps.version.outputs.prop }}'
|
||||||
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
22
.github/workflows/lint_and_build.yml
vendored
22
.github/workflows/lint_and_build.yml
vendored
@@ -14,22 +14,24 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Node.js
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
version: 9
|
||||||
|
|
||||||
- name: Set up bun
|
- name: Setup Node.js
|
||||||
uses: oven-sh/setup-bun@v1
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Run lint
|
- name: Run lint
|
||||||
run: bun run lint
|
run: pnpm run lint
|
||||||
|
|
||||||
- name: Build project
|
- name: Build project
|
||||||
run: bun run build
|
run: pnpm run build
|
||||||
|
|||||||
12
CHANGELOG.md
Normal file
12
CHANGELOG.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.5.0] - 2024-01-13
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Styling with Tailwind CSS instead of styled-components
|
||||||
661
LICENSE
Normal file
661
LICENSE
Normal file
@@ -0,0 +1,661 @@
|
|||||||
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
Developers that use our General Public Licenses protect your rights
|
||||||
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
|
you this License which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
A secondary benefit of defending all users' freedom is that
|
||||||
|
improvements made in alternate versions of the program, if they
|
||||||
|
receive widespread use, become available for other developers to
|
||||||
|
incorporate. Many developers of free software are heartened and
|
||||||
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
|
||||||
|
The GNU Affero General Public License is designed specifically to
|
||||||
|
ensure that, in such cases, the modified source code becomes available
|
||||||
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
|
||||||
|
An older license, called the Affero General Public License and
|
||||||
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
|
this license.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the work with which it is combined will remain governed by version
|
||||||
|
3 of the GNU General Public License.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If your software can interact with users remotely through a computer
|
||||||
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
|
interface could display a "Source" link that leads users to an archive
|
||||||
|
of the code. There are many ways you could offer source, and different
|
||||||
|
solutions will be better for different programs; see section 13 for the
|
||||||
|
specific requirements.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
26
eslint.config.mjs
Normal file
26
eslint.config.mjs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import js from '@eslint/js';
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks';
|
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{ ignores: ['dist'] },
|
||||||
|
{
|
||||||
|
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'react-hooks': reactHooks,
|
||||||
|
'react-refresh': reactRefresh,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
{
|
{
|
||||||
"hosting": {
|
"hosting": {
|
||||||
"public": "dist",
|
"public": "dist",
|
||||||
"ignore": [
|
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
|
||||||
"firebase.json",
|
|
||||||
"**/.*",
|
|
||||||
"**/node_modules/**"
|
|
||||||
],
|
|
||||||
"rewrites": [
|
"rewrites": [
|
||||||
{
|
{
|
||||||
"source": "**",
|
"source": "**",
|
||||||
|
|||||||
68
package.json
68
package.json
@@ -1,49 +1,55 @@
|
|||||||
{
|
{
|
||||||
"name": "life-trinket",
|
"name": "life-trinket",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.4.0",
|
"version": "1.0.5",
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18",
|
"node": ">=20",
|
||||||
"npm": "please use bun or yarn :) "
|
"yarn": "use pnpm",
|
||||||
|
"npm": "please use pnpm"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"generate-icons": "npx @svgr/cli src/Icons/svgs",
|
"generate-icons": "npx @svgr/cli src/Icons/svgs",
|
||||||
"deploy": "bun build && firebase deploy --only hosting"
|
"force-deploy": "pnpm run build && firebase deploy --only hosting",
|
||||||
|
"release": "bash scripts/create-release.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mui/material": "^5.13.6",
|
"firebase": "^12.6.0",
|
||||||
"firebase": "^10.3.0",
|
"react": "^19.2.0",
|
||||||
"ga-4-react": "^0.1.281",
|
"react-dom": "^19.2.0",
|
||||||
"react": "^18.2.0",
|
"react-screen-wake-lock": "^3.1.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-swipeable": "^7.0.2",
|
||||||
"react-screen-wake-lock": "^3.0.2",
|
"react-twc": "^1.5.1",
|
||||||
"styled-components": "^6.0.7"
|
"semver": "^7.7.3",
|
||||||
|
"zod": "^4.1.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@emotion/react": "^11.11.1",
|
"@eslint/js": "^9.39.1",
|
||||||
"@emotion/styled": "^11.11.0",
|
|
||||||
"@savvywombat/tailwindcss-grid-areas": "^3.1.0",
|
|
||||||
"@svgr/cli": "^8.1.0",
|
"@svgr/cli": "^8.1.0",
|
||||||
"@types/react": "^18.2.15",
|
"@tailwindcss/postcss": "^4.1.17",
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/prop-types": "^15.7.15",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
"@types/react": "^19.2.5",
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
"@types/semver": "^7.7.1",
|
||||||
"autoprefixer": "^10.4.16",
|
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
||||||
"eslint": "^8.45.0",
|
"@typescript-eslint/parser": "^8.47.0",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"@vitejs/plugin-react-swc": "^4.2.2",
|
||||||
"eslint-plugin-react-refresh": "^0.4.3",
|
"autoprefixer": "^10.4.22",
|
||||||
"firebase-tools": "^12.5.2",
|
"eslint": "^9.39.1",
|
||||||
"install": "^0.13.0",
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"postcss": "^8.4.32",
|
"eslint-plugin-react-refresh": "^0.4.24",
|
||||||
"prettier": "2.8.8",
|
"firebase-tools": "^14.25.0",
|
||||||
"tailwindcss": "^3.4.0",
|
"postcss": "^8.5.6",
|
||||||
"typescript": "^5.0.2",
|
"prettier": "3.6.2",
|
||||||
"vite": "^4.4.5"
|
"prop-types": "^15.8.1",
|
||||||
|
"tailwindcss": "^4.1.17",
|
||||||
|
"typescript": "^5.9.3",
|
||||||
|
"typescript-eslint": "^8.47.0",
|
||||||
|
"vite": "^7.2.2",
|
||||||
|
"vite-plugin-pwa": "^1.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11424
pnpm-lock.yaml
generated
Normal file
11424
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
'@tailwindcss/postcss': {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 9.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 43 KiB |
74
scripts/README.md
Normal file
74
scripts/README.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# Release Scripts
|
||||||
|
|
||||||
|
## create-release.sh
|
||||||
|
|
||||||
|
This script automates the process of creating a new release for LifeTrinket.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run release
|
||||||
|
# or
|
||||||
|
pnpm release
|
||||||
|
# or
|
||||||
|
bash scripts/create-release.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### What it does
|
||||||
|
|
||||||
|
1. **Reads the current version** from `package.json`
|
||||||
|
2. **Checks for existing tags** - If a tag with the current version already exists, it will prompt you to update the version in `package.json` first
|
||||||
|
3. **Warns about uncommitted changes** - Prompts for confirmation if you have uncommitted changes
|
||||||
|
4. **Prompts for release description** - You can enter a multi-line description for the release
|
||||||
|
5. **Creates an annotated git tag** with the version and description
|
||||||
|
6. **Pushes the tag to remote** - This triggers the GitHub Actions workflow that builds and deploys the app
|
||||||
|
|
||||||
|
### Workflow
|
||||||
|
|
||||||
|
When you push a tag, the following happens:
|
||||||
|
|
||||||
|
1. The `firebase-release.yml` workflow is triggered
|
||||||
|
2. The app is built and deployed to Firebase Hosting
|
||||||
|
3. A GitHub release is created with the version number
|
||||||
|
|
||||||
|
### Before running
|
||||||
|
|
||||||
|
Make sure to:
|
||||||
|
|
||||||
|
1. **Update the version** in `package.json` if needed
|
||||||
|
2. **Commit all changes** you want to include in the release
|
||||||
|
3. **Test the build** with `npm run build` to ensure everything works
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Update version in package.json to 1.0.3
|
||||||
|
# 2. Commit your changes
|
||||||
|
git add .
|
||||||
|
git commit -m "feat: add new features for v1.0.3"
|
||||||
|
|
||||||
|
# 3. Run the release script
|
||||||
|
npm run release
|
||||||
|
|
||||||
|
# The script will:
|
||||||
|
# - Show current version: 1.0.3
|
||||||
|
# - Prompt for confirmation
|
||||||
|
# - Ask for release description
|
||||||
|
# - Create and push the tag
|
||||||
|
# - Trigger the deployment workflow
|
||||||
|
```
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
|
||||||
|
**"Tag already exists" error:**
|
||||||
|
|
||||||
|
- Update the version in `package.json` before creating a new release
|
||||||
|
|
||||||
|
**"Failed to push tag" error:**
|
||||||
|
|
||||||
|
- Check your git remote permissions
|
||||||
|
- Try pushing manually: `git push origin <version>`
|
||||||
|
|
||||||
|
**Script won't run:**
|
||||||
|
|
||||||
|
- Make sure the script is executable: `chmod +x scripts/create-release.sh`
|
||||||
87
scripts/create-release.sh
Executable file
87
scripts/create-release.sh
Executable file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Color codes for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${BLUE}=== LifeTrinket Release Script ===${NC}\n"
|
||||||
|
|
||||||
|
# Get current version from package.json
|
||||||
|
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
||||||
|
|
||||||
|
if [ -z "$CURRENT_VERSION" ]; then
|
||||||
|
echo -e "${RED}Error: Could not read version from package.json${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${BLUE}Current version in package.json:${NC} ${GREEN}$CURRENT_VERSION${NC}"
|
||||||
|
|
||||||
|
# Check if we're on a clean working tree
|
||||||
|
if [[ -n $(git status -s) ]]; then
|
||||||
|
echo -e "${YELLOW}Warning: You have uncommitted changes.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fetch latest tags from remote
|
||||||
|
echo -e "\n${BLUE}Fetching latest tags from remote...${NC}"
|
||||||
|
git fetch --tags
|
||||||
|
|
||||||
|
# Check if tag already exists locally or remotely
|
||||||
|
if git rev-parse "$CURRENT_VERSION" >/dev/null 2>&1; then
|
||||||
|
echo -e "${RED}Error: Tag '$CURRENT_VERSION' already exists!${NC}"
|
||||||
|
echo -e "${YELLOW}Please update the version in package.json before creating a new release.${NC}"
|
||||||
|
echo -e "${YELLOW}Current version: $CURRENT_VERSION${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the latest tag (if any)
|
||||||
|
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -n "$LATEST_TAG" ]; then
|
||||||
|
echo -e "${BLUE}Latest existing tag:${NC} ${YELLOW}$LATEST_TAG${NC}"
|
||||||
|
|
||||||
|
# Compare versions
|
||||||
|
if [ "$LATEST_TAG" = "$CURRENT_VERSION" ]; then
|
||||||
|
echo -e "${RED}Error: Latest tag matches current version ($CURRENT_VERSION)${NC}"
|
||||||
|
echo -e "${YELLOW}Please update the version in package.json before creating a new release.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}No existing tags found. This will be the first release.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get release description from user
|
||||||
|
echo -e "\n${BLUE}Enter release description (optional, press Enter to skip):${NC}"
|
||||||
|
read -r RELEASE_DESCRIPTION
|
||||||
|
|
||||||
|
if [ -z "$RELEASE_DESCRIPTION" ]; then
|
||||||
|
RELEASE_DESCRIPTION="Release $CURRENT_VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create annotated tag with description
|
||||||
|
echo -e "\n${BLUE}Creating tag '$CURRENT_VERSION'...${NC}"
|
||||||
|
git tag -a "$CURRENT_VERSION" -m "$RELEASE_DESCRIPTION"
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo -e "${RED}Error: Failed to create tag${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Tag created successfully${NC}"
|
||||||
|
|
||||||
|
# Push tag to remote
|
||||||
|
echo -e "\n${BLUE}Pushing tag to remote...${NC}"
|
||||||
|
git push origin "$CURRENT_VERSION"
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo -e "${RED}Error: Failed to push tag${NC}"
|
||||||
|
echo -e "${YELLOW}Tag was created locally. You can try pushing manually:${NC}"
|
||||||
|
echo -e " git push origin $CURRENT_VERSION"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\n${GREEN}✓ Tag pushed successfully!${NC}"
|
||||||
|
echo -e "${BLUE}GitHub Actions will now build and deploy version $CURRENT_VERSION${NC}"
|
||||||
|
echo -e "${BLUE}Check the progress at:${NC} https://github.com/Vikeo/LifeTrinket/actions"
|
||||||
26
src/App.tsx
26
src/App.tsx
@@ -1,30 +1,14 @@
|
|||||||
import { createGlobalStyle } from 'styled-components';
|
|
||||||
import { ThemeProvider } from '@mui/material';
|
|
||||||
import { LifeTrinket } from './Components/LifeTrinket';
|
import { LifeTrinket } from './Components/LifeTrinket';
|
||||||
import { theme } from './Data/theme';
|
|
||||||
import { GlobalSettingsProvider } from './Providers/GlobalSettingsProvider';
|
import { GlobalSettingsProvider } from './Providers/GlobalSettingsProvider';
|
||||||
import { PlayersProvider } from './Providers/PlayersProvider';
|
import { PlayersProvider } from './Providers/PlayersProvider';
|
||||||
|
|
||||||
const GlobalStyles = createGlobalStyle`
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
background-color: ${theme.palette.background.default};
|
|
||||||
}
|
|
||||||
#root {
|
|
||||||
touch-action: manipulation;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={theme}>
|
<PlayersProvider>
|
||||||
<GlobalStyles />
|
<GlobalSettingsProvider>
|
||||||
<PlayersProvider>
|
<LifeTrinket />
|
||||||
<GlobalSettingsProvider>
|
</GlobalSettingsProvider>
|
||||||
<LifeTrinket />
|
</PlayersProvider>
|
||||||
</GlobalSettingsProvider>
|
|
||||||
</PlayersProvider>
|
|
||||||
</ThemeProvider>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,110 +1,47 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
import { css } from 'styled-components';
|
|
||||||
import { Player, Rotation } from '../../Types/Player';
|
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { OutlinedText } from '../Misc/OutlinedText';
|
import { TwcComponentProps, twc } from 'react-twc';
|
||||||
import { decrementTimeoutMs } from '../../Data/constants';
|
import { decrementTimeoutMs } from '../../Data/constants';
|
||||||
import { usePlayers } from '../../Hooks/usePlayers';
|
import { usePlayers } from '../../Hooks/usePlayers';
|
||||||
|
import { Player, Rotation } from '../../Types/Player';
|
||||||
|
import { OutlinedText } from '../Misc/OutlinedText';
|
||||||
|
|
||||||
const CommanderDamageContainer = styled.div<{
|
export type RotationDivProps = TwcComponentProps<'div'> & {
|
||||||
$rotation: number;
|
$rotation?: number;
|
||||||
}>`
|
};
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-grow: 1;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
${(props) => {
|
export type RotationButtonProps = TwcComponentProps<'button'> & {
|
||||||
if (
|
$rotation?: number;
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
};
|
||||||
props.$rotation === Rotation.Side
|
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
flex-direction: column;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CommanderDamageButton = styled.button<{
|
export const MAX_TAP_MOVE_DISTANCE = 20;
|
||||||
$backgroundColor?: string;
|
|
||||||
$rotation: number;
|
|
||||||
}>`
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 1;
|
|
||||||
border: none;
|
|
||||||
height: 10vmin;
|
|
||||||
width: 50%;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: ${(props) => props.$backgroundColor || 'antiquewhite'};
|
|
||||||
margin: 0;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
-moz-user-select: -moz-none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
padding: 0;
|
|
||||||
${(props) => {
|
|
||||||
if (
|
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
|
||||||
props.$rotation === Rotation.Side
|
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
width: 6vmax;
|
|
||||||
height: auto;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CommanderDamageTextContainer = styled.div<{
|
const CommanderDamageContainer = twc.div<RotationDivProps>((props) => [
|
||||||
$rotation: number;
|
'flex flex-grow',
|
||||||
}>`
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
position: relative;
|
? 'flex-col'
|
||||||
top: 50%;
|
: 'flex-row',
|
||||||
left: 50%;
|
]);
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
pointer-events: none;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
-moz-user-select: -moz-none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
|
|
||||||
${(props) => {
|
const CommanderDamageButton = twc.button<RotationButtonProps>((props) => [
|
||||||
if (
|
'flex flex-grow border-none outline-none cursor-pointer m-0 p-0 webkit-user-select-none',
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
props.$rotation === Rotation.Side
|
? 'w-[6vmax] h-auto'
|
||||||
) {
|
: 'h-[10vmin] w-1/2',
|
||||||
return css`
|
]);
|
||||||
rotate: 270deg;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const PartnerDamageSeperator = styled.div<{
|
const CommanderDamageTextContainer = twc.div<RotationDivProps>((props) => [
|
||||||
$rotation: number;
|
'relative -translate-y-1/2 top-1/2 left-1/2 tabular-nums pointer-events-none select-none webkit-user-select-none',
|
||||||
}>`
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
width: 1px;
|
? 'rotate-[270deg]'
|
||||||
background-color: rgba(0, 0, 0, 1);
|
: '',
|
||||||
|
]);
|
||||||
|
|
||||||
${(props) => {
|
const PartnerDamageSeparator = twc.div<RotationDivProps>((props) => [
|
||||||
if (
|
'bg-black',
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
props.$rotation === Rotation.Side
|
? 'w-full h-px'
|
||||||
) {
|
: 'w-px',
|
||||||
return css`
|
]);
|
||||||
width: auto;
|
|
||||||
height: 1px;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
type CommanderDamageButtonComponentProps = {
|
type CommanderDamageButtonComponentProps = {
|
||||||
player: Player;
|
player: Player;
|
||||||
@@ -115,6 +52,7 @@ type CommanderDamageButtonComponentProps = {
|
|||||||
type InputProps = {
|
type InputProps = {
|
||||||
opponentIndex: number;
|
opponentIndex: number;
|
||||||
isPartner: boolean;
|
isPartner: boolean;
|
||||||
|
event: React.PointerEvent<HTMLButtonElement>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CommanderDamage = ({
|
export const CommanderDamage = ({
|
||||||
@@ -124,12 +62,8 @@ export const CommanderDamage = ({
|
|||||||
}: CommanderDamageButtonComponentProps) => {
|
}: CommanderDamageButtonComponentProps) => {
|
||||||
const { updatePlayer } = usePlayers();
|
const { updatePlayer } = usePlayers();
|
||||||
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||||
const [timeoutFinished, setTimeoutFinished] = useState(false);
|
const [downLongPressed, setDownLongPressed] = useState(false);
|
||||||
const [hasPressedDown, setHasPressedDown] = useState(false);
|
const downPositionRef = useRef({ x: 0, y: 0 });
|
||||||
|
|
||||||
const isSide =
|
|
||||||
player.settings.rotation === Rotation.Side ||
|
|
||||||
player.settings.rotation === Rotation.SideFlipped;
|
|
||||||
|
|
||||||
const handleCommanderDamageChange = (
|
const handleCommanderDamageChange = (
|
||||||
index: number,
|
index: number,
|
||||||
@@ -168,34 +102,47 @@ export const CommanderDamage = ({
|
|||||||
handleLifeChange(player.lifeTotal - increment);
|
handleLifeChange(player.lifeTotal - increment);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDownInput = ({ opponentIndex, isPartner }: InputProps) => {
|
const handleDownInput = ({ opponentIndex, isPartner, event }: InputProps) => {
|
||||||
setTimeoutFinished(false);
|
downPositionRef.current = { x: event.clientX, y: event.clientY };
|
||||||
setHasPressedDown(true);
|
setDownLongPressed(false);
|
||||||
|
|
||||||
timeoutRef.current = setTimeout(() => {
|
timeoutRef.current = setTimeout(() => {
|
||||||
setTimeoutFinished(true);
|
setDownLongPressed(true);
|
||||||
handleCommanderDamageChange(opponentIndex, -1, isPartner);
|
handleCommanderDamageChange(opponentIndex, -1, isPartner);
|
||||||
}, decrementTimeoutMs);
|
}, decrementTimeoutMs);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpInput = ({ opponentIndex, isPartner }: InputProps) => {
|
const handleUpInput = ({ opponentIndex, isPartner, event }: InputProps) => {
|
||||||
if (!(hasPressedDown && !timeoutFinished)) {
|
if (downLongPressed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const upPosition = { x: event.clientX, y: event.clientY };
|
||||||
|
|
||||||
|
const hasMoved =
|
||||||
|
Math.abs(upPosition.x - downPositionRef.current.x) >
|
||||||
|
MAX_TAP_MOVE_DISTANCE ||
|
||||||
|
Math.abs(upPosition.y - downPositionRef.current.y) >
|
||||||
|
MAX_TAP_MOVE_DISTANCE;
|
||||||
|
|
||||||
|
if (hasMoved) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
clearTimeout(timeoutRef.current);
|
clearTimeout(timeoutRef.current);
|
||||||
|
|
||||||
handleCommanderDamageChange(opponentIndex, 1, isPartner);
|
handleCommanderDamageChange(opponentIndex, 1, isPartner);
|
||||||
setHasPressedDown(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLeaveInput = () => {
|
const handleLeaveInput = () => {
|
||||||
setTimeoutFinished(true);
|
setDownLongPressed(true);
|
||||||
clearTimeout(timeoutRef.current);
|
clearTimeout(timeoutRef.current);
|
||||||
setHasPressedDown(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const opponentIndex = opponent.index;
|
const opponentIndex = opponent.index;
|
||||||
const fontSize = isSide ? '4vmax' : '7vmin';
|
const fontSize = player.isSide ? '4vmax' : '7vmin';
|
||||||
const fontWeight = 'bold';
|
const fontWeight = 'bold';
|
||||||
const strokeWidth = isSide ? '0.4vmax' : '0.7vmin';
|
const strokeWidth = player.isSide ? '0.4vmax' : '0.7vmin';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CommanderDamageContainer
|
<CommanderDamageContainer
|
||||||
@@ -206,16 +153,18 @@ export const CommanderDamage = ({
|
|||||||
<CommanderDamageButton
|
<CommanderDamageButton
|
||||||
key={opponentIndex}
|
key={opponentIndex}
|
||||||
$rotation={player.settings.rotation}
|
$rotation={player.settings.rotation}
|
||||||
onPointerDown={() =>
|
onPointerDown={(e) =>
|
||||||
handleDownInput({ opponentIndex, isPartner: false })
|
handleDownInput({ opponentIndex, isPartner: false, event: e })
|
||||||
|
}
|
||||||
|
onPointerUp={(e) =>
|
||||||
|
handleUpInput({ opponentIndex, isPartner: false, event: e })
|
||||||
}
|
}
|
||||||
onPointerUp={() => handleUpInput({ opponentIndex, isPartner: false })}
|
|
||||||
onPointerLeave={handleLeaveInput}
|
onPointerLeave={handleLeaveInput}
|
||||||
onContextMenu={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
onContextMenu={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}}
|
}}
|
||||||
$backgroundColor={opponent.color}
|
|
||||||
aria-label={`Commander damage. Player ${player.index}, opponent ${opponent.index}`}
|
aria-label={`Commander damage. Player ${player.index}, opponent ${opponent.index}`}
|
||||||
|
style={{ background: opponent.color }}
|
||||||
>
|
>
|
||||||
<CommanderDamageTextContainer $rotation={player.settings.rotation}>
|
<CommanderDamageTextContainer $rotation={player.settings.rotation}>
|
||||||
<OutlinedText
|
<OutlinedText
|
||||||
@@ -232,15 +181,15 @@ export const CommanderDamage = ({
|
|||||||
|
|
||||||
{opponent.settings.usePartner && (
|
{opponent.settings.usePartner && (
|
||||||
<>
|
<>
|
||||||
<PartnerDamageSeperator $rotation={player.settings.rotation} />
|
<PartnerDamageSeparator $rotation={player.settings.rotation} />
|
||||||
<CommanderDamageButton
|
<CommanderDamageButton
|
||||||
key={opponentIndex}
|
key={opponentIndex}
|
||||||
$rotation={player.settings.rotation}
|
$rotation={player.settings.rotation}
|
||||||
onPointerDown={() =>
|
onPointerDown={(e) =>
|
||||||
handleDownInput({ opponentIndex, isPartner: true })
|
handleDownInput({ opponentIndex, isPartner: true, event: e })
|
||||||
}
|
}
|
||||||
onPointerUp={() =>
|
onPointerUp={(e) =>
|
||||||
handleUpInput({ opponentIndex, isPartner: true })
|
handleUpInput({ opponentIndex, isPartner: true, event: e })
|
||||||
}
|
}
|
||||||
onPointerLeave={handleLeaveInput}
|
onPointerLeave={handleLeaveInput}
|
||||||
onContextMenu={(
|
onContextMenu={(
|
||||||
@@ -248,8 +197,8 @@ export const CommanderDamage = ({
|
|||||||
) => {
|
) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}}
|
}}
|
||||||
$backgroundColor={opponent.color}
|
|
||||||
aria-label={`Partner Commander damage. Player ${player.index}, opponent ${opponent.index}`}
|
aria-label={`Partner Commander damage. Player ${player.index}, opponent ${opponent.index}`}
|
||||||
|
style={{ background: opponent.color }}
|
||||||
>
|
>
|
||||||
<CommanderDamageTextContainer $rotation={player.settings.rotation}>
|
<CommanderDamageTextContainer $rotation={player.settings.rotation}>
|
||||||
<OutlinedText
|
<OutlinedText
|
||||||
|
|||||||
@@ -1,60 +1,42 @@
|
|||||||
import { ReactNode, useRef, useState } from 'react';
|
import { ReactNode, useRef, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import { twc } from 'react-twc';
|
||||||
import { css } from 'styled-components';
|
|
||||||
import { decrementTimeoutMs } from '../../Data/constants';
|
import { decrementTimeoutMs } from '../../Data/constants';
|
||||||
import { CounterType, Rotation } from '../../Types/Player';
|
import { CounterType, Rotation } from '../../Types/Player';
|
||||||
import { OutlinedText } from '../Misc/OutlinedText';
|
import { OutlinedText } from '../Misc/OutlinedText';
|
||||||
|
import { MAX_TAP_MOVE_DISTANCE } from './CommanderDamage';
|
||||||
|
|
||||||
const ExtraCounterContainer = styled.div`
|
const ExtraCounterContainer = twc.div`
|
||||||
display: flex;
|
flex
|
||||||
justify-content: center;
|
justify-center
|
||||||
align-items: center;
|
items-center
|
||||||
pointer-events: all;
|
pointer-events-all
|
||||||
flex-grow: 1;
|
flex-grow
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const StyledExtraCounterButton = styled.button`
|
const ExtraCounterButton = twc.button`
|
||||||
display: flex;
|
flex
|
||||||
justify-content: center;
|
justify-center
|
||||||
align-items: center;
|
items-center
|
||||||
position: relative;
|
relative
|
||||||
flex-grow: 1;
|
flex-grow
|
||||||
border: none;
|
border-none
|
||||||
outline: none;
|
outline-none
|
||||||
cursor: pointer;
|
cursor-pointer
|
||||||
background-color: transparent;
|
bg-transparent
|
||||||
user-select: none;
|
select-none
|
||||||
-webkit-touch-callout: none;
|
h-full
|
||||||
-webkit-tap-highlight-color: transparent;
|
webkit-user-select-none
|
||||||
-moz-user-select: -moz-none;
|
`;
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
height: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const IconContainer = styled.div<{
|
const IconContainer = twc.div`
|
||||||
$rotation: number;
|
w-auto opacity-50 data-[rotation-is-side=true]:-rotate-90`;
|
||||||
}>`
|
|
||||||
width: auto;
|
|
||||||
|
|
||||||
${(props) => {
|
const TextContainer = twc.div`
|
||||||
if (
|
absolute
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
top-1/2
|
||||||
props.$rotation === Rotation.Side
|
left-1/2
|
||||||
) {
|
data-[rotation-is-side=true]:-rotate-90
|
||||||
return css`
|
`;
|
||||||
rotate: -90deg;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const TextContainer = styled.div`
|
|
||||||
position: absolute;
|
|
||||||
translate: -50%;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type ExtraCounterProps = {
|
type ExtraCounterProps = {
|
||||||
Icon: ReactNode;
|
Icon: ReactNode;
|
||||||
@@ -62,6 +44,7 @@ type ExtraCounterProps = {
|
|||||||
type: CounterType;
|
type: CounterType;
|
||||||
setCounterTotal: (updatedCounterTotal: number, type: CounterType) => void;
|
setCounterTotal: (updatedCounterTotal: number, type: CounterType) => void;
|
||||||
rotation: number;
|
rotation: number;
|
||||||
|
isSide: boolean;
|
||||||
playerIndex: number;
|
playerIndex: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,14 +54,13 @@ const ExtraCounter = ({
|
|||||||
setCounterTotal,
|
setCounterTotal,
|
||||||
type,
|
type,
|
||||||
rotation,
|
rotation,
|
||||||
|
isSide,
|
||||||
playerIndex,
|
playerIndex,
|
||||||
}: ExtraCounterProps) => {
|
}: ExtraCounterProps) => {
|
||||||
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||||
const [timeoutFinished, setTimeoutFinished] = useState(false);
|
const [timeoutFinished, setTimeoutFinished] = useState(false);
|
||||||
const [hasPressedDown, setHasPressedDown] = useState(false);
|
const [hasPressedDown, setHasPressedDown] = useState(false);
|
||||||
|
const downPositionRef = useRef({ x: 0, y: 0 });
|
||||||
const isSide =
|
|
||||||
rotation === Rotation.Side || rotation === Rotation.SideFlipped;
|
|
||||||
|
|
||||||
const handleCountChange = (increment: number) => {
|
const handleCountChange = (increment: number) => {
|
||||||
if (!counterTotal) {
|
if (!counterTotal) {
|
||||||
@@ -88,7 +70,8 @@ const ExtraCounter = ({
|
|||||||
setCounterTotal(counterTotal + increment, type);
|
setCounterTotal(counterTotal + increment, type);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDownInput = () => {
|
const handleDownInput = (event: React.PointerEvent<HTMLButtonElement>) => {
|
||||||
|
downPositionRef.current = { x: event.clientX, y: event.clientY };
|
||||||
setTimeoutFinished(false);
|
setTimeoutFinished(false);
|
||||||
setHasPressedDown(true);
|
setHasPressedDown(true);
|
||||||
timeoutRef.current = setTimeout(() => {
|
timeoutRef.current = setTimeout(() => {
|
||||||
@@ -97,10 +80,23 @@ const ExtraCounter = ({
|
|||||||
}, decrementTimeoutMs);
|
}, decrementTimeoutMs);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpInput = () => {
|
const handleUpInput = (event: React.PointerEvent<HTMLButtonElement>) => {
|
||||||
if (!(hasPressedDown && !timeoutFinished)) {
|
if (!(hasPressedDown && !timeoutFinished)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const upPosition = { x: event.clientX, y: event.clientY };
|
||||||
|
|
||||||
|
const hasMoved =
|
||||||
|
Math.abs(upPosition.x - downPositionRef.current.x) >
|
||||||
|
MAX_TAP_MOVE_DISTANCE ||
|
||||||
|
Math.abs(upPosition.y - downPositionRef.current.y) >
|
||||||
|
MAX_TAP_MOVE_DISTANCE;
|
||||||
|
|
||||||
|
if (hasMoved) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
clearTimeout(timeoutRef.current);
|
clearTimeout(timeoutRef.current);
|
||||||
handleCountChange(1);
|
handleCountChange(1);
|
||||||
setHasPressedDown(false);
|
setHasPressedDown(false);
|
||||||
@@ -116,9 +112,12 @@ const ExtraCounter = ({
|
|||||||
const fontWeight = 'bold';
|
const fontWeight = 'bold';
|
||||||
const strokeWidth = isSide ? '0.4vmax' : '0.7vmin';
|
const strokeWidth = isSide ? '0.4vmax' : '0.7vmin';
|
||||||
|
|
||||||
|
const rotationIsSide =
|
||||||
|
rotation === Rotation.SideFlipped || rotation === Rotation.Side;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExtraCounterContainer>
|
<ExtraCounterContainer>
|
||||||
<StyledExtraCounterButton
|
<ExtraCounterButton
|
||||||
onPointerDown={handleDownInput}
|
onPointerDown={handleDownInput}
|
||||||
onPointerUp={handleUpInput}
|
onPointerUp={handleUpInput}
|
||||||
onPointerLeave={handleLeaveInput}
|
onPointerLeave={handleLeaveInput}
|
||||||
@@ -127,19 +126,19 @@ const ExtraCounter = ({
|
|||||||
}}
|
}}
|
||||||
aria-label={`Player ${playerIndex} extra counter: ${type}`}
|
aria-label={`Player ${playerIndex} extra counter: ${type}`}
|
||||||
>
|
>
|
||||||
<IconContainer $rotation={rotation}>
|
<IconContainer data-rotation-is-side={rotationIsSide}>
|
||||||
{Icon}
|
{Icon}
|
||||||
<TextContainer>
|
|
||||||
<OutlinedText
|
|
||||||
fontSize={fontSize}
|
|
||||||
fontWeight={fontWeight}
|
|
||||||
strokeWidth={strokeWidth}
|
|
||||||
>
|
|
||||||
{counterTotal ? counterTotal : undefined}
|
|
||||||
</OutlinedText>
|
|
||||||
</TextContainer>
|
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
</StyledExtraCounterButton>
|
<TextContainer data-rotation-is-side={rotationIsSide}>
|
||||||
|
<OutlinedText
|
||||||
|
fontSize={fontSize}
|
||||||
|
fontWeight={fontWeight}
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
>
|
||||||
|
{counterTotal ? counterTotal : undefined}
|
||||||
|
</OutlinedText>
|
||||||
|
</TextContainer>
|
||||||
|
</ExtraCounterButton>
|
||||||
</ExtraCounterContainer>
|
</ExtraCounterContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,89 +1,68 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import { TwcComponentProps, twc } from 'react-twc';
|
||||||
import { css } from 'styled-components';
|
|
||||||
import { lifeLongPressMultiplier } from '../../Data/constants';
|
import { lifeLongPressMultiplier } from '../../Data/constants';
|
||||||
|
import { Player, Rotation } from '../../Types/Player';
|
||||||
|
import { MAX_TAP_MOVE_DISTANCE } from './CommanderDamage';
|
||||||
|
|
||||||
import { Rotation } from '../../Types/Player';
|
type RotationButtonProps = TwcComponentProps<'div'> & {
|
||||||
|
|
||||||
export const StyledLifeCounterButton = styled.button`
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
color: rgba(0, 0, 0, 0.4);
|
|
||||||
font-weight: 600;
|
|
||||||
background-color: transparent;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
-moz-user-select: -moz-none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const TextContainer = styled.div<{
|
|
||||||
$align?: string;
|
$align?: string;
|
||||||
$rotation: number;
|
$rotation?: number;
|
||||||
}>`
|
};
|
||||||
position: relative;
|
|
||||||
|
|
||||||
${(props) => {
|
const LifeCounterButtonTwc = twc.button`
|
||||||
if (
|
h-full
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
w-full
|
||||||
props.$rotation === Rotation.Side
|
flex
|
||||||
) {
|
font-semibold
|
||||||
if (props.$align === 'right') {
|
bg-transparent
|
||||||
return css`
|
border-none
|
||||||
rotate: -90deg;
|
outline-none
|
||||||
bottom: 25%;
|
cursor-pointer
|
||||||
top: auto;
|
justify-center
|
||||||
`;
|
items-center
|
||||||
}
|
select-none
|
||||||
return css`
|
webkit-user-select-none
|
||||||
rotate: -90deg;
|
opacity-50
|
||||||
top: 25%;
|
`;
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.$align === 'right') {
|
const TextContainer = twc.div<RotationButtonProps>((props) => [
|
||||||
return css`
|
'relative',
|
||||||
left: 25%;
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
`;
|
? props.$align === 'right'
|
||||||
}
|
? '-rotate-90 bottom-1/4 top-auto'
|
||||||
return css`
|
: '-rotate-90 top-1/4'
|
||||||
right: 25%;
|
: 'top-auto',
|
||||||
`;
|
props.$rotation === Rotation.Flipped || props.$rotation === Rotation.Normal
|
||||||
}}
|
? props.$align === 'right'
|
||||||
`;
|
? 'left-1/4'
|
||||||
|
: 'right-1/4'
|
||||||
|
: '',
|
||||||
|
]);
|
||||||
|
|
||||||
type LifeCounterButtonProps = {
|
type LifeCounterButtonProps = {
|
||||||
lifeTotal: number;
|
player: Player;
|
||||||
setLifeTotal: (lifeTotal: number) => void;
|
setLifeTotal: (lifeTotal: number) => void;
|
||||||
rotation: number;
|
|
||||||
operation: 'add' | 'subtract';
|
operation: 'add' | 'subtract';
|
||||||
increment: number;
|
increment: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const LifeCounterButton = ({
|
const LifeCounterButton = ({
|
||||||
lifeTotal,
|
player,
|
||||||
setLifeTotal,
|
setLifeTotal,
|
||||||
rotation,
|
|
||||||
operation,
|
operation,
|
||||||
increment,
|
increment,
|
||||||
}: LifeCounterButtonProps) => {
|
}: LifeCounterButtonProps) => {
|
||||||
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||||
const [timeoutFinished, setTimeoutFinished] = useState(false);
|
const [timeoutFinished, setTimeoutFinished] = useState(false);
|
||||||
const [hasPressedDown, setHasPressedDown] = useState(false);
|
const [hasPressedDown, setHasPressedDown] = useState(false);
|
||||||
|
const downPositionRef = useRef({ x: 0, y: 0 });
|
||||||
|
|
||||||
const handleLifeChange = (increment: number) => {
|
const handleLifeChange = (increment: number) => {
|
||||||
setLifeTotal(lifeTotal + increment);
|
setLifeTotal(player.lifeTotal + increment);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDownInput = () => {
|
const handleDownInput = (event: React.PointerEvent<HTMLButtonElement>) => {
|
||||||
|
downPositionRef.current = { x: event.clientX, y: event.clientY };
|
||||||
setTimeoutFinished(false);
|
setTimeoutFinished(false);
|
||||||
setHasPressedDown(true);
|
setHasPressedDown(true);
|
||||||
timeoutRef.current = setTimeout(() => {
|
timeoutRef.current = setTimeout(() => {
|
||||||
@@ -92,10 +71,23 @@ const LifeCounterButton = ({
|
|||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpInput = () => {
|
const handleUpInput = (event: React.PointerEvent<HTMLButtonElement>) => {
|
||||||
if (!(hasPressedDown && !timeoutFinished)) {
|
if (!(hasPressedDown && !timeoutFinished)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const upPosition = { x: event.clientX, y: event.clientY };
|
||||||
|
|
||||||
|
const hasMoved =
|
||||||
|
Math.abs(upPosition.x - downPositionRef.current.x) >
|
||||||
|
MAX_TAP_MOVE_DISTANCE ||
|
||||||
|
Math.abs(upPosition.y - downPositionRef.current.y) >
|
||||||
|
MAX_TAP_MOVE_DISTANCE;
|
||||||
|
|
||||||
|
if (hasMoved) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
clearTimeout(timeoutRef.current);
|
clearTimeout(timeoutRef.current);
|
||||||
handleLifeChange(operation === 'add' ? 1 : -1);
|
handleLifeChange(operation === 'add' ? 1 : -1);
|
||||||
setHasPressedDown(false);
|
setHasPressedDown(false);
|
||||||
@@ -108,12 +100,13 @@ const LifeCounterButton = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fontSize =
|
const fontSize =
|
||||||
rotation === Rotation.SideFlipped || rotation === Rotation.Side
|
player.settings.rotation === Rotation.SideFlipped ||
|
||||||
|
player.settings.rotation === Rotation.Side
|
||||||
? '8vmax'
|
? '8vmax'
|
||||||
: '12vmin';
|
: '12vmin';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledLifeCounterButton
|
<LifeCounterButtonTwc
|
||||||
onPointerDown={handleDownInput}
|
onPointerDown={handleDownInput}
|
||||||
onPointerUp={handleUpInput}
|
onPointerUp={handleUpInput}
|
||||||
onPointerLeave={handleLeaveInput}
|
onPointerLeave={handleLeaveInput}
|
||||||
@@ -124,12 +117,15 @@ const LifeCounterButton = ({
|
|||||||
aria-label={`${operation === 'add' ? 'Add' : 'Subtract'} life`}
|
aria-label={`${operation === 'add' ? 'Add' : 'Subtract'} life`}
|
||||||
>
|
>
|
||||||
<TextContainer
|
<TextContainer
|
||||||
$rotation={rotation}
|
$rotation={player.settings.rotation}
|
||||||
$align={operation === 'add' ? 'right' : 'left'}
|
$align={operation === 'add' ? 'right' : 'left'}
|
||||||
|
data-contrast={player.iconTheme}
|
||||||
|
className="data-[contrast=dark]:text-icons-dark
|
||||||
|
data-[contrast=light]:text-icons-light"
|
||||||
>
|
>
|
||||||
{operation === 'add' ? '\u002B' : '\u2212'}
|
{operation === 'add' ? '\u002B' : '\u2212'}
|
||||||
</TextContainer>
|
</TextContainer>
|
||||||
</StyledLifeCounterButton>
|
</LifeCounterButtonTwc>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +1,15 @@
|
|||||||
import styled, { css } from 'styled-components';
|
import { twc } from 'react-twc';
|
||||||
import { Skull } from '../../Icons/generated';
|
import { Skull } from '../../Icons/generated';
|
||||||
import { Rotation } from '../../Types/Player';
|
import { Rotation } from '../../Types/Player';
|
||||||
|
import { RotationDivProps } from './CommanderDamage';
|
||||||
|
|
||||||
export const LoseButton = styled.button<{ $rotation: Rotation }>`
|
const LoseButton = twc.div<RotationDivProps>((props) => [
|
||||||
position: absolute;
|
'absolute flex-grow border-none outline-none cursor-pointer bg-interface-loseButton-background rounded-lg select-none z-[1] webkit-user-select-none py-2 px-4 ',
|
||||||
flex-grow: 1;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
top: 25%;
|
|
||||||
right: 15%;
|
|
||||||
background-color: #43434380;
|
|
||||||
border-radius: 8px;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: -moz-none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
${(props) => {
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
if (props.$rotation === Rotation.SideFlipped) {
|
? `left-[21%]`
|
||||||
return css`
|
: 'top-[21%]',
|
||||||
right: auto;
|
]);
|
||||||
top: 15%;
|
|
||||||
left: 27%;
|
|
||||||
rotate: ${props.$rotation}deg;
|
|
||||||
`;
|
|
||||||
} else if (props.$rotation === Rotation.Side) {
|
|
||||||
return css`
|
|
||||||
right: auto;
|
|
||||||
top: 15%;
|
|
||||||
left: 27%;
|
|
||||||
rotate: ${props.$rotation - 180}deg;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
type LoseButtonProps = {
|
type LoseButtonProps = {
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
@@ -45,9 +17,23 @@ type LoseButtonProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const LoseGameButton = ({ rotation, onClick }: LoseButtonProps) => {
|
export const LoseGameButton = ({ rotation, onClick }: LoseButtonProps) => {
|
||||||
|
const calcRotation =
|
||||||
|
rotation === Rotation.SideFlipped
|
||||||
|
? rotation
|
||||||
|
: rotation === Rotation.Side
|
||||||
|
? rotation - 180
|
||||||
|
: rotation === Rotation.Flipped
|
||||||
|
? rotation - 180
|
||||||
|
: rotation;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoseButton $rotation={rotation} onClick={onClick} aria-label={`Lose Game`}>
|
<LoseButton
|
||||||
<Skull size="5vmin" color="black" opacity={0.5} />
|
$rotation={rotation}
|
||||||
|
onClick={onClick}
|
||||||
|
aria-label={`Lose Game`}
|
||||||
|
style={{ rotate: `${calcRotation}deg` }}
|
||||||
|
>
|
||||||
|
<Skull size="8vmin" color="black" opacity={0.5} />
|
||||||
</LoseButton>
|
</LoseButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
import { css } from 'styled-components';
|
|
||||||
import { Rotation } from '../../Types/Player';
|
|
||||||
import { Cog } from '../../Icons/generated';
|
|
||||||
|
|
||||||
export const StyledSettingsButton = styled.button<{ $rotation: Rotation }>`
|
|
||||||
position: absolute;
|
|
||||||
flex-grow: 1;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
top: 25%;
|
|
||||||
right: 1vmax;
|
|
||||||
background-color: transparent;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
-moz-user-select: -moz-none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
z-index: 1;
|
|
||||||
${(props) => {
|
|
||||||
if (
|
|
||||||
props.$rotation === Rotation.Side ||
|
|
||||||
props.$rotation === Rotation.SideFlipped
|
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
right: auto;
|
|
||||||
top: 1vmax;
|
|
||||||
left: 27%;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
type SettingsButtonProps = {
|
|
||||||
onClick: () => void;
|
|
||||||
rotation: Rotation;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SettingsButton = ({ onClick, rotation }: SettingsButtonProps) => {
|
|
||||||
return (
|
|
||||||
<StyledSettingsButton
|
|
||||||
onClick={onClick}
|
|
||||||
$rotation={rotation}
|
|
||||||
aria-label={`Settings`}
|
|
||||||
>
|
|
||||||
<Cog size="5vmin" color="black" opacity="0.3" />
|
|
||||||
</StyledSettingsButton>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SettingsButton;
|
|
||||||
@@ -1,27 +1,13 @@
|
|||||||
|
import { twc } from 'react-twc';
|
||||||
import { Player, Rotation } from '../../Types/Player';
|
import { Player, Rotation } from '../../Types/Player';
|
||||||
import styled from 'styled-components';
|
import { CommanderDamage, RotationDivProps } from '../Buttons/CommanderDamage';
|
||||||
import { css } from 'styled-components';
|
|
||||||
import { CommanderDamage } from '../Buttons/CommanderDamage';
|
|
||||||
|
|
||||||
const CommanderDamageGrid = styled.div<{ $rotation: number }>`
|
const CommanderDamageGrid = twc.div<RotationDivProps>((props) => [
|
||||||
display: flex;
|
'flex flex-grow',
|
||||||
flex-direction: row;
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
flex-grow: 1;
|
? 'flex-col h-full w-auto'
|
||||||
width: 100%;
|
: 'flex-row w-full',
|
||||||
|
]);
|
||||||
${(props) => {
|
|
||||||
if (
|
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
|
||||||
props.$rotation === Rotation.Side
|
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
width: auto;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
type CommanderDamageBarProps = {
|
type CommanderDamageBarProps = {
|
||||||
opponents: Player[];
|
opponents: Player[];
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
import { usePlayers } from '../../Hooks/usePlayers';
|
|
||||||
import LifeCounter from '../LifeCounter/LifeCounter';
|
|
||||||
|
|
||||||
export const CountersWrapper = styled.div`
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: black;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const CountersGrid = styled.div<{ $gridTemplateAreas: string }>`
|
|
||||||
display: grid;
|
|
||||||
gap: 4px;
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 100%;
|
|
||||||
grid-template-areas: ${({ $gridTemplateAreas }) => $gridTemplateAreas};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const GridItemContainer = styled.div<{
|
|
||||||
$gridArea: string;
|
|
||||||
}>`
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
grid-area: ${(props) => props.$gridArea};
|
|
||||||
`;
|
|
||||||
|
|
||||||
type CountersProps = {
|
|
||||||
gridAreas: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Counters = ({ gridAreas }: CountersProps) => {
|
|
||||||
const { players } = usePlayers();
|
|
||||||
return (
|
|
||||||
<CountersWrapper>
|
|
||||||
<CountersGrid $gridTemplateAreas={gridAreas}>
|
|
||||||
{players.map((player) => {
|
|
||||||
return (
|
|
||||||
<GridItemContainer
|
|
||||||
key={player.index}
|
|
||||||
$gridArea={`player${player.index}`}
|
|
||||||
>
|
|
||||||
<LifeCounter
|
|
||||||
player={player}
|
|
||||||
opponents={players.filter(
|
|
||||||
(opponent) => opponent.index !== player.index
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</GridItemContainer>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</CountersGrid>
|
|
||||||
</CountersWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Counters;
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
import { CounterType, Player } from '../../Types/Player';
|
import { twc } from 'react-twc';
|
||||||
import ExtraCounter from '../Buttons/ExtraCounter';
|
import { usePlayers } from '../../Hooks/usePlayers';
|
||||||
import styled from 'styled-components';
|
|
||||||
import { css } from 'styled-components';
|
|
||||||
import { Rotation } from '../../Types/Player';
|
|
||||||
import {
|
import {
|
||||||
CommanderTax,
|
CommanderTax,
|
||||||
Energy,
|
Energy,
|
||||||
@@ -10,49 +7,23 @@ import {
|
|||||||
PartnerTax,
|
PartnerTax,
|
||||||
Poison,
|
Poison,
|
||||||
} from '../../Icons/generated';
|
} from '../../Icons/generated';
|
||||||
import { usePlayers } from '../../Hooks/usePlayers';
|
import { CounterType, Player, Rotation } from '../../Types/Player';
|
||||||
|
import { RotationDivProps } from '../Buttons/CommanderDamage';
|
||||||
|
import ExtraCounter from '../Buttons/ExtraCounter';
|
||||||
|
|
||||||
const Container = styled.div<{ $rotation: Rotation }>`
|
const Container = twc.div<RotationDivProps>((props) => [
|
||||||
width: 100%;
|
'flex',
|
||||||
height: 20vmin;
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
display: flex;
|
? 'h-full w-[8vmax]'
|
||||||
|
: 'h-[20vmin] w-full',
|
||||||
|
]);
|
||||||
|
|
||||||
${(props) => {
|
export const ExtraCountersGrid = twc.div<RotationDivProps>((props) => [
|
||||||
if (
|
'flex absolute flex-grow pointer-events-none',
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
props.$rotation === Rotation.Side
|
? 'flex-col h-full w-auto overflow-y-scroll overflow-x-hidden bottom-auto right-0'
|
||||||
) {
|
: 'flex-row w-full overflow-x-scroll overflow-y-hidden bottom-0',
|
||||||
return css`
|
]);
|
||||||
height: 100%;
|
|
||||||
width: 9.3vmax;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const ExtraCountersGrid = styled.div<{ $rotation: Rotation }>`
|
|
||||||
display: flex;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-grow: 1;
|
|
||||||
bottom: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
${(props) => {
|
|
||||||
if (
|
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
|
||||||
props.$rotation === Rotation.Side
|
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
height: 100%;
|
|
||||||
width: auto;
|
|
||||||
bottom: auto;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
type ExtraCountersBarProps = {
|
type ExtraCountersBarProps = {
|
||||||
player: Player;
|
player: Player;
|
||||||
@@ -122,13 +93,22 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
|
|||||||
{useCommanderDamage && (
|
{useCommanderDamage && (
|
||||||
<ExtraCounter
|
<ExtraCounter
|
||||||
rotation={player.settings.rotation}
|
rotation={player.settings.rotation}
|
||||||
Icon={<CommanderTax size={iconSize} opacity="0.5" color="black" />}
|
Icon={
|
||||||
|
<CommanderTax
|
||||||
|
size={iconSize}
|
||||||
|
data-contrast={player.iconTheme}
|
||||||
|
strokeWidth={0}
|
||||||
|
stroke="transparent"
|
||||||
|
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
|
||||||
|
/>
|
||||||
|
}
|
||||||
type={CounterType.CommanderTax}
|
type={CounterType.CommanderTax}
|
||||||
counterTotal={
|
counterTotal={
|
||||||
player.extraCounters?.find(
|
player.extraCounters?.find(
|
||||||
(counter) => counter.type === 'commanderTax'
|
(counter) => counter.type === 'commanderTax'
|
||||||
)?.value
|
)?.value
|
||||||
}
|
}
|
||||||
|
isSide={player.isSide}
|
||||||
setCounterTotal={handleCounterChange}
|
setCounterTotal={handleCounterChange}
|
||||||
playerIndex={player.index}
|
playerIndex={player.index}
|
||||||
/>
|
/>
|
||||||
@@ -136,13 +116,20 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
|
|||||||
{Boolean(useCommanderDamage && usePartner) && (
|
{Boolean(useCommanderDamage && usePartner) && (
|
||||||
<ExtraCounter
|
<ExtraCounter
|
||||||
rotation={player.settings.rotation}
|
rotation={player.settings.rotation}
|
||||||
Icon={<PartnerTax size={iconSize} opacity="0.5" color="black" />}
|
Icon={
|
||||||
|
<PartnerTax
|
||||||
|
size={iconSize}
|
||||||
|
data-contrast={player.iconTheme}
|
||||||
|
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
|
||||||
|
/>
|
||||||
|
}
|
||||||
type={CounterType.PartnerTax}
|
type={CounterType.PartnerTax}
|
||||||
counterTotal={
|
counterTotal={
|
||||||
player.extraCounters?.find(
|
player.extraCounters?.find(
|
||||||
(counter) => counter.type === 'partnerTax'
|
(counter) => counter.type === 'partnerTax'
|
||||||
)?.value
|
)?.value
|
||||||
}
|
}
|
||||||
|
isSide={player.isSide}
|
||||||
setCounterTotal={handleCounterChange}
|
setCounterTotal={handleCounterChange}
|
||||||
playerIndex={player.index}
|
playerIndex={player.index}
|
||||||
/>
|
/>
|
||||||
@@ -150,12 +137,19 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
|
|||||||
{usePoison && (
|
{usePoison && (
|
||||||
<ExtraCounter
|
<ExtraCounter
|
||||||
rotation={player.settings.rotation}
|
rotation={player.settings.rotation}
|
||||||
Icon={<Poison size={iconSize} opacity="0.5" color="black" />}
|
Icon={
|
||||||
|
<Poison
|
||||||
|
size={iconSize}
|
||||||
|
data-contrast={player.iconTheme}
|
||||||
|
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
|
||||||
|
/>
|
||||||
|
}
|
||||||
type={CounterType.Poison}
|
type={CounterType.Poison}
|
||||||
counterTotal={
|
counterTotal={
|
||||||
player.extraCounters?.find((counter) => counter.type === 'poison')
|
player.extraCounters?.find((counter) => counter.type === 'poison')
|
||||||
?.value
|
?.value
|
||||||
}
|
}
|
||||||
|
isSide={player.isSide}
|
||||||
setCounterTotal={handleCounterChange}
|
setCounterTotal={handleCounterChange}
|
||||||
playerIndex={player.index}
|
playerIndex={player.index}
|
||||||
/>
|
/>
|
||||||
@@ -163,12 +157,19 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
|
|||||||
{useEnergy && (
|
{useEnergy && (
|
||||||
<ExtraCounter
|
<ExtraCounter
|
||||||
rotation={player.settings.rotation}
|
rotation={player.settings.rotation}
|
||||||
Icon={<Energy size={iconSize} opacity="0.5" color="black" />}
|
Icon={
|
||||||
|
<Energy
|
||||||
|
size={iconSize}
|
||||||
|
data-contrast={player.iconTheme}
|
||||||
|
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
|
||||||
|
/>
|
||||||
|
}
|
||||||
type={CounterType.Energy}
|
type={CounterType.Energy}
|
||||||
counterTotal={
|
counterTotal={
|
||||||
player.extraCounters?.find((counter) => counter.type === 'energy')
|
player.extraCounters?.find((counter) => counter.type === 'energy')
|
||||||
?.value
|
?.value
|
||||||
}
|
}
|
||||||
|
isSide={player.isSide}
|
||||||
setCounterTotal={handleCounterChange}
|
setCounterTotal={handleCounterChange}
|
||||||
playerIndex={player.index}
|
playerIndex={player.index}
|
||||||
/>
|
/>
|
||||||
@@ -176,13 +177,20 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
|
|||||||
{useExperience && (
|
{useExperience && (
|
||||||
<ExtraCounter
|
<ExtraCounter
|
||||||
rotation={player.settings.rotation}
|
rotation={player.settings.rotation}
|
||||||
Icon={<Experience size={iconSize} opacity="0.5" color="black" />}
|
Icon={
|
||||||
|
<Experience
|
||||||
|
size={iconSize}
|
||||||
|
data-contrast={player.iconTheme}
|
||||||
|
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
|
||||||
|
/>
|
||||||
|
}
|
||||||
type={CounterType.Experience}
|
type={CounterType.Experience}
|
||||||
counterTotal={
|
counterTotal={
|
||||||
player.extraCounters?.find(
|
player.extraCounters?.find(
|
||||||
(counter) => counter.type === 'experience'
|
(counter) => counter.type === 'experience'
|
||||||
)?.value
|
)?.value
|
||||||
}
|
}
|
||||||
|
isSide={player.isSide}
|
||||||
setCounterTotal={handleCounterChange}
|
setCounterTotal={handleCounterChange}
|
||||||
playerIndex={player.index}
|
playerIndex={player.index}
|
||||||
/>
|
/>
|
||||||
|
|||||||
64
src/Components/Dialogs/Dialog.tsx
Normal file
64
src/Components/Dialogs/Dialog.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { Close } from '../../Icons/generated';
|
||||||
|
import { useAnalytics } from '../../Hooks/useAnalytics';
|
||||||
|
import { Separator } from '../Misc/Separator';
|
||||||
|
|
||||||
|
export const Dialog: React.FC<{
|
||||||
|
id: string;
|
||||||
|
title?: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
dialogRef: React.MutableRefObject<HTMLDialogElement | null>;
|
||||||
|
}> = ({ id, title, children, dialogRef }) => {
|
||||||
|
const analytics = useAnalytics();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!dialogRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogRef.current.addEventListener('click', (e) => {
|
||||||
|
const dialogDimensions = dialogRef.current!.getBoundingClientRect();
|
||||||
|
if (
|
||||||
|
(e.clientX < dialogDimensions.left ||
|
||||||
|
e.clientX > dialogDimensions.right ||
|
||||||
|
e.clientY < dialogDimensions.top ||
|
||||||
|
e.clientY > dialogDimensions.bottom) &&
|
||||||
|
dialogRef.current?.open
|
||||||
|
) {
|
||||||
|
analytics.trackEvent(`${id}_outside_clicked`);
|
||||||
|
dialogRef.current?.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<dialog
|
||||||
|
id={id}
|
||||||
|
ref={dialogRef}
|
||||||
|
className="backdrop:bg-background-backdrop border-none backdrop:backdrop-blur-[1px] open:visible invisible bg-transparent overflow-visible my-0 justify-self-center top-[10%]"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
analytics.trackEvent(`${id}_cross_clicked`);
|
||||||
|
dialogRef.current?.close();
|
||||||
|
}}
|
||||||
|
className="flex absolute -top-2 right-2 z-10 w-10 h-10 bg-background-default rounded-full"
|
||||||
|
>
|
||||||
|
<Close className="text-primary-main size-full" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="bg-background-default rounded-2xl max-w-[548px] max-h-[80vh] flex flex-col">
|
||||||
|
{title && (
|
||||||
|
<div className="text-2xl text-center text-text-primary px-8 pt-4">
|
||||||
|
<h2 className="">{title}</h2>
|
||||||
|
<Separator height="1px" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="h-full overflow-auto text-text-primary show-scrollbar px-8 pb-8">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
168
src/Components/Dialogs/InfoDialog.tsx
Normal file
168
src/Components/Dialogs/InfoDialog.tsx
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
import { useAnalytics } from '../../Hooks/useAnalytics';
|
||||||
|
import {
|
||||||
|
CommanderTax,
|
||||||
|
Energy,
|
||||||
|
Experience,
|
||||||
|
Monarch,
|
||||||
|
PartnerTax,
|
||||||
|
Poison,
|
||||||
|
} from '../../Icons/generated';
|
||||||
|
import { BuyMeCoffee } from '../../Icons/generated/Support';
|
||||||
|
import { Separator } from '../Misc/Separator';
|
||||||
|
import { Paragraph } from '../Misc/TextComponents';
|
||||||
|
import { Dialog } from './Dialog';
|
||||||
|
|
||||||
|
export const InfoDialog = ({
|
||||||
|
dialogRef,
|
||||||
|
}: {
|
||||||
|
dialogRef: React.MutableRefObject<HTMLDialogElement | null>;
|
||||||
|
}) => {
|
||||||
|
const analytics = useAnalytics();
|
||||||
|
return (
|
||||||
|
<Dialog id="info" title="Info" dialogRef={dialogRef}>
|
||||||
|
<div className=" text-text-primary">
|
||||||
|
<h2 className="text-md underline">Contributors</h2>
|
||||||
|
<div className="flex flex-row items-center gap-1 text-sm">
|
||||||
|
{/* <Trinket className="size-4" /> */}
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href="https://github.com/Vikeo"
|
||||||
|
target="_blank"
|
||||||
|
className="text-text-secondary underline"
|
||||||
|
>
|
||||||
|
Vikeo
|
||||||
|
</a>
|
||||||
|
: Creator
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href="#" className="text-text-secondary underline">
|
||||||
|
Elin
|
||||||
|
</a>
|
||||||
|
: Icon design
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-text-primary">
|
||||||
|
Visit the{' '}
|
||||||
|
<a
|
||||||
|
href="https://github.com/Vikeo/LifeTrinket"
|
||||||
|
target="_blank"
|
||||||
|
className="text-text-secondary underline"
|
||||||
|
>
|
||||||
|
GitHub
|
||||||
|
</a>{' '}
|
||||||
|
to contribute or learn more about this web app.
|
||||||
|
</div>
|
||||||
|
<Separator height="1px" />
|
||||||
|
</div>
|
||||||
|
<div className="text-text-primary mt-4">
|
||||||
|
<div className="text">
|
||||||
|
<h2 className="text-xl">📋 Usage Guide</h2>
|
||||||
|
<Separator height="1px" />
|
||||||
|
</div>
|
||||||
|
<div className="text-text-primary">
|
||||||
|
<Paragraph className="mb-4">
|
||||||
|
There are some controls that you might not know about, so here's a
|
||||||
|
short list of them.
|
||||||
|
</Paragraph>
|
||||||
|
<h3 className="text-lg font-bold mb-2">Life counter</h3>
|
||||||
|
<ul className="list-disc ml-6 mb-4">
|
||||||
|
<li>
|
||||||
|
<strong>Tap</strong> on a player's + or - button to add or
|
||||||
|
subtract <strong>1 life</strong>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Long press</strong> on a player's + or - button to add or
|
||||||
|
subtract <strong>10 life</strong>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3 className="text-lg font-bold mb-2">
|
||||||
|
Commander damage and other counters
|
||||||
|
</h3>
|
||||||
|
<ul className="list-disc ml-6 mb-4">
|
||||||
|
<li>
|
||||||
|
<strong>Tap</strong> on the counter to add{' '}
|
||||||
|
<strong>1 counter</strong>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Long press</strong> on the counter to subtract{' '}
|
||||||
|
<strong>1 counter</strong>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3 className="text-lg font-bold mb-1">Other counter symbols</h3>
|
||||||
|
<ul className="list-disc ml-6 mb-4">
|
||||||
|
<li className="flex items-center">
|
||||||
|
<CommanderTax className="size-6" /> - Commander Tax
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<PartnerTax className="size-6" /> - Partner Tax
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<Poison className="size-6" /> - Poison
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<Energy className="size-6" /> - Energy
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<Experience className="size-6" /> - Experience
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<Monarch className="size-6" /> - Monarch
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3 className="text-lg font-bold mb-2">Other functionality</h3>
|
||||||
|
<ul className="list-disc ml-6">
|
||||||
|
<li>
|
||||||
|
<Paragraph className="mb-1">
|
||||||
|
When a player is <strong>at or below 0 life</strong>, has taken{' '}
|
||||||
|
<strong>21 or more Commander Damage</strong> or has{' '}
|
||||||
|
<strong>10 or more poison counters</strong>, a button with a
|
||||||
|
skull will appear on that player's card. Tapping it will dim the
|
||||||
|
player's card.
|
||||||
|
</Paragraph>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Paragraph className="mb-4">
|
||||||
|
Swiping <strong>down</strong> on a player's card will show that
|
||||||
|
player's settings menu.
|
||||||
|
</Paragraph>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3 className="text-lg font-bold mb-2">Acknowledgements </h3>
|
||||||
|
<Paragraph>
|
||||||
|
LifeTrinket is unofficial Fan Content permitted under the{' '}
|
||||||
|
<a
|
||||||
|
href="https://company.wizards.com/en/legal/fancontentpolicy"
|
||||||
|
className="text-text-secondary underline"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Fan Content Policy
|
||||||
|
</a>
|
||||||
|
. Not approved or endorsed by Wizards. Portions of the material or
|
||||||
|
concepts, such as "Commander", used are property of Wizards of the
|
||||||
|
Coast. ©Wizards of the Coast LLC.
|
||||||
|
</Paragraph>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-center mt-4">
|
||||||
|
<a
|
||||||
|
className="flex flex-row items-center self-center border-none cursor-pointer bg-primary-main rounded-md mx-4 pr-4 pl-3 py-2 transition-colors duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
|
||||||
|
onClick={() => {
|
||||||
|
analytics.trackEvent('click_bmc');
|
||||||
|
}}
|
||||||
|
href="https://www.buymeacoffee.com/vikeo"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<BuyMeCoffee height="1.5rem" width="1.5rem" className="mr-2" />
|
||||||
|
<Paragraph className="text-xs">Buy me a tea</Paragraph>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
320
src/Components/Dialogs/SettingsDialog.tsx
Normal file
320
src/Components/Dialogs/SettingsDialog.tsx
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
import { twc } from 'react-twc';
|
||||||
|
import { useAnalytics } from '../../Hooks/useAnalytics';
|
||||||
|
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
||||||
|
import { PreStartMode } from '../../Types/Settings';
|
||||||
|
import { InstallPWAButton } from '../Misc/InstallPWAButton';
|
||||||
|
import { Separator } from '../Misc/Separator';
|
||||||
|
import { Paragraph } from '../Misc/TextComponents';
|
||||||
|
import { ToggleButton } from '../Misc/ToggleButton';
|
||||||
|
import { Dialog } from './Dialog';
|
||||||
|
|
||||||
|
const SettingContainer = twc.div`w-full flex flex-col mb-2`;
|
||||||
|
|
||||||
|
const ToggleContainer = twc.div`flex flex-row justify-between items-center -mb-1`;
|
||||||
|
|
||||||
|
const Description = twc.p`mr-16 text-xs text-left text-text-secondary mt-1`;
|
||||||
|
|
||||||
|
const baseGithubReleasesUrl =
|
||||||
|
'https://github.com/Vikeo/LifeTrinket/releases/tag/';
|
||||||
|
|
||||||
|
export const SettingsDialog = ({
|
||||||
|
dialogRef,
|
||||||
|
}: {
|
||||||
|
dialogRef: React.MutableRefObject<HTMLDialogElement | null>;
|
||||||
|
}) => {
|
||||||
|
const { settings, setSettings, isPWA, version } = useGlobalSettings();
|
||||||
|
const analytics = useAnalytics();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog id="settings" title="⚙️ Settings ⚙️" dialogRef={dialogRef}>
|
||||||
|
<div className="flex flex-col mb-2 w-full">
|
||||||
|
<div className="text-text-primary flex items-center gap-2">
|
||||||
|
Current version: {version.installedVersion}{' '}
|
||||||
|
{version.isLatest && (
|
||||||
|
<span className="text-sm text-text-secondary">(latest)</span>
|
||||||
|
)}
|
||||||
|
<div className="text-xs text-text-primary opacity-75">
|
||||||
|
(
|
||||||
|
<a
|
||||||
|
href={baseGithubReleasesUrl + version.installedVersion}
|
||||||
|
target="_blank"
|
||||||
|
className="underline"
|
||||||
|
onClick={() => {
|
||||||
|
analytics.trackEvent(
|
||||||
|
`current_change_log_clicked_v${version.installedVersion}`
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Release notes
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{!version.isLatest && version.remoteVersion && (
|
||||||
|
<>
|
||||||
|
<div className="flex gap-2 items-center mt-2">
|
||||||
|
<Paragraph className="text-text-secondary">
|
||||||
|
{version.remoteVersion} available!
|
||||||
|
</Paragraph>
|
||||||
|
<div className="text-xs text-text-primary opacity-75">
|
||||||
|
(
|
||||||
|
<a
|
||||||
|
href={baseGithubReleasesUrl + version.remoteVersion}
|
||||||
|
target="_blank"
|
||||||
|
className="underline"
|
||||||
|
onClick={() => {
|
||||||
|
analytics.trackEvent(
|
||||||
|
`new_change_log_clicked_v${version.remoteVersion}`
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Release notes
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="flex justify-center items-center self-start mt-2 bg-primary-main px-3 py-1 rounded-md"
|
||||||
|
onClick={() => {
|
||||||
|
{
|
||||||
|
analytics.trackEvent(`pressed_update`, {
|
||||||
|
toVersion: version.remoteVersion,
|
||||||
|
fromVersion: version.installedVersion,
|
||||||
|
});
|
||||||
|
window?.location?.reload();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="text-sm">Update</span>
|
||||||
|
<span className="text-xs"> (reload app)</span>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Separator height="1px" />
|
||||||
|
|
||||||
|
<SettingContainer>
|
||||||
|
<ToggleContainer>
|
||||||
|
<label>Show Player Menu Cog</label>
|
||||||
|
<ToggleButton
|
||||||
|
checked={settings.showPlayerMenuCog}
|
||||||
|
onChange={() => {
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
showPlayerMenuCog: !settings.showPlayerMenuCog,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ToggleContainer>
|
||||||
|
<Description>
|
||||||
|
A cog on the top right of each player's card will be shown if this is
|
||||||
|
enabled.
|
||||||
|
</Description>
|
||||||
|
</SettingContainer>
|
||||||
|
<SettingContainer>
|
||||||
|
<ToggleContainer>
|
||||||
|
<label>Show Start Player</label>
|
||||||
|
<ToggleButton
|
||||||
|
checked={settings.showStartingPlayer}
|
||||||
|
onChange={() => {
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
showStartingPlayer: !settings.showStartingPlayer,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ToggleContainer>
|
||||||
|
<Description>
|
||||||
|
On start or reset of game, will pick a random starting player,
|
||||||
|
according to the <b>Pre-Start mode</b>
|
||||||
|
</Description>
|
||||||
|
</SettingContainer>
|
||||||
|
<SettingContainer>
|
||||||
|
<div className="flex flex-row justify-between items-center mb-1">
|
||||||
|
<label htmlFor="pre-start-modes">Player selection style</label>
|
||||||
|
<select
|
||||||
|
name="pre-start-modes"
|
||||||
|
id="pre-start-modes"
|
||||||
|
value={settings.preStartMode}
|
||||||
|
className="bg-secondary-main border-none outline-none text-text-primary rounded-md p-1 text-xs disabled:saturate-50 font-semibold"
|
||||||
|
onChange={(e) => {
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
preStartMode: e.target.value as PreStartMode,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
disabled={!settings.showStartingPlayer}
|
||||||
|
>
|
||||||
|
<option value={PreStartMode.None}>Instant</option>
|
||||||
|
<option value={PreStartMode.RandomKing}>Royal Shuffle</option>
|
||||||
|
<option value={PreStartMode.FingerGame}>Touch Roulette</option>
|
||||||
|
<option value={PreStartMode.Trivia}>Group Trivia</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-left text-text-secondary">
|
||||||
|
Different ways to determine the starting player before the game
|
||||||
|
starts.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{settings.preStartMode === PreStartMode.None && (
|
||||||
|
<div className="text-xs text-left text-text-secondary mt-1">
|
||||||
|
<span className="text-text-primary">Instant:</span> A random
|
||||||
|
starting player will simply be shown on start.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{settings.preStartMode === PreStartMode.RandomKing && (
|
||||||
|
<div className="text-xs text-left text-text-secondary mt-1">
|
||||||
|
<span className="text-text-primary">Royal Shuffle:</span> Randomly
|
||||||
|
pass a crown between all players, press the screen to stop it. The
|
||||||
|
player who has the crown when it stops gets to start.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{settings.preStartMode === PreStartMode.FingerGame && (
|
||||||
|
<div className="text-xs text-left text-text-secondary mt-1">
|
||||||
|
<span className="text-text-primary">Touch Roulette:</span> All
|
||||||
|
players put a finger on the screen, one will be chosen at random.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{settings.preStartMode === PreStartMode.Trivia && (
|
||||||
|
<div className="text-xs text-left text-text-secondary mt-1">
|
||||||
|
<span className="text-text-primary">Group Trivia:</span> A random
|
||||||
|
"who is the most ..." type question will be shown, the group decides
|
||||||
|
which player fits the question best.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</SettingContainer>
|
||||||
|
<SettingContainer>
|
||||||
|
<ToggleContainer>
|
||||||
|
<label>Keep Awake</label>
|
||||||
|
<ToggleButton
|
||||||
|
checked={settings.keepAwake}
|
||||||
|
onChange={() => {
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
keepAwake: !settings.keepAwake,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ToggleContainer>
|
||||||
|
<Description>
|
||||||
|
Will prevent device from going to sleep while this app is open if this
|
||||||
|
is enabled.
|
||||||
|
</Description>
|
||||||
|
</SettingContainer>
|
||||||
|
{(!window.isIOS || window.isIPad) && (
|
||||||
|
<SettingContainer>
|
||||||
|
<ToggleContainer>
|
||||||
|
<label>Fullscreen on start</label>
|
||||||
|
<ToggleButton
|
||||||
|
checked={settings.goFullscreenOnStart}
|
||||||
|
onChange={() => {
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
goFullscreenOnStart: !settings.goFullscreenOnStart,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ToggleContainer>
|
||||||
|
<Description>
|
||||||
|
Will enter fullscreen mode when starting a game if this is enabled.
|
||||||
|
</Description>
|
||||||
|
</SettingContainer>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<SettingContainer>
|
||||||
|
<ToggleContainer>
|
||||||
|
<label>Show animations</label>
|
||||||
|
<ToggleButton
|
||||||
|
checked={settings.showAnimations}
|
||||||
|
onChange={() => {
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
showAnimations: !settings.showAnimations,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ToggleContainer>
|
||||||
|
<Description>
|
||||||
|
Disables the following animation:
|
||||||
|
<ul className="pl-1 list-inside">
|
||||||
|
<li className="list-disc">Glow effect on start menu</li>
|
||||||
|
</ul>
|
||||||
|
</Description>
|
||||||
|
</SettingContainer>
|
||||||
|
<SettingContainer>
|
||||||
|
<ToggleContainer>
|
||||||
|
<label>Show Match Score</label>
|
||||||
|
<ToggleButton
|
||||||
|
checked={settings.showMatchScore}
|
||||||
|
onChange={() => {
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
showMatchScore: !settings.showMatchScore,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ToggleContainer>
|
||||||
|
<Description>
|
||||||
|
Shows a score badge on each player's card to track wins across multiple games.
|
||||||
|
</Description>
|
||||||
|
</SettingContainer>
|
||||||
|
<Separator height="1px" />
|
||||||
|
<div className="flex w-full justify-center">
|
||||||
|
<button
|
||||||
|
className="mt-1 mb-1 bg-primary-main px-3 py-1 rounded-md duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark font-bold"
|
||||||
|
onClick={() => {
|
||||||
|
analytics.trackEvent('settings_save_clicked');
|
||||||
|
dialogRef.current?.close();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="text-sm">Save and Close</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{!isPWA && (
|
||||||
|
<>
|
||||||
|
{window.isIOS && (
|
||||||
|
<>
|
||||||
|
<Separator height="1px" />
|
||||||
|
<SettingContainer>
|
||||||
|
<ToggleContainer>
|
||||||
|
<Paragraph>
|
||||||
|
<b>Tip:</b> You can <b>add this webapp to your home page</b>{' '}
|
||||||
|
to have it act just like a normal app!
|
||||||
|
</Paragraph>
|
||||||
|
</ToggleContainer>
|
||||||
|
<Description className="mt-1">
|
||||||
|
If you do, this web app will work offline and the toolbar will
|
||||||
|
be automatically hidden.
|
||||||
|
</Description>
|
||||||
|
</SettingContainer>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!window.isIOS && (
|
||||||
|
<>
|
||||||
|
<Separator height="1px" />
|
||||||
|
<SettingContainer>
|
||||||
|
<ToggleContainer>
|
||||||
|
<Paragraph>
|
||||||
|
<b>Tip:</b> You can <b>install this page as a PWA</b> to
|
||||||
|
have it act just like a normal app!
|
||||||
|
</Paragraph>
|
||||||
|
</ToggleContainer>
|
||||||
|
<Description className="mt-1">
|
||||||
|
If you do, this web app will work offline and the toolbar will
|
||||||
|
be automatically hidden. PWA stands for Progressive Web
|
||||||
|
Application
|
||||||
|
</Description>
|
||||||
|
</SettingContainer>
|
||||||
|
<div className="flex w-full justify-center">
|
||||||
|
<InstallPWAButton />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Separator height="1px" />
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
85
src/Components/GameOver/GameOver.tsx
Normal file
85
src/Components/GameOver/GameOver.tsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { twc } from 'react-twc';
|
||||||
|
import { Player } from '../../Types/Player';
|
||||||
|
|
||||||
|
const Overlay = twc.div`
|
||||||
|
fixed top-0 left-0 w-[100dvmax] h-[100dvmin]
|
||||||
|
bg-black/80 backdrop-blur-sm
|
||||||
|
flex items-center justify-center
|
||||||
|
z-50
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Modal = twc.div`
|
||||||
|
bg-background-default
|
||||||
|
rounded-2xl p-8
|
||||||
|
max-w-md w-[90%]
|
||||||
|
shadow-2xl
|
||||||
|
flex flex-col gap-6
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Title = twc.h2`
|
||||||
|
text-[7vmin] font-bold text-center
|
||||||
|
text-text-primary
|
||||||
|
-mb-4
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ButtonContainer = twc.div`
|
||||||
|
flex flex-col gap-3
|
||||||
|
`;
|
||||||
|
|
||||||
|
const WinnerName = twc.div`
|
||||||
|
text-[6vmin] font-bold text-center
|
||||||
|
py-[2vmin] px-[3vmin] rounded-xl
|
||||||
|
text-white
|
||||||
|
mb-0
|
||||||
|
`;
|
||||||
|
|
||||||
|
const PrimaryButton = twc.button`
|
||||||
|
py-[2vmin] px-[3vmin] rounded-xl
|
||||||
|
text-[4vmin] font-semibold
|
||||||
|
bg-interface-primary
|
||||||
|
text-white
|
||||||
|
transition-all duration-200
|
||||||
|
hover:scale-105 active:scale-95
|
||||||
|
border-3 border-white/50
|
||||||
|
shadow-lg shadow-interface-primary/50
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SecondaryButton = twc.button`
|
||||||
|
py-[2vmin] px-[3vmin] rounded-xl
|
||||||
|
text-[4vmin] font-semibold
|
||||||
|
bg-secondary-main
|
||||||
|
text-text-primary
|
||||||
|
transition-all duration-200
|
||||||
|
hover:scale-105 active:scale-95
|
||||||
|
border-3 border-primary-main
|
||||||
|
shadow-lg shadow-secondary-main/50
|
||||||
|
`;
|
||||||
|
|
||||||
|
type GameOverProps = {
|
||||||
|
winner: Player;
|
||||||
|
onStartNextGame: () => void;
|
||||||
|
onStay: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GameOver = ({
|
||||||
|
winner,
|
||||||
|
onStartNextGame,
|
||||||
|
onStay,
|
||||||
|
}: GameOverProps) => {
|
||||||
|
return (
|
||||||
|
<Overlay>
|
||||||
|
<Modal>
|
||||||
|
<Title>Winner</Title>
|
||||||
|
<WinnerName style={{ backgroundColor: winner.color }}>
|
||||||
|
{winner.name || `Player ${winner.index + 1}`}
|
||||||
|
</WinnerName>
|
||||||
|
<ButtonContainer>
|
||||||
|
<SecondaryButton onClick={onStartNextGame}>
|
||||||
|
Start Next Game
|
||||||
|
</SecondaryButton>
|
||||||
|
<PrimaryButton onClick={onStay}>Close</PrimaryButton>
|
||||||
|
</ButtonContainer>
|
||||||
|
</Modal>
|
||||||
|
</Overlay>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,115 +1,42 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import styled, { css, keyframes } from 'styled-components';
|
import { twc } from 'react-twc';
|
||||||
|
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
||||||
import { Player, Rotation } from '../../Types/Player';
|
import { Player, Rotation } from '../../Types/Player';
|
||||||
|
import { RotationDivProps } from '../Buttons/CommanderDamage';
|
||||||
import LifeCounterButton from '../Buttons/LifeCounterButton';
|
import LifeCounterButton from '../Buttons/LifeCounterButton';
|
||||||
|
import { MonarchCrown } from '../Misc/MonarchCrown';
|
||||||
import { OutlinedText } from '../Misc/OutlinedText';
|
import { OutlinedText } from '../Misc/OutlinedText';
|
||||||
|
|
||||||
const LifeCountainer = styled.div<{
|
const LifeContainer = twc.div<RotationDivProps>((props) => [
|
||||||
$rotation: Rotation;
|
'flex flex-grow relative w-full h-full justify-between items-center',
|
||||||
}>`
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
position: relative;
|
? 'flex-col-reverse'
|
||||||
display: flex;
|
: 'flex-row',
|
||||||
flex-direction: row;
|
]);
|
||||||
flex-grow: 1;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
${(props) => {
|
const LifeCounterTextContainer = twc.div<RotationDivProps>((props) => [
|
||||||
if (
|
'absolute m-0 p-0 pointer-events-none select-none webkit-user-select-none',
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
props.$rotation === Rotation.Side
|
? 'w-full h-2/3'
|
||||||
) {
|
: 'w-2/3 h-full',
|
||||||
return css`
|
]);
|
||||||
flex-direction: column-reverse;
|
|
||||||
`;
|
const TextWrapper = twc.div`
|
||||||
}
|
flex
|
||||||
}}
|
absolute
|
||||||
|
justify-center
|
||||||
|
items-center
|
||||||
|
w-full
|
||||||
|
h-full
|
||||||
|
z-[-1]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const LifeCounterTextContainer = styled.div<{
|
const RecentDifference = twc.div`
|
||||||
$rotation: Rotation;
|
absolute min-w-[20vmin] drop-shadow-none text-center bg-interface-recentDifference-background tabular-nums rounded-full p-[6px 12px] text-[8vmin] text-interface-recentDifference-text animate-fadeOut
|
||||||
}>`
|
|
||||||
position: absolute;
|
|
||||||
width: 60%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: -moz-none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
|
|
||||||
${(props) => {
|
top-1/4 left-[50%] -translate-x-1/2
|
||||||
if (
|
data-[is-side=true]:top-1/3 data-[is-side=true]:translate-x-1/4 data-[is-side=true]:translate-y-1/2 data-[is-side=true]:rotate-[270deg] data-[is-side=true]:left-auto
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
`;
|
||||||
props.$rotation === Rotation.Side
|
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
width: 100%;
|
|
||||||
height: 60%;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const TextWrapper = styled.div`
|
|
||||||
display: flex;
|
|
||||||
position: absolute;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -1;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const fadeOut = keyframes`
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
33% {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const RecentDifference = styled.span<{ $rotation: Rotation }>`
|
|
||||||
position: absolute;
|
|
||||||
top: 40%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
min-width: 15vmin;
|
|
||||||
text-shadow: none;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
background-color: rgba(255, 255, 255, 0.6);
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
border-radius: 10vmin;
|
|
||||||
padding: 5px 10px;
|
|
||||||
font-size: 8vmin;
|
|
||||||
color: #333333;
|
|
||||||
animation: ${fadeOut} 3s 1s ease-out forwards;
|
|
||||||
|
|
||||||
${(props) => {
|
|
||||||
if (
|
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
|
||||||
props.$rotation === Rotation.Side
|
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
top: 27%;
|
|
||||||
left: 30%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
rotate: 270deg;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
type HealthProps = {
|
type HealthProps = {
|
||||||
player: Player;
|
player: Player;
|
||||||
@@ -121,27 +48,14 @@ type HealthProps = {
|
|||||||
|
|
||||||
const Health = ({
|
const Health = ({
|
||||||
player,
|
player,
|
||||||
rotation,
|
|
||||||
handleLifeChange,
|
handleLifeChange,
|
||||||
differenceKey,
|
differenceKey,
|
||||||
recentDifference,
|
recentDifference,
|
||||||
}: HealthProps) => {
|
}: HealthProps) => {
|
||||||
const [showStartingPlayer, setShowStartingPlayer] = useState(
|
|
||||||
localStorage.getItem('playing') === 'true'
|
|
||||||
);
|
|
||||||
const [fontSize, setFontSize] = useState(16);
|
const [fontSize, setFontSize] = useState(16);
|
||||||
const textContainerRef = useRef<HTMLDivElement | null>(null);
|
const textContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
const { settings } = useGlobalSettings();
|
||||||
if (!showStartingPlayer) {
|
|
||||||
const playingTimer = setTimeout(() => {
|
|
||||||
localStorage.setItem('playing', 'true');
|
|
||||||
setShowStartingPlayer(localStorage.getItem('playing') === 'true');
|
|
||||||
}, 3_000);
|
|
||||||
|
|
||||||
return () => clearTimeout(playingTimer);
|
|
||||||
}
|
|
||||||
}, [showStartingPlayer]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!textContainerRef.current) {
|
if (!textContainerRef.current) {
|
||||||
@@ -151,7 +65,6 @@ const Health = ({
|
|||||||
const textContainer = textContainerRef.current;
|
const textContainer = textContainerRef.current;
|
||||||
const resizeObserver = new ResizeObserver(() => {
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
const calcFontSize = calculateFontSize(textContainer);
|
const calcFontSize = calculateFontSize(textContainer);
|
||||||
console.log(calcFontSize);
|
|
||||||
setFontSize(calcFontSize);
|
setFontSize(calcFontSize);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -172,12 +85,13 @@ const Health = ({
|
|||||||
}, [textContainerRef]);
|
}, [textContainerRef]);
|
||||||
|
|
||||||
const calculateFontSize = (container: HTMLDivElement) => {
|
const calculateFontSize = (container: HTMLDivElement) => {
|
||||||
const isSide =
|
const widthRatio = player.isSide
|
||||||
rotation === Rotation.SideFlipped || rotation === Rotation.Side;
|
? container.clientHeight
|
||||||
|
: container.clientWidth;
|
||||||
|
|
||||||
const widthRatio = isSide ? container.clientHeight : container.clientWidth;
|
const heightRatio = player.isSide
|
||||||
|
? container.clientWidth
|
||||||
const heightRatio = isSide ? container.clientWidth : container.clientHeight;
|
: container.clientHeight;
|
||||||
|
|
||||||
const minRatio = Math.min(widthRatio, heightRatio);
|
const minRatio = Math.min(widthRatio, heightRatio);
|
||||||
|
|
||||||
@@ -188,15 +102,48 @@ const Health = ({
|
|||||||
return minRatio * scaleFactor * 1;
|
return minRatio * scaleFactor * 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isSide =
|
||||||
|
player.settings.rotation === Rotation.SideFlipped ||
|
||||||
|
player.settings.rotation === Rotation.Side;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LifeCountainer $rotation={player.settings.rotation}>
|
<LifeContainer $rotation={player.settings.rotation}>
|
||||||
|
{settings.useMonarch && <MonarchCrown player={player} />}
|
||||||
|
|
||||||
<LifeCounterButton
|
<LifeCounterButton
|
||||||
lifeTotal={player.lifeTotal}
|
player={player}
|
||||||
setLifeTotal={handleLifeChange}
|
setLifeTotal={handleLifeChange}
|
||||||
rotation={player.settings.rotation}
|
|
||||||
operation="subtract"
|
operation="subtract"
|
||||||
increment={-1}
|
increment={-1}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
data-is-side={isSide}
|
||||||
|
className="size-full absolute flex items-start justify-center pointer-events-none webkit-user-select-none
|
||||||
|
data-[is-side=true]:items-center data-[is-side=true]:justify-start
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{player.name && isSide ? (
|
||||||
|
<div className="fixed flex justify-center -rotate-90 left-[5.4vmax]">
|
||||||
|
<div
|
||||||
|
data-contrast={player.iconTheme}
|
||||||
|
className="absolute text-[4vmin] opacity-50 font-bold text-center text-nowrap
|
||||||
|
data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
|
||||||
|
>
|
||||||
|
{player.name}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
data-contrast={player.iconTheme}
|
||||||
|
className="absolute text-[4vmin] -top-[1.1vmin] opacity-50 font-bold text-center
|
||||||
|
data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
|
||||||
|
>
|
||||||
|
{player.name}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<TextWrapper>
|
<TextWrapper>
|
||||||
<LifeCounterTextContainer
|
<LifeCounterTextContainer
|
||||||
$rotation={player.settings.rotation}
|
$rotation={player.settings.rotation}
|
||||||
@@ -210,10 +157,7 @@ const Health = ({
|
|||||||
{player.lifeTotal}
|
{player.lifeTotal}
|
||||||
</OutlinedText>
|
</OutlinedText>
|
||||||
{recentDifference !== 0 && (
|
{recentDifference !== 0 && (
|
||||||
<RecentDifference
|
<RecentDifference data-is-side={isSide} key={differenceKey}>
|
||||||
key={differenceKey}
|
|
||||||
$rotation={player.settings.rotation}
|
|
||||||
>
|
|
||||||
{recentDifference > 0 ? '+' : ''}
|
{recentDifference > 0 ? '+' : ''}
|
||||||
{recentDifference}
|
{recentDifference}
|
||||||
</RecentDifference>
|
</RecentDifference>
|
||||||
@@ -221,13 +165,12 @@ const Health = ({
|
|||||||
</LifeCounterTextContainer>
|
</LifeCounterTextContainer>
|
||||||
</TextWrapper>
|
</TextWrapper>
|
||||||
<LifeCounterButton
|
<LifeCounterButton
|
||||||
lifeTotal={player.lifeTotal}
|
player={player}
|
||||||
setLifeTotal={handleLifeChange}
|
setLifeTotal={handleLifeChange}
|
||||||
rotation={player.settings.rotation}
|
|
||||||
operation="add"
|
operation="add"
|
||||||
increment={1}
|
increment={1}
|
||||||
/>
|
/>
|
||||||
</LifeCountainer>
|
</LifeContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,137 +1,92 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import styled, { css, keyframes } from 'styled-components';
|
import { useSwipeable } from 'react-swipeable';
|
||||||
import { theme } from '../../Data/theme';
|
import { twc } from 'react-twc';
|
||||||
|
import { useAnalytics } from '../../Hooks/useAnalytics';
|
||||||
|
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
||||||
|
import { usePlayers } from '../../Hooks/usePlayers';
|
||||||
|
import { Cog } from '../../Icons/generated';
|
||||||
import { Player, Rotation } from '../../Types/Player';
|
import { Player, Rotation } from '../../Types/Player';
|
||||||
|
import {
|
||||||
|
RotationButtonProps,
|
||||||
|
RotationDivProps,
|
||||||
|
} from '../Buttons/CommanderDamage';
|
||||||
import { LoseGameButton } from '../Buttons/LoseButton';
|
import { LoseGameButton } from '../Buttons/LoseButton';
|
||||||
import SettingsButton from '../Buttons/SettingsButton';
|
|
||||||
import CommanderDamageBar from '../Counters/CommanderDamageBar';
|
import CommanderDamageBar from '../Counters/CommanderDamageBar';
|
||||||
import ExtraCountersBar from '../Counters/ExtraCountersBar';
|
import ExtraCountersBar from '../Counters/ExtraCountersBar';
|
||||||
import PlayerMenu from '../Player/PlayerMenu';
|
import PlayerMenu from '../Players/PlayerMenu';
|
||||||
|
import { StartingPlayerCard } from '../PreStartGame/StartingPlayerCard';
|
||||||
import Health from './Health';
|
import Health from './Health';
|
||||||
import { usePlayers } from '../../Hooks/usePlayers';
|
|
||||||
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
|
||||||
|
|
||||||
const LifeCounterContentWrapper = styled.div<{
|
const SettingsButtonTwc = twc.button<RotationButtonProps>((props) => [
|
||||||
$backgroundColor: string;
|
'absolute flex-grow border-none outline-none cursor-pointer bg-transparent z-[1] select-none webkit-user-select-none opacity-50',
|
||||||
}>`
|
props.$rotation === Rotation.Side || props.$rotation === Rotation.SideFlipped
|
||||||
position: relative;
|
? `right-auto top-[1vmax] left-[27%]`
|
||||||
display: flex;
|
: 'top-1/4 right-[1vmax]',
|
||||||
flex-grow: 1;
|
]);
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
background-color: ${(props) => props.$backgroundColor || 'antiquewhite'};
|
|
||||||
@media (orientation: landscape) {
|
|
||||||
max-width: 100vmax;
|
|
||||||
max-height: 100vmin;
|
|
||||||
}
|
|
||||||
|
|
||||||
overflow: hidden;
|
type MatchScoreBadgeProps = RotationDivProps & {
|
||||||
`;
|
$useCommanderDamage: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
const LifeCounterWrapper = styled.div<{
|
const MatchScoreBadge = twc.div<MatchScoreBadgeProps>((props) => [
|
||||||
$rotation: Rotation;
|
'absolute flex items-center justify-center',
|
||||||
}>`
|
'bg-black/70 backdrop-blur-sm',
|
||||||
position: relative;
|
'rounded-full',
|
||||||
display: flex;
|
'w-[5vmin] h-[5vmin]',
|
||||||
flex-direction: column;
|
'text-white font-bold',
|
||||||
align-items: center;
|
'text-[3vmin]',
|
||||||
width: 100%;
|
'z-[1]',
|
||||||
height: 100%;
|
'pointer-events-none',
|
||||||
|
'select-none webkit-user-select-none',
|
||||||
|
props.$rotation === Rotation.Side || props.$rotation === Rotation.SideFlipped
|
||||||
|
? `left-[6.5vmax] bottom-[1vmax]`
|
||||||
|
: props.$useCommanderDamage
|
||||||
|
? 'left-[0.5vmax] top-[11.5vmin]'
|
||||||
|
: 'left-[0.5vmax] top-[1vmax]',
|
||||||
|
]);
|
||||||
|
|
||||||
z-index: 1;
|
type SettingsButtonProps = {
|
||||||
|
onClick: () => void;
|
||||||
|
rotation: Rotation;
|
||||||
|
iconTheme: 'light' | 'dark';
|
||||||
|
};
|
||||||
|
|
||||||
${(props) => {
|
const SettingsButton = ({
|
||||||
if (
|
onClick,
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
rotation,
|
||||||
props.$rotation === Rotation.Side
|
iconTheme,
|
||||||
) {
|
}: SettingsButtonProps) => {
|
||||||
return css`
|
return (
|
||||||
flex-direction: row;
|
<SettingsButtonTwc
|
||||||
rotate: ${props.$rotation - 90}deg;
|
onClick={onClick}
|
||||||
`;
|
$rotation={rotation}
|
||||||
}
|
aria-label={`Settings`}
|
||||||
|
>
|
||||||
|
<Cog
|
||||||
|
size="5vmin"
|
||||||
|
data-contrast={iconTheme}
|
||||||
|
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
|
||||||
|
/>
|
||||||
|
</SettingsButtonTwc>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return css`
|
const LifeCounterContentWrapper = twc.div`
|
||||||
flex-direction: column;
|
relative flex flex-grow flex-col items-center w-full h-full overflow-hidden`;
|
||||||
rotate: ${props.$rotation}deg;
|
|
||||||
`;
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const PlayerNoticeWrapper = styled.div<{
|
const LifeCounterWrapper = twc.div<RotationDivProps>((props) => [
|
||||||
$rotation: Rotation;
|
'relative flex items-center w-full h-full z-[1]',
|
||||||
$backgroundColor: string;
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
}>`
|
? `flex-row`
|
||||||
z-index: 1;
|
: `flex-col`,
|
||||||
display: flex;
|
]);
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background: ${(props) => props.$backgroundColor};
|
|
||||||
pointer-events: none;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: -moz-none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
|
|
||||||
${(props) => {
|
const PlayerLostWrapper = twc.div<RotationDivProps>((props) => [
|
||||||
if (
|
'z-[1] flex absolute w-full h-full justify-center items-center pointer-events-none select-none webkit-user-select-none bg-lifeCounter-lostWrapper opacity-75',
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
props.$rotation === Rotation.Side
|
? `rotate-[${props.$rotation - 90}deg]`
|
||||||
) {
|
: '',
|
||||||
return css`
|
]);
|
||||||
rotate: ${props.$rotation - 90}deg;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const DynamicText = styled.div<{ $rotation: Rotation }>`
|
|
||||||
font-size: 8vmin;
|
|
||||||
${(props) => {
|
|
||||||
if (
|
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
|
||||||
props.$rotation === Rotation.Side
|
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
rotate: ${props.$rotation - 180}deg;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const fadeOut = keyframes`
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
33% {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const RecentDifference = styled.span`
|
|
||||||
position: absolute;
|
|
||||||
top: 40%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
text-shadow: none;
|
|
||||||
background-color: rgba(255, 255, 255, 0.6);
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
border-radius: 50%;
|
|
||||||
padding: 5px 10px;
|
|
||||||
font-size: 8vmin;
|
|
||||||
color: #333333;
|
|
||||||
animation: ${fadeOut} 3s 1s ease-out forwards;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const hasCommanderDamageReached21 = (player: Player) => {
|
const hasCommanderDamageReached21 = (player: Player) => {
|
||||||
const commanderDamageTotals = player.commanderDamage.map(
|
const commanderDamageTotals = player.commanderDamage.map(
|
||||||
@@ -163,39 +118,82 @@ const playerCanLose = (player: Player) => {
|
|||||||
type LifeCounterProps = {
|
type LifeCounterProps = {
|
||||||
player: Player;
|
player: Player;
|
||||||
opponents: Player[];
|
opponents: Player[];
|
||||||
|
isStartingPlayer?: boolean;
|
||||||
|
matchScore?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
|
const RECENT_DIFFERENCE_TTL = 3_000;
|
||||||
|
|
||||||
|
const LifeCounter = ({ player, opponents, matchScore }: LifeCounterProps) => {
|
||||||
const { updatePlayer, updateLifeTotal } = usePlayers();
|
const { updatePlayer, updateLifeTotal } = usePlayers();
|
||||||
const { settings } = useGlobalSettings();
|
const { settings, playing } = useGlobalSettings();
|
||||||
|
const recentDifferenceTimerRef = useRef<NodeJS.Timeout | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
const [showPlayerMenu, setShowPlayerMenu] = useState(false);
|
const [showPlayerMenu, setShowPlayerMenu] = useState(false);
|
||||||
const [recentDifference, setRecentDifference] = useState(0);
|
const [recentDifference, setRecentDifference] = useState(0);
|
||||||
const [differenceKey, setDifferenceKey] = useState(Date.now());
|
const [differenceKey, setDifferenceKey] = useState(Date.now());
|
||||||
|
const [isLandscape, setIsLandscape] = useState(false);
|
||||||
|
|
||||||
|
const calcRot = player.isSide
|
||||||
|
? player.settings.rotation - 180
|
||||||
|
: player.settings.rotation;
|
||||||
|
|
||||||
|
const rotationAngle = isLandscape ? calcRot : calcRot + 90;
|
||||||
|
|
||||||
|
const handlers = useSwipeable({
|
||||||
|
trackMouse: true,
|
||||||
|
onSwipedDown: (e) => {
|
||||||
|
e.event.stopPropagation();
|
||||||
|
analytics.trackEvent('open_player_menu_swipe');
|
||||||
|
setShowPlayerMenu(true);
|
||||||
|
},
|
||||||
|
onSwipedUp: (e) => {
|
||||||
|
e.event.stopPropagation();
|
||||||
|
analytics.trackEvent('close_player_menu_swipe');
|
||||||
|
setShowPlayerMenu(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
swipeDuration: 500,
|
||||||
|
onSwiping: (e) => e.event.stopPropagation(),
|
||||||
|
rotationAngle,
|
||||||
|
});
|
||||||
|
const analytics = useAnalytics();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
if (recentDifference === 0) {
|
||||||
setRecentDifference(0);
|
clearTimeout(recentDifferenceTimerRef.current);
|
||||||
}, 3_000);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
recentDifferenceTimerRef.current = setTimeout(() => {
|
||||||
|
analytics.trackEvent('life_changed', {
|
||||||
|
lifeChangedAmount: recentDifference,
|
||||||
|
});
|
||||||
|
setRecentDifference(0);
|
||||||
|
}, RECENT_DIFFERENCE_TTL);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(recentDifferenceTimerRef.current);
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [recentDifference]);
|
}, [recentDifference]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (player.showStartingPlayer) {
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
const playingTimer = setTimeout(() => {
|
if (document.body.clientWidth > document.body.clientHeight)
|
||||||
localStorage.setItem('playing', 'true');
|
setIsLandscape(true);
|
||||||
player.showStartingPlayer = false;
|
else setIsLandscape(false);
|
||||||
updatePlayer(player);
|
return () => {
|
||||||
}, 3_000);
|
// Cleanup: disconnect the ResizeObserver when the component unmounts.
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return () => clearTimeout(playingTimer);
|
resizeObserver.observe(document.body);
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [player.showStartingPlayer]);
|
}, [document.body.clientHeight, document.body.clientWidth]);
|
||||||
|
|
||||||
player.settings.rotation === Rotation.SideFlipped ||
|
|
||||||
player.settings.rotation === Rotation.Side;
|
|
||||||
|
|
||||||
const handleLifeChange = (updatedLifeTotal: number) => {
|
const handleLifeChange = (updatedLifeTotal: number) => {
|
||||||
const difference = updateLifeTotal(player, updatedLifeTotal);
|
const difference = updateLifeTotal(player, updatedLifeTotal);
|
||||||
@@ -208,27 +206,27 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
|
|||||||
updatePlayer(updatedPlayer);
|
updatePlayer(updatedPlayer);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const calcRotation =
|
||||||
<LifeCounterContentWrapper $backgroundColor={player.color}>
|
player.settings.rotation === Rotation.SideFlipped ||
|
||||||
<LifeCounterWrapper $rotation={player.settings.rotation}>
|
player.settings.rotation === Rotation.Side
|
||||||
{settings.showStartingPlayer &&
|
? player.settings.rotation - 90
|
||||||
player.isStartingPlayer &&
|
: player.settings.rotation;
|
||||||
player.showStartingPlayer && (
|
|
||||||
<PlayerNoticeWrapper
|
|
||||||
$rotation={player.settings.rotation}
|
|
||||||
$backgroundColor={theme.palette.primary.main}
|
|
||||||
>
|
|
||||||
<DynamicText $rotation={player.settings.rotation}>
|
|
||||||
You start!
|
|
||||||
</DynamicText>
|
|
||||||
</PlayerNoticeWrapper>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
const amountOfPlayers = opponents.length + 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LifeCounterContentWrapper style={{ background: player.color }}>
|
||||||
|
<LifeCounterWrapper
|
||||||
|
$rotation={player.settings.rotation}
|
||||||
|
style={{ rotate: `${calcRotation}deg` }}
|
||||||
|
{...handlers}
|
||||||
|
>
|
||||||
|
{amountOfPlayers > 1 &&
|
||||||
|
!playing &&
|
||||||
|
settings.showStartingPlayer &&
|
||||||
|
player.isStartingPlayer && <StartingPlayerCard player={player} />}
|
||||||
{player.hasLost && (
|
{player.hasLost && (
|
||||||
<PlayerNoticeWrapper
|
<PlayerLostWrapper $rotation={player.settings.rotation} />
|
||||||
$rotation={player.settings.rotation}
|
|
||||||
$backgroundColor={'#00000070'}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<CommanderDamageBar
|
<CommanderDamageBar
|
||||||
opponents={opponents}
|
opponents={opponents}
|
||||||
@@ -236,12 +234,31 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
|
|||||||
key={player.index}
|
key={player.index}
|
||||||
handleLifeChange={handleLifeChange}
|
handleLifeChange={handleLifeChange}
|
||||||
/>
|
/>
|
||||||
<SettingsButton
|
{matchScore !== undefined && matchScore > 0 && (
|
||||||
onClick={() => {
|
<MatchScoreBadge
|
||||||
setShowPlayerMenu(!showPlayerMenu);
|
$rotation={player.settings.rotation}
|
||||||
}}
|
$useCommanderDamage={player.settings.useCommanderDamage}
|
||||||
rotation={player.settings.rotation}
|
style={{
|
||||||
/>
|
rotate:
|
||||||
|
player.settings.rotation === Rotation.Side ||
|
||||||
|
player.settings.rotation === Rotation.SideFlipped
|
||||||
|
? `-90deg`
|
||||||
|
: '0deg',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{matchScore}
|
||||||
|
</MatchScoreBadge>
|
||||||
|
)}
|
||||||
|
{settings.showPlayerMenuCog && (
|
||||||
|
<SettingsButton
|
||||||
|
onClick={() => {
|
||||||
|
analytics.trackEvent('open_player_menu_button');
|
||||||
|
setShowPlayerMenu(!showPlayerMenu);
|
||||||
|
}}
|
||||||
|
rotation={player.settings.rotation}
|
||||||
|
iconTheme={player.iconTheme}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{playerCanLose(player) && (
|
{playerCanLose(player) && (
|
||||||
<LoseGameButton
|
<LoseGameButton
|
||||||
rotation={player.settings.rotation}
|
rotation={player.settings.rotation}
|
||||||
@@ -255,12 +272,16 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
|
|||||||
recentDifference={recentDifference}
|
recentDifference={recentDifference}
|
||||||
handleLifeChange={handleLifeChange}
|
handleLifeChange={handleLifeChange}
|
||||||
/>
|
/>
|
||||||
<ExtraCountersBar player={player} />
|
|
||||||
</LifeCounterWrapper>
|
|
||||||
|
|
||||||
{showPlayerMenu && (
|
<ExtraCountersBar player={player} />
|
||||||
<PlayerMenu player={player} setShowPlayerMenu={setShowPlayerMenu} />
|
<PlayerMenu
|
||||||
)}
|
isShown={showPlayerMenu}
|
||||||
|
player={player}
|
||||||
|
setShowPlayerMenu={setShowPlayerMenu}
|
||||||
|
onForfeit={toggleGameLost}
|
||||||
|
totalPlayers={opponents.length + 1}
|
||||||
|
/>
|
||||||
|
</LifeCounterWrapper>
|
||||||
</LifeCounterContentWrapper>
|
</LifeCounterContentWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,47 +1,37 @@
|
|||||||
import styled from 'styled-components';
|
import { twc } from 'react-twc';
|
||||||
import { useGlobalSettings } from '../Hooks/useGlobalSettings';
|
import { useGlobalSettings } from '../Hooks/useGlobalSettings';
|
||||||
import StartMenu from './Views/StartMenu/StartMenu';
|
|
||||||
import { Play } from './Views/Play';
|
import { Play } from './Views/Play';
|
||||||
|
import StartMenu from './Views/StartMenu/StartMenu';
|
||||||
|
|
||||||
const StartWrapper = styled.div`
|
const StartWrapper = twc.div`max-w-fit max-h-fit`;
|
||||||
max-width: fit-content;
|
|
||||||
max-height: fit-content;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const PlayWrapper = styled.div`
|
const PlayWrapper = twc.div`relative z-0 max-w-fit max-h-fit portrait:rotate-90`;
|
||||||
position: relative;
|
|
||||||
z-index: 0;
|
|
||||||
max-width: fit-content;
|
|
||||||
max-height: fit-content;
|
|
||||||
@media (orientation: portrait) {
|
|
||||||
rotate: 90deg;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const EmergencyResetButton = styled.button`
|
const EmergencyResetButton = () => {
|
||||||
width: 100vmax;
|
const { goToStart } = useGlobalSettings();
|
||||||
height: 100vmin;
|
|
||||||
font-size: 4vmax;
|
const EmergencyResetButton = twc.button`w-[100dvmax] h-[100dvmin] absolute top-0 z-[-1] bg-background-default`;
|
||||||
position: absolute;
|
const Paragraph = twc.p`text-[4vmax] text-text-secondary`;
|
||||||
top: 0;
|
|
||||||
z-index: -1;
|
return (
|
||||||
background-color: #4e6815;
|
<EmergencyResetButton onClick={goToStart}>
|
||||||
`;
|
<Paragraph>If you can see this, something is wrong.</Paragraph>
|
||||||
|
<Paragraph>Press screen to go to start.</Paragraph>
|
||||||
|
<br />
|
||||||
|
<Paragraph>If the issue persists, please inform me.</Paragraph>
|
||||||
|
</EmergencyResetButton>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const LifeTrinket = () => {
|
export const LifeTrinket = () => {
|
||||||
const { showPlay, goToStart, initialGameSettings } = useGlobalSettings();
|
const { showPlay, initialGameSettings } = useGlobalSettings();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{showPlay && initialGameSettings ? (
|
{showPlay && initialGameSettings ? (
|
||||||
<PlayWrapper>
|
<PlayWrapper>
|
||||||
<Play />
|
<Play />
|
||||||
<EmergencyResetButton onClick={goToStart}>
|
<EmergencyResetButton />
|
||||||
<p>If you can see this, something is wrong.</p>
|
|
||||||
<p>Press screen to go to start.</p>
|
|
||||||
<br />
|
|
||||||
<p>If the issue persists, please inform me.</p>
|
|
||||||
</EmergencyResetButton>
|
|
||||||
</PlayWrapper>
|
</PlayWrapper>
|
||||||
) : (
|
) : (
|
||||||
<StartWrapper>
|
<StartWrapper>
|
||||||
|
|||||||
33
src/Components/Misc/IconCheckbox.tsx
Normal file
33
src/Components/Misc/IconCheckbox.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { ReactElement } from 'react';
|
||||||
|
|
||||||
|
export const IconCheckbox = ({
|
||||||
|
name,
|
||||||
|
icon,
|
||||||
|
checkedIcon,
|
||||||
|
checked,
|
||||||
|
onChange,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
icon: ReactElement;
|
||||||
|
checkedIcon: ReactElement;
|
||||||
|
checked: boolean;
|
||||||
|
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
className?: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
name={name}
|
||||||
|
type="checkbox"
|
||||||
|
checked={checked}
|
||||||
|
onChange={onChange}
|
||||||
|
className="sr-only peer"
|
||||||
|
/>
|
||||||
|
<div className="peer-checked:hidden block">{icon}</div>
|
||||||
|
<div className="peer-checked:block hidden">{checkedIcon}</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
import { Modal } from '@mui/material';
|
|
||||||
import { theme } from '../../Data/theme';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
export const ModalWrapper = styled.div`
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 80vw;
|
|
||||||
height: 85vh;
|
|
||||||
background-color: ${theme.palette.background.default};
|
|
||||||
padding: 1rem;
|
|
||||||
overflow: scroll;
|
|
||||||
border-radius: 1rem;
|
|
||||||
color: ${theme.palette.text.primary};
|
|
||||||
border: none;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type InfoModalProps = {
|
|
||||||
isOpen: boolean;
|
|
||||||
closeModal: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const InfoModal = ({ isOpen, closeModal }: InfoModalProps) => {
|
|
||||||
return (
|
|
||||||
<Modal open={isOpen} onClose={closeModal}>
|
|
||||||
<ModalWrapper>
|
|
||||||
<div>
|
|
||||||
<h2 style={{ textAlign: 'center' }}>📋 Usage Guide</h2>
|
|
||||||
<p>
|
|
||||||
There are some controls that you might not know about, so here's a
|
|
||||||
short list of them.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>Life counter</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>Tap</strong> on a player's + or - button to add or
|
|
||||||
subtract <strong>1 life</strong>.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Long press</strong> on a player's + or - button to add or
|
|
||||||
subtract <strong>10 life</strong>.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3>Commander damage and other counters</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>Tap</strong> on the counter to add{' '}
|
|
||||||
<strong>1 counter</strong>.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Long press</strong> on the counter to subtract{' '}
|
|
||||||
<strong>1 counter</strong>.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3>Other</h3>
|
|
||||||
<p>
|
|
||||||
When a player is <strong>at or below 0 life</strong>, has taken{' '}
|
|
||||||
<strong>21 or more Commander Damage</strong> or has{' '}
|
|
||||||
<strong>10 or more poison counters</strong>, a button with a skull
|
|
||||||
will appear on that player's card.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Tap on the button to mark that player as lost, dimming their player
|
|
||||||
card.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
textAlign: 'center',
|
|
||||||
marginTop: '1rem',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Visit my
|
|
||||||
<a
|
|
||||||
href="https://github.com/Vikeo/LifeTrinket"
|
|
||||||
target="_blank"
|
|
||||||
style={{
|
|
||||||
textDecoration: 'none',
|
|
||||||
color: theme.palette.primary.light,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
GitHub{' '}
|
|
||||||
</a>
|
|
||||||
for more info about this web app.
|
|
||||||
</div>
|
|
||||||
</ModalWrapper>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
43
src/Components/Misc/InstallPWAButton.tsx
Normal file
43
src/Components/Misc/InstallPWAButton.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { BeforeInstallPromptEvent } from '../../global';
|
||||||
|
import { useAnalytics } from '../../Hooks/useAnalytics';
|
||||||
|
|
||||||
|
export const InstallPWAButton = () => {
|
||||||
|
const [promptInstall, setPromptInstall] =
|
||||||
|
useState<BeforeInstallPromptEvent | null>(null);
|
||||||
|
|
||||||
|
const analytics = useAnalytics();
|
||||||
|
|
||||||
|
const handler = (e: BeforeInstallPromptEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setPromptInstall(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener('beforeinstallprompt', handler);
|
||||||
|
|
||||||
|
return () => window.removeEventListener('transitionend', handler);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!promptInstall) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="mt-1 mb-1 bg-primary-main px-3 py-1 rounded-md duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark font-bold"
|
||||||
|
aria-label="Install app"
|
||||||
|
title="Install app"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!promptInstall) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
analytics.trackEvent('install_pwa_prompt_shown');
|
||||||
|
promptInstall.prompt();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Install as a PWA
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
67
src/Components/Misc/MonarchCrown.tsx
Normal file
67
src/Components/Misc/MonarchCrown.tsx
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { usePlayers } from '../../Hooks/usePlayers';
|
||||||
|
import { Monarch } from '../../Icons/generated';
|
||||||
|
import { Player, Rotation } from '../../Types/Player';
|
||||||
|
import { IconCheckbox } from './IconCheckbox';
|
||||||
|
|
||||||
|
export const MonarchCrown = ({ player }: { player: Player }) => {
|
||||||
|
const { players, setPlayers } = usePlayers();
|
||||||
|
|
||||||
|
const iconSize =
|
||||||
|
player.settings.rotation === Rotation.SideFlipped ||
|
||||||
|
player.settings.rotation === Rotation.Side
|
||||||
|
? '5vmax'
|
||||||
|
: '10vmin';
|
||||||
|
|
||||||
|
const rotationIsSide =
|
||||||
|
player.settings.rotation === Rotation.SideFlipped ||
|
||||||
|
player.settings.rotation === Rotation.Side;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-rotation-is-side={rotationIsSide}
|
||||||
|
className="absolute w-full h-full flex items-start justify-center pointer-events-none z-[1]
|
||||||
|
data-[rotation-is-side=true]:justify-start data-[rotation-is-side=true]:items-center
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-rotation-is-side={rotationIsSide}
|
||||||
|
className="data-[rotation-is-side=true]:-rotate-90"
|
||||||
|
>
|
||||||
|
<IconCheckbox
|
||||||
|
className="pointer-events-all"
|
||||||
|
name="useMonarch"
|
||||||
|
checked={player.isMonarch}
|
||||||
|
icon={<Monarch size={iconSize} color={player.color} stroke="white" />}
|
||||||
|
checkedIcon={
|
||||||
|
<div>
|
||||||
|
<Monarch
|
||||||
|
size={iconSize}
|
||||||
|
stroke="white"
|
||||||
|
className="absolute blur z-[-1] text-icons-gold"
|
||||||
|
/>
|
||||||
|
<Monarch
|
||||||
|
size={iconSize}
|
||||||
|
stroke="white"
|
||||||
|
className="text-icons-gold"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
onChange={(e) => {
|
||||||
|
const updatedPlayer = { ...player, isMonarch: e.target.checked };
|
||||||
|
|
||||||
|
const updatedPlayers = players.map((p) => {
|
||||||
|
if (p.index === player.index) {
|
||||||
|
return updatedPlayer;
|
||||||
|
}
|
||||||
|
return { ...p, isMonarch: false };
|
||||||
|
});
|
||||||
|
|
||||||
|
setPlayers(updatedPlayers);
|
||||||
|
}}
|
||||||
|
aria-checked={player.isMonarch}
|
||||||
|
aria-label="Monarch"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,58 +1,27 @@
|
|||||||
import styled, { css } from 'styled-components';
|
|
||||||
import { theme } from '../../Data/theme';
|
|
||||||
import { Rotation } from '../../Types/Player';
|
import { Rotation } from '../../Types/Player';
|
||||||
|
|
||||||
const Container = styled.div`
|
import { twc } from 'react-twc';
|
||||||
display: flex;
|
//TODO Create provider for this
|
||||||
position: relative;
|
import { baseColors } from './../../../tailwind.config';
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CenteredText = styled.div<{
|
const Container = twc.div`
|
||||||
strokeWidth?: string;
|
flex
|
||||||
strokeColor?: string;
|
relative
|
||||||
fillColor?: string;
|
w-full
|
||||||
fontSize?: string;
|
h-full
|
||||||
fontWeight?: string;
|
items-center
|
||||||
$rotation?: Rotation;
|
justify-center
|
||||||
}>`
|
`;
|
||||||
position: absolute;
|
|
||||||
font-weight: ${(props) => props.fontWeight || ''};
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
-moz-user-select: -moz-none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
|
|
||||||
color: ${(props) => props.fillColor || theme.palette.common.black};
|
const CenteredText = twc.div`absolute select-none text-common-black text-[6vmin] stroke-common-white
|
||||||
font-size: ${(props) => props.fontSize || '6vmin'};
|
webkit-user-select-none tabular-nums`;
|
||||||
-webkit-text-stroke: ${(props) => props.strokeWidth || '1vmin'}${(props) => props.strokeColor || theme.palette.common.white};
|
|
||||||
-webkit-text-fill-color: ${(props) =>
|
|
||||||
props.fillColor || theme.palette.common.black};
|
|
||||||
|
|
||||||
${(props) => {
|
const CenteredTextOutline = twc.span`
|
||||||
if (
|
absolute
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
left-0
|
||||||
props.$rotation === Rotation.Side
|
stroke-none
|
||||||
) {
|
pointer-events-none
|
||||||
return css`
|
`;
|
||||||
rotate: 270deg;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CenteredTextOutline = styled.span`
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
-webkit-text-stroke: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type OutlinedTextProps = {
|
type OutlinedTextProps = {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@@ -73,18 +42,33 @@ export const OutlinedText: React.FC<OutlinedTextProps> = ({
|
|||||||
fillColor,
|
fillColor,
|
||||||
rotation,
|
rotation,
|
||||||
}) => {
|
}) => {
|
||||||
|
const calcRotation =
|
||||||
|
rotation === Rotation.Side
|
||||||
|
? rotation - 180
|
||||||
|
: rotation === Rotation.SideFlipped
|
||||||
|
? rotation
|
||||||
|
: 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<CenteredText
|
<CenteredText
|
||||||
fontSize={fontSize}
|
style={{
|
||||||
fontWeight={fontWeight}
|
fontSize,
|
||||||
strokeWidth={strokeWidth}
|
fontWeight,
|
||||||
strokeColor={strokeColor}
|
strokeWidth: strokeWidth || '1vmin',
|
||||||
fillColor={fillColor}
|
color: fillColor || baseColors.common.black,
|
||||||
$rotation={rotation}
|
WebkitTextStroke: `${strokeWidth || '1vmin'} ${
|
||||||
|
strokeColor || baseColors.common.white
|
||||||
|
}`,
|
||||||
|
WebkitTextFillColor:
|
||||||
|
fillColor || baseColors.common.black,
|
||||||
|
rotate: `${calcRotation}deg`,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<CenteredTextOutline aria-hidden>{children}</CenteredTextOutline>
|
<CenteredTextOutline aria-hidden style={{ WebkitTextStroke: 0 }}>
|
||||||
|
{children}
|
||||||
|
</CenteredTextOutline>
|
||||||
</CenteredText>
|
</CenteredText>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,13 +1,3 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
import { Spacer } from './Spacer';
|
|
||||||
|
|
||||||
const SeparatorContainer = styled.div<{ width?: string; height?: string }>`
|
|
||||||
width: ${(props) => props.width};
|
|
||||||
height: ${(props) => props.height};
|
|
||||||
background-color: #00000025;
|
|
||||||
border-radius: 50px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const Separator = ({
|
export const Separator = ({
|
||||||
width = '100%',
|
width = '100%',
|
||||||
height = '100%',
|
height = '100%',
|
||||||
@@ -16,10 +6,9 @@ export const Separator = ({
|
|||||||
height?: string;
|
height?: string;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
<Spacer height="0.5rem" />
|
className={`bg-common-white bg-opacity-30 rounded-full mt-2 mb-2`}
|
||||||
<SeparatorContainer width={width} height={height} />
|
style={{ width, height }}
|
||||||
<Spacer height="0.5rem" />
|
/>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,133 +0,0 @@
|
|||||||
import { Button, FormLabel, Modal, Switch } from '@mui/material';
|
|
||||||
import { ModalWrapper } from './InfoModal';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
|
||||||
import { theme } from '../../Data/theme';
|
|
||||||
import { Separator } from './Separator';
|
|
||||||
import { Paragraph } from './TextComponents';
|
|
||||||
|
|
||||||
const SettingContainer = styled.div`
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ToggleContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Container = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Description = styled.p`
|
|
||||||
margin-top: -0.25rem;
|
|
||||||
margin-right: 3.5rem;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
text-align: left;
|
|
||||||
color: ${theme.palette.text.secondary};
|
|
||||||
`;
|
|
||||||
|
|
||||||
type SettingsModalProps = {
|
|
||||||
isOpen: boolean;
|
|
||||||
closeModal: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SettingsModal = ({ isOpen, closeModal }: SettingsModalProps) => {
|
|
||||||
const { settings, setSettings, isPWA } = useGlobalSettings();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal open={isOpen} onClose={closeModal}>
|
|
||||||
<ModalWrapper>
|
|
||||||
<Container>
|
|
||||||
<h2 style={{ textAlign: 'center' }}>⚙️ Settings ⚙️</h2>
|
|
||||||
<SettingContainer>
|
|
||||||
<ToggleContainer>
|
|
||||||
<FormLabel>Show Start Player</FormLabel>
|
|
||||||
<Switch
|
|
||||||
checked={settings.showStartingPlayer}
|
|
||||||
onChange={() => {
|
|
||||||
setSettings({
|
|
||||||
...settings,
|
|
||||||
showStartingPlayer: !settings.showStartingPlayer,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ToggleContainer>
|
|
||||||
<Description>
|
|
||||||
On start or reset of game, will pick a random player who will
|
|
||||||
start first if this is enabled.
|
|
||||||
</Description>
|
|
||||||
</SettingContainer>
|
|
||||||
<SettingContainer>
|
|
||||||
<ToggleContainer>
|
|
||||||
<FormLabel>Keep Awake</FormLabel>
|
|
||||||
<Switch
|
|
||||||
checked={settings.keepAwake}
|
|
||||||
onChange={() => {
|
|
||||||
setSettings({ ...settings, keepAwake: !settings.keepAwake });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ToggleContainer>
|
|
||||||
<Description>
|
|
||||||
Will prevent device from going to sleep while this app is open if
|
|
||||||
this is enabled.
|
|
||||||
</Description>
|
|
||||||
</SettingContainer>
|
|
||||||
<SettingContainer>
|
|
||||||
<ToggleContainer>
|
|
||||||
<FormLabel>Go fullscreen on start (Android only)</FormLabel>
|
|
||||||
<Switch
|
|
||||||
checked={settings.goFullscreenOnStart}
|
|
||||||
onChange={() => {
|
|
||||||
setSettings({
|
|
||||||
...settings,
|
|
||||||
goFullscreenOnStart: !settings.goFullscreenOnStart,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ToggleContainer>
|
|
||||||
<Description>
|
|
||||||
Will enter fullscreen mode when starting a game if this is
|
|
||||||
enabled.
|
|
||||||
</Description>
|
|
||||||
</SettingContainer>
|
|
||||||
{!isPWA && (
|
|
||||||
<>
|
|
||||||
<Separator height="2px" />
|
|
||||||
<SettingContainer>
|
|
||||||
<ToggleContainer>
|
|
||||||
<Paragraph>
|
|
||||||
<b>Tip:</b> You can{' '}
|
|
||||||
<b>add this webapp to your home page on iOS</b> or{' '}
|
|
||||||
<b>install it on Android</b> to have it act just like a
|
|
||||||
normal app!
|
|
||||||
</Paragraph>
|
|
||||||
</ToggleContainer>
|
|
||||||
<Description>
|
|
||||||
If you do, this app will work offline and the toolbar will be
|
|
||||||
automatically hidden.
|
|
||||||
</Description>
|
|
||||||
</SettingContainer>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Separator height="2px" />
|
|
||||||
<SettingContainer>
|
|
||||||
<Paragraph>Version: 0.4.0</Paragraph>
|
|
||||||
</SettingContainer>
|
|
||||||
<Separator height="2px" />
|
|
||||||
|
|
||||||
<Button variant="contained" onClick={closeModal}>
|
|
||||||
Save and Close
|
|
||||||
</Button>
|
|
||||||
</Container>
|
|
||||||
</ModalWrapper>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
export const Spacer = styled.div<{ width?: string; height?: string }>`
|
|
||||||
width: ${(props) => props.width};
|
|
||||||
height: ${(props) => props.height};
|
|
||||||
`;
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
import { Button, Drawer } from '@mui/material';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { theme } from '../../Data/theme';
|
|
||||||
import { BuyMeCoffee, KoFi } from '../../Icons/generated/Support';
|
|
||||||
import { Paragraph } from './TextComponents';
|
|
||||||
import LittleGuy from '../../Icons/generated/LittleGuy';
|
|
||||||
import { useAnalytics } from '../../Hooks/useAnalytics';
|
|
||||||
|
|
||||||
// import { ButtonBase } from '@mui/material';
|
|
||||||
|
|
||||||
const SupportContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 1rem;
|
|
||||||
margin: 16px 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SupportButton = styled.button`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border: none;
|
|
||||||
background-color: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
background-color: ${theme.palette.primary.main};
|
|
||||||
border-radius: 4px;
|
|
||||||
margin: 0 1rem;
|
|
||||||
padding: 0 1rem;
|
|
||||||
transition: background-color 0.2s ease-in-out;
|
|
||||||
box-shadow: 1px 2px 4px 0px rgba(0, 0, 0, 0.3);
|
|
||||||
&:hover {
|
|
||||||
background-color: ${theme.palette.primary.dark};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const SupportMe = () => {
|
|
||||||
const analytics = useAnalytics();
|
|
||||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
|
||||||
|
|
||||||
const handleOpenBuyMeCoffee = () => {
|
|
||||||
analytics.trackEvent('click_bmc');
|
|
||||||
window.open('https://www.buymeacoffee.com/vikeo');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenKoFi = () => {
|
|
||||||
analytics.trackEvent('click_kofi');
|
|
||||||
window.open('https://ko-fi.com/vikeo');
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleDrawer =
|
|
||||||
(open: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => {
|
|
||||||
analytics.trackEvent('toggle_support_drawer');
|
|
||||||
|
|
||||||
if (
|
|
||||||
event.type === 'keydown' &&
|
|
||||||
((event as React.KeyboardEvent).key === 'Tab' ||
|
|
||||||
(event as React.KeyboardEvent).key === 'Shift')
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsDrawerOpen(open);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
onClick={toggleDrawer(true)}
|
|
||||||
size="small"
|
|
||||||
variant="contained"
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: '1rem',
|
|
||||||
right: '1rem',
|
|
||||||
fontSize: '0.5rem',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Nourish <br /> this guy {'->'}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<LittleGuy
|
|
||||||
height={'4rem'}
|
|
||||||
width={'2.5rem'}
|
|
||||||
style={{
|
|
||||||
pointerEvents: 'none',
|
|
||||||
position: 'absolute',
|
|
||||||
top: '2.5rem',
|
|
||||||
right: '0',
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Drawer
|
|
||||||
anchor={'right'}
|
|
||||||
open={isDrawerOpen}
|
|
||||||
onClose={toggleDrawer(false)}
|
|
||||||
variant="temporary"
|
|
||||||
>
|
|
||||||
<SupportContainer>
|
|
||||||
<SupportButton onClick={handleOpenBuyMeCoffee}>
|
|
||||||
<BuyMeCoffee
|
|
||||||
height={'1.5rem'}
|
|
||||||
width={'1.5rem'}
|
|
||||||
style={{ marginRight: '0.5rem' }}
|
|
||||||
/>
|
|
||||||
<Paragraph style={{ fontSize: '0.7rem' }}>Buy him a tea</Paragraph>
|
|
||||||
</SupportButton>
|
|
||||||
<SupportButton onClick={handleOpenKoFi}>
|
|
||||||
<KoFi
|
|
||||||
height={'1.5rem'}
|
|
||||||
width={'1.5rem'}
|
|
||||||
style={{ marginRight: '0.5rem' }}
|
|
||||||
/>
|
|
||||||
<Paragraph style={{ fontSize: '0.7rem' }}>
|
|
||||||
Buy him a ko-fi
|
|
||||||
</Paragraph>
|
|
||||||
</SupportButton>
|
|
||||||
</SupportContainer>
|
|
||||||
</Drawer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,11 +1,5 @@
|
|||||||
import styled from 'styled-components';
|
import { twc } from 'react-twc';
|
||||||
import { theme } from '../../Data/theme';
|
|
||||||
|
|
||||||
export const Paragraph = styled.p`
|
export const Paragraph = twc.p`text-text-primary`;
|
||||||
color: ${theme.palette.text.primary};
|
|
||||||
`;
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-refresh/only-export-components
|
export const H1 = twc.h1`text-text-primary;`;
|
||||||
export const H1 = styled.h1`
|
|
||||||
color: ${theme.palette.text.primary};
|
|
||||||
`;
|
|
||||||
|
|||||||
29
src/Components/Misc/ToggleButton.tsx
Normal file
29
src/Components/Misc/ToggleButton.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { LabelText } from '../Views/StartMenu/StartMenu';
|
||||||
|
|
||||||
|
export const ToggleButton = ({
|
||||||
|
label,
|
||||||
|
checked,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
label?: string;
|
||||||
|
checked: boolean;
|
||||||
|
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
{label && <LabelText>{label}</LabelText>}
|
||||||
|
|
||||||
|
<label className="inline-flex items-center cursor-pointer relative h-6 w-10">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
value=""
|
||||||
|
checked={checked}
|
||||||
|
onChange={onChange}
|
||||||
|
className="sr-only peer"
|
||||||
|
/>
|
||||||
|
<div className="relative mx-1 w-10 h-[0.875rem] bg-gray-900 rounded-full peer peer-checked:bg-primary-dark" />
|
||||||
|
<div className="absolute peer-checked:translate-x-full rtl:peer-checked:-translate-x-full bg-secondary-main peer-checked:bg-primary-main rounded-full h-5 w-5 transition-all" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import LifeCounter from '../LifeCounter/LifeCounter';
|
|
||||||
import { Player as PlayerType } from '../../Types/Player';
|
|
||||||
|
|
||||||
const getGridArea = (player: PlayerType) => {
|
|
||||||
switch (player.index) {
|
|
||||||
case 0:
|
|
||||||
return 'grid-in-player0';
|
|
||||||
case 1:
|
|
||||||
return 'grid-in-player1';
|
|
||||||
case 2:
|
|
||||||
return 'grid-in-player2';
|
|
||||||
case 3:
|
|
||||||
return 'grid-in-player3';
|
|
||||||
case 4:
|
|
||||||
return 'grid-in-player4';
|
|
||||||
case 5:
|
|
||||||
return 'grid-in-player5';
|
|
||||||
default:
|
|
||||||
throw new Error('Invalid player index');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Player = (players: PlayerType[], gridClasses: string) => {
|
|
||||||
return (
|
|
||||||
<div className="w-full h-full bg-black">
|
|
||||||
<div className={`grid w-full h-full gap-1 box-border ${gridClasses} `}>
|
|
||||||
{players.map((player) => {
|
|
||||||
const gridArea = getGridArea(player);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={player.index}
|
|
||||||
className={`flex justify-center items-center align-middle ${gridArea}`}
|
|
||||||
>
|
|
||||||
<LifeCounter
|
|
||||||
player={player}
|
|
||||||
opponents={players.filter(
|
|
||||||
(opponent) => opponent.index !== player.index
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,455 +0,0 @@
|
|||||||
import { Button, Checkbox } from '@mui/material';
|
|
||||||
import styled, { css } from 'styled-components';
|
|
||||||
import { Player, Rotation } from '../../Types/Player';
|
|
||||||
import { theme } from '../../Data/theme';
|
|
||||||
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
|
||||||
import { usePlayers } from '../../Hooks/usePlayers';
|
|
||||||
import {
|
|
||||||
PartnerTax,
|
|
||||||
Poison,
|
|
||||||
Energy,
|
|
||||||
Experience,
|
|
||||||
Exit,
|
|
||||||
FullscreenOff,
|
|
||||||
FullscreenOn,
|
|
||||||
Cross,
|
|
||||||
ResetGame,
|
|
||||||
} from '../../Icons/generated';
|
|
||||||
import { useRef } from 'react';
|
|
||||||
import { Spacer } from '../Misc/Spacer';
|
|
||||||
import { useSafeRotate } from '../../Hooks/useSafeRotate';
|
|
||||||
|
|
||||||
const SettingsContainer = styled.div<{
|
|
||||||
$rotation: Rotation;
|
|
||||||
}>`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
${(props) => {
|
|
||||||
if (
|
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
|
||||||
props.$rotation === Rotation.Side
|
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
${(props) => {
|
|
||||||
if (props.$rotation === Rotation.Side) {
|
|
||||||
return css`
|
|
||||||
rotate: ${props.$rotation - 180}deg;
|
|
||||||
`;
|
|
||||||
} else if (props.$rotation === Rotation.SideFlipped) {
|
|
||||||
return css`
|
|
||||||
rotate: ${props.$rotation - 180}deg;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const BetterRowContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-grow: 1;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
justify-content: end;
|
|
||||||
align-items: stretch;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const TogglesSection = styled.div`
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 0.5rem;
|
|
||||||
justify-content: space-evenly;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ButtonsSections = styled.div`
|
|
||||||
display: flex;
|
|
||||||
max-width: 100%;
|
|
||||||
gap: 1rem;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 3% 3%;
|
|
||||||
align-items: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ColorPicker = styled.input`
|
|
||||||
position: absolute;
|
|
||||||
top: 5%;
|
|
||||||
left: 5%;
|
|
||||||
height: 8vmax;
|
|
||||||
width: 8vmax;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: transparent;
|
|
||||||
user-select: none;
|
|
||||||
color: #ffffff;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CheckboxContainer = styled.div<{ $rotation: Rotation }>`
|
|
||||||
${(props) => {
|
|
||||||
if (
|
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
|
||||||
props.$rotation === Rotation.Side
|
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
/* rotate: ${props.$rotation - 180}deg; */
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const PlayerMenuWrapper = styled.div<{
|
|
||||||
$rotation: Rotation;
|
|
||||||
}>`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(20, 20, 20, 0.9);
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 2;
|
|
||||||
${(props) => {
|
|
||||||
if (
|
|
||||||
props.$rotation === Rotation.SideFlipped ||
|
|
||||||
props.$rotation === Rotation.Side
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return css`
|
|
||||||
rotate: ${props.$rotation}deg;
|
|
||||||
`;
|
|
||||||
}};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CloseButton = styled.div<{
|
|
||||||
$rotation: Rotation;
|
|
||||||
}>`
|
|
||||||
position: absolute;
|
|
||||||
top: 15%;
|
|
||||||
right: 5%;
|
|
||||||
z-index: 9999;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: transparent;
|
|
||||||
|
|
||||||
${(props) => {
|
|
||||||
if (props.$rotation === Rotation.Side) {
|
|
||||||
return css`
|
|
||||||
rotate: ${props.$rotation - 180}deg;
|
|
||||||
top: 5%;
|
|
||||||
right: auto;
|
|
||||||
left: 5%;
|
|
||||||
`;
|
|
||||||
} else if (props.$rotation === Rotation.SideFlipped) {
|
|
||||||
return css`
|
|
||||||
rotate: ${props.$rotation - 180}deg;
|
|
||||||
top: auto;
|
|
||||||
left: auto;
|
|
||||||
bottom: 5%;
|
|
||||||
right: 5%;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
type PlayerMenuProps = {
|
|
||||||
player: Player;
|
|
||||||
setShowPlayerMenu: (showPlayerMenu: boolean) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const PlayerMenu = ({ player, setShowPlayerMenu }: PlayerMenuProps) => {
|
|
||||||
const settingsContainerRef = useRef<HTMLDivElement | null>(null);
|
|
||||||
const dialogRef = useRef<HTMLDialogElement | null>(null);
|
|
||||||
|
|
||||||
const { isSide } = useSafeRotate({
|
|
||||||
rotation: player.settings.rotation,
|
|
||||||
containerRef: settingsContainerRef,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleOnClick = () => {
|
|
||||||
setShowPlayerMenu(false);
|
|
||||||
};
|
|
||||||
const { fullscreen, wakeLock, goToStart } = useGlobalSettings();
|
|
||||||
const { updatePlayer, resetCurrentGame } = usePlayers();
|
|
||||||
|
|
||||||
const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const updatedPlayer = { ...player, color: event.target.value };
|
|
||||||
updatePlayer(updatedPlayer);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSettingsChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const { name, checked } = event.target;
|
|
||||||
const updatedSettings = { ...player.settings, [name]: checked };
|
|
||||||
const updatedPlayer = { ...player, settings: updatedSettings };
|
|
||||||
updatePlayer(updatedPlayer);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleResetGame = () => {
|
|
||||||
resetCurrentGame();
|
|
||||||
setShowPlayerMenu(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleFullscreen = () => {
|
|
||||||
if (fullscreen.isFullscreen) {
|
|
||||||
fullscreen.disableFullscreen();
|
|
||||||
} else {
|
|
||||||
fullscreen.enableFullscreen();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const buttonFontSize = isSide ? '1.5vmax' : '3vmin';
|
|
||||||
const iconSize = isSide ? '6vmin' : '3vmax';
|
|
||||||
const extraCountersSize = isSide ? '8vmin' : '4vmax';
|
|
||||||
const closeButtonSize = isSide ? '6vmin' : '3vmax';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PlayerMenuWrapper $rotation={player.settings.rotation}>
|
|
||||||
<CloseButton $rotation={player.settings.rotation}>
|
|
||||||
<Button
|
|
||||||
variant="text"
|
|
||||||
onClick={handleOnClick}
|
|
||||||
style={{
|
|
||||||
margin: 0,
|
|
||||||
padding: 0,
|
|
||||||
height: closeButtonSize,
|
|
||||||
width: closeButtonSize,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Cross size={closeButtonSize} />
|
|
||||||
</Button>
|
|
||||||
</CloseButton>
|
|
||||||
<SettingsContainer
|
|
||||||
$rotation={player.settings.rotation}
|
|
||||||
ref={settingsContainerRef}
|
|
||||||
>
|
|
||||||
<ColorPicker
|
|
||||||
type="color"
|
|
||||||
value={player.color}
|
|
||||||
onChange={handleColorChange}
|
|
||||||
role="button"
|
|
||||||
aria-label="Color picker"
|
|
||||||
/>
|
|
||||||
<BetterRowContainer>
|
|
||||||
<TogglesSection>
|
|
||||||
{player.settings.useCommanderDamage && (
|
|
||||||
<CheckboxContainer $rotation={player.settings.rotation}>
|
|
||||||
<Checkbox
|
|
||||||
name="usePartner"
|
|
||||||
checked={player.settings.usePartner}
|
|
||||||
icon={
|
|
||||||
<PartnerTax
|
|
||||||
size={extraCountersSize}
|
|
||||||
color="black"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="30"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<PartnerTax
|
|
||||||
size={extraCountersSize}
|
|
||||||
color={player.color}
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="30"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onChange={handleSettingsChange}
|
|
||||||
role="checkbox"
|
|
||||||
aria-checked={player.settings.usePartner}
|
|
||||||
aria-label="Partner"
|
|
||||||
/>
|
|
||||||
</CheckboxContainer>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<CheckboxContainer $rotation={player.settings.rotation}>
|
|
||||||
<Checkbox
|
|
||||||
name="usePoison"
|
|
||||||
checked={player.settings.usePoison}
|
|
||||||
icon={
|
|
||||||
<Poison
|
|
||||||
size={extraCountersSize}
|
|
||||||
color="black"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="30"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<Poison
|
|
||||||
size={extraCountersSize}
|
|
||||||
color={player.color}
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="30"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onChange={handleSettingsChange}
|
|
||||||
role="checkbox"
|
|
||||||
aria-checked={player.settings.usePoison}
|
|
||||||
aria-label="Poison"
|
|
||||||
/>
|
|
||||||
</CheckboxContainer>
|
|
||||||
|
|
||||||
<CheckboxContainer $rotation={player.settings.rotation}>
|
|
||||||
<Checkbox
|
|
||||||
name="useEnergy"
|
|
||||||
checked={player.settings.useEnergy}
|
|
||||||
icon={
|
|
||||||
<Energy
|
|
||||||
size={extraCountersSize}
|
|
||||||
color="black"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="15"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<Energy
|
|
||||||
size={extraCountersSize}
|
|
||||||
color={player.color}
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="15"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onChange={handleSettingsChange}
|
|
||||||
role="checkbox"
|
|
||||||
aria-checked={player.settings.useEnergy}
|
|
||||||
aria-label="Energy"
|
|
||||||
/>
|
|
||||||
</CheckboxContainer>
|
|
||||||
|
|
||||||
<CheckboxContainer $rotation={player.settings.rotation}>
|
|
||||||
<Checkbox
|
|
||||||
name="useExperience"
|
|
||||||
checked={player.settings.useExperience}
|
|
||||||
icon={
|
|
||||||
<Experience
|
|
||||||
size={extraCountersSize}
|
|
||||||
color="black"
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="15"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<Experience
|
|
||||||
size={extraCountersSize}
|
|
||||||
color={player.color}
|
|
||||||
stroke="white"
|
|
||||||
strokeWidth="15"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onChange={handleSettingsChange}
|
|
||||||
role="checkbox"
|
|
||||||
aria-checked={player.settings.useExperience}
|
|
||||||
aria-label="Experience"
|
|
||||||
/>
|
|
||||||
</CheckboxContainer>
|
|
||||||
</TogglesSection>
|
|
||||||
<Spacer height="1rem" />
|
|
||||||
<ButtonsSections>
|
|
||||||
<Button
|
|
||||||
variant="text"
|
|
||||||
style={{
|
|
||||||
cursor: 'pointer',
|
|
||||||
userSelect: 'none',
|
|
||||||
}}
|
|
||||||
onClick={goToStart}
|
|
||||||
aria-label="Back to start"
|
|
||||||
>
|
|
||||||
<Exit size={iconSize} style={{ rotate: '180deg' }} />
|
|
||||||
</Button>
|
|
||||||
<CheckboxContainer $rotation={player.settings.rotation}>
|
|
||||||
<Checkbox
|
|
||||||
name="fullscreen"
|
|
||||||
checked={document.fullscreenElement ? true : false}
|
|
||||||
icon={
|
|
||||||
<FullscreenOff
|
|
||||||
size={iconSize}
|
|
||||||
color={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={<FullscreenOn size={iconSize} />}
|
|
||||||
onChange={toggleFullscreen}
|
|
||||||
role="checkbox"
|
|
||||||
aria-checked={document.fullscreenElement ? true : false}
|
|
||||||
aria-label="Fullscreen"
|
|
||||||
/>
|
|
||||||
</CheckboxContainer>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
variant={wakeLock.active ? 'contained' : 'outlined'}
|
|
||||||
style={{
|
|
||||||
cursor: 'pointer',
|
|
||||||
userSelect: 'none',
|
|
||||||
fontSize: buttonFontSize,
|
|
||||||
padding: '0 4px 0 4px',
|
|
||||||
}}
|
|
||||||
onClick={wakeLock.toggleWakeLock}
|
|
||||||
role="checkbox"
|
|
||||||
aria-checked={wakeLock.active}
|
|
||||||
aria-label="Keep awake"
|
|
||||||
>
|
|
||||||
Keep Awake
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style={{
|
|
||||||
cursor: 'pointer',
|
|
||||||
userSelect: 'none',
|
|
||||||
fontSize: buttonFontSize,
|
|
||||||
padding: '4px',
|
|
||||||
}}
|
|
||||||
onClick={() => dialogRef.current?.show()}
|
|
||||||
role="checkbox"
|
|
||||||
aria-checked={wakeLock.active}
|
|
||||||
aria-label="Reset Game"
|
|
||||||
>
|
|
||||||
<ResetGame size={iconSize} />
|
|
||||||
</Button>
|
|
||||||
</ButtonsSections>
|
|
||||||
</BetterRowContainer>
|
|
||||||
<dialog
|
|
||||||
ref={dialogRef}
|
|
||||||
style={{
|
|
||||||
zIndex: 9999,
|
|
||||||
background: theme.palette.background.default,
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
borderRadius: '1rem',
|
|
||||||
border: 'none',
|
|
||||||
position: 'absolute',
|
|
||||||
top: '10%',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<h1>Reset Game?</h1>
|
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-evenly' }}>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={() => dialogRef.current?.close()}
|
|
||||||
>
|
|
||||||
No
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={() => {
|
|
||||||
handleResetGame();
|
|
||||||
dialogRef.current?.close();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Yes
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</dialog>
|
|
||||||
</SettingsContainer>
|
|
||||||
</PlayerMenuWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PlayerMenu;
|
|
||||||
642
src/Components/Players/PlayerMenu.tsx
Normal file
642
src/Components/Players/PlayerMenu.tsx
Normal file
@@ -0,0 +1,642 @@
|
|||||||
|
import { useRef } from 'react';
|
||||||
|
import { twc } from 'react-twc';
|
||||||
|
import { useAnalytics } from '../../Hooks/useAnalytics';
|
||||||
|
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
||||||
|
import { usePlayers } from '../../Hooks/usePlayers';
|
||||||
|
import { useSafeRotate } from '../../Hooks/useSafeRotate';
|
||||||
|
import {
|
||||||
|
Close,
|
||||||
|
Energy,
|
||||||
|
Exit,
|
||||||
|
Experience,
|
||||||
|
FullscreenOff,
|
||||||
|
FullscreenOn,
|
||||||
|
Monarch,
|
||||||
|
NameTag,
|
||||||
|
PartnerTax,
|
||||||
|
Poison,
|
||||||
|
ResetGame,
|
||||||
|
Skull,
|
||||||
|
} from '../../Icons/generated';
|
||||||
|
import { Player, Rotation } from '../../Types/Player';
|
||||||
|
import { PreStartMode } from '../../Types/Settings';
|
||||||
|
import { RotationDivProps } from '../Buttons/CommanderDamage';
|
||||||
|
import { IconCheckbox } from '../Misc/IconCheckbox';
|
||||||
|
import { checkContrast } from '../../Utils/checkContrast';
|
||||||
|
|
||||||
|
const PlayerMenuWrapper = twc.div`
|
||||||
|
flex
|
||||||
|
flex-col
|
||||||
|
absolute
|
||||||
|
size-full
|
||||||
|
bg-background-settings
|
||||||
|
backdrop-blur-[3px]
|
||||||
|
items-center
|
||||||
|
justify-center
|
||||||
|
z-[2]
|
||||||
|
webkit-user-select-none
|
||||||
|
transition-all
|
||||||
|
`;
|
||||||
|
|
||||||
|
const BetterRowContainer = twc.div`
|
||||||
|
flex
|
||||||
|
flex-col
|
||||||
|
flex-grow
|
||||||
|
w-full
|
||||||
|
h-full
|
||||||
|
justify-between
|
||||||
|
items-stretch
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TogglesSection = twc.div`
|
||||||
|
flex
|
||||||
|
flex-row
|
||||||
|
flex-wrap
|
||||||
|
relative
|
||||||
|
h-full
|
||||||
|
justify-evenly
|
||||||
|
items-center
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ButtonsSections = twc.div`
|
||||||
|
flex
|
||||||
|
max-w-full
|
||||||
|
justify-evenly
|
||||||
|
items-center
|
||||||
|
flex-wrap
|
||||||
|
h-full
|
||||||
|
mt-0
|
||||||
|
px-2
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ColorPickerButton = twc.div`
|
||||||
|
h-[8vmax]
|
||||||
|
w-[8vmax]
|
||||||
|
relative
|
||||||
|
max-h-12
|
||||||
|
max-w-12
|
||||||
|
rounded-full
|
||||||
|
cursor-pointer
|
||||||
|
overflow-hidden
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SettingsContainer = twc.div<RotationDivProps>((props) => [
|
||||||
|
'flex flex-wrap h-full w-full overflow-y-scroll',
|
||||||
|
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||||
|
? 'flex-col'
|
||||||
|
: 'flex-row',
|
||||||
|
]);
|
||||||
|
|
||||||
|
type PlayerMenuProps = {
|
||||||
|
player: Player;
|
||||||
|
setShowPlayerMenu: (showPlayerMenu: boolean) => void;
|
||||||
|
isShown: boolean;
|
||||||
|
onForfeit?: () => void;
|
||||||
|
totalPlayers: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PlayerMenu = ({
|
||||||
|
player,
|
||||||
|
setShowPlayerMenu,
|
||||||
|
isShown,
|
||||||
|
onForfeit,
|
||||||
|
totalPlayers,
|
||||||
|
}: PlayerMenuProps) => {
|
||||||
|
const settingsContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const resetGameDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||||
|
const endGameDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||||
|
const forfeitGameDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||||
|
|
||||||
|
const { isSide } = useSafeRotate({
|
||||||
|
rotation: player.settings.rotation,
|
||||||
|
containerRef: settingsContainerRef,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
fullscreen,
|
||||||
|
wakeLock,
|
||||||
|
goToStart,
|
||||||
|
settings,
|
||||||
|
setSettings,
|
||||||
|
setPlaying,
|
||||||
|
setRandomizingPlayer,
|
||||||
|
saveCurrentGame,
|
||||||
|
initialGameSettings,
|
||||||
|
setPreStartCompleted,
|
||||||
|
gameScore,
|
||||||
|
} = useGlobalSettings();
|
||||||
|
|
||||||
|
const analytics = useAnalytics();
|
||||||
|
|
||||||
|
const { updatePlayer, resetCurrentGame, players } = usePlayers();
|
||||||
|
|
||||||
|
const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const iconTheme =
|
||||||
|
checkContrast(event.target.value, '#00000080') === 'Fail'
|
||||||
|
? 'light'
|
||||||
|
: 'dark';
|
||||||
|
|
||||||
|
updatePlayer({
|
||||||
|
...player,
|
||||||
|
color: event.target.value,
|
||||||
|
iconTheme,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSettingsChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const { name, checked } = event.target;
|
||||||
|
const updatedSettings = { ...player.settings, [name]: checked };
|
||||||
|
const updatedPlayer = { ...player, settings: updatedSettings };
|
||||||
|
|
||||||
|
updatePlayer(updatedPlayer);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResetGame = () => {
|
||||||
|
resetCurrentGame();
|
||||||
|
setShowPlayerMenu(false);
|
||||||
|
|
||||||
|
setPlaying(false);
|
||||||
|
|
||||||
|
if (settings.preStartMode === PreStartMode.RandomKing) {
|
||||||
|
setRandomizingPlayer(true);
|
||||||
|
setPreStartCompleted(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
analytics.trackEvent('reset_game');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGoToStart = () => {
|
||||||
|
saveCurrentGame({ players, initialGameSettings, gameScore });
|
||||||
|
goToStart();
|
||||||
|
setRandomizingPlayer(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdatePlayerName = () => {
|
||||||
|
const newName = prompt('Enter your name', player.name);
|
||||||
|
|
||||||
|
const updatedPlayer: Player = { ...player, name: '' };
|
||||||
|
if (!newName) {
|
||||||
|
updatePlayer(updatedPlayer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updatedPlayer.name = newName;
|
||||||
|
updatePlayer(updatedPlayer);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleFullscreen = () => {
|
||||||
|
if (fullscreen.isFullscreen) {
|
||||||
|
fullscreen.disableFullscreen();
|
||||||
|
} else {
|
||||||
|
fullscreen.enableFullscreen();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttonFontSize = isSide ? '1.5vmax' : '3vmin';
|
||||||
|
const iconSize = isSide ? '6vmin' : '3vmax';
|
||||||
|
const extraCountersSize = isSide ? '8vmin' : '4vmax';
|
||||||
|
|
||||||
|
const calcRotation =
|
||||||
|
player.settings.rotation === Rotation.Side
|
||||||
|
? `${player.settings.rotation - 180}deg`
|
||||||
|
: player.settings.rotation === Rotation.SideFlipped
|
||||||
|
? `${player.settings.rotation - 180}deg`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PlayerMenuWrapper
|
||||||
|
//TODO: Fix hacky solution to rotation for SideFlipped
|
||||||
|
style={{
|
||||||
|
rotate:
|
||||||
|
player.settings.rotation === Rotation.SideFlipped ? `180deg` : '',
|
||||||
|
translate: isShown ? '' : player.isSide ? `-100%` : `0 -100%`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SettingsContainer
|
||||||
|
$rotation={player.settings.rotation}
|
||||||
|
style={{
|
||||||
|
rotate: calcRotation,
|
||||||
|
}}
|
||||||
|
ref={settingsContainerRef}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
analytics.trackEvent('close_player_menu_button');
|
||||||
|
setShowPlayerMenu(false);
|
||||||
|
}}
|
||||||
|
className="flex absolute top-2 right-2 z-10"
|
||||||
|
>
|
||||||
|
<Close size={iconSize} className="text-primary-main" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<BetterRowContainer>
|
||||||
|
<TogglesSection>
|
||||||
|
<ColorPickerButton aria-label="Color picker">
|
||||||
|
<input
|
||||||
|
onChange={handleColorChange}
|
||||||
|
type="color"
|
||||||
|
className="size-[200%] absolute -left-2 -top-2"
|
||||||
|
value={player.color}
|
||||||
|
onClick={() => {
|
||||||
|
analytics.trackEvent('color_picker_opened', {
|
||||||
|
player: player.index,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ColorPickerButton>
|
||||||
|
{player.settings.useCommanderDamage && (
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<IconCheckbox
|
||||||
|
name="usePartner"
|
||||||
|
checked={player.settings.usePartner}
|
||||||
|
icon={
|
||||||
|
<PartnerTax
|
||||||
|
size={extraCountersSize}
|
||||||
|
color="black"
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth="1"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
checkedIcon={
|
||||||
|
<PartnerTax
|
||||||
|
size={extraCountersSize}
|
||||||
|
color={player.color}
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth="1"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onChange={(e) => {
|
||||||
|
analytics.trackEvent('toggle_partner', {
|
||||||
|
checked: e.target.checked,
|
||||||
|
});
|
||||||
|
handleSettingsChange(e);
|
||||||
|
}}
|
||||||
|
aria-checked={player.settings.usePartner}
|
||||||
|
aria-label="Partner"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<IconCheckbox
|
||||||
|
name="usePoison"
|
||||||
|
checked={player.settings.usePoison}
|
||||||
|
icon={
|
||||||
|
<Poison
|
||||||
|
size={extraCountersSize}
|
||||||
|
color="black"
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth="2"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
checkedIcon={
|
||||||
|
<Poison
|
||||||
|
size={extraCountersSize}
|
||||||
|
color={player.color}
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onChange={(e) => {
|
||||||
|
analytics.trackEvent('toggle_poison', {
|
||||||
|
checked: e.target.checked,
|
||||||
|
});
|
||||||
|
handleSettingsChange(e);
|
||||||
|
}}
|
||||||
|
aria-checked={player.settings.usePoison}
|
||||||
|
aria-label="Poison"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<IconCheckbox
|
||||||
|
name="useEnergy"
|
||||||
|
checked={player.settings.useEnergy}
|
||||||
|
icon={
|
||||||
|
<Energy
|
||||||
|
size={extraCountersSize}
|
||||||
|
color="black"
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth={2.2}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
checkedIcon={
|
||||||
|
<Energy
|
||||||
|
size={extraCountersSize}
|
||||||
|
color={player.color}
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth={2.2}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onChange={(e) => {
|
||||||
|
analytics.trackEvent('toggle_energy', {
|
||||||
|
checked: e.target.checked,
|
||||||
|
});
|
||||||
|
handleSettingsChange(e);
|
||||||
|
}}
|
||||||
|
aria-checked={player.settings.useEnergy}
|
||||||
|
aria-label="Energy"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<IconCheckbox
|
||||||
|
name="useExperience"
|
||||||
|
checked={player.settings.useExperience}
|
||||||
|
icon={
|
||||||
|
<Experience
|
||||||
|
size={extraCountersSize}
|
||||||
|
color="black"
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth={2.5}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
checkedIcon={
|
||||||
|
<Experience
|
||||||
|
size={extraCountersSize}
|
||||||
|
color={player.color}
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth={2.5}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onChange={(e) => {
|
||||||
|
analytics.trackEvent('toggle_experience', {
|
||||||
|
checked: e.target.checked,
|
||||||
|
});
|
||||||
|
handleSettingsChange(e);
|
||||||
|
}}
|
||||||
|
aria-checked={player.settings.useExperience}
|
||||||
|
aria-label="Experience"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<IconCheckbox
|
||||||
|
name="useMonarch"
|
||||||
|
checked={settings.useMonarch}
|
||||||
|
icon={
|
||||||
|
<Monarch
|
||||||
|
size={extraCountersSize}
|
||||||
|
color="black"
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth={2.5}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
checkedIcon={
|
||||||
|
<Monarch
|
||||||
|
size={extraCountersSize}
|
||||||
|
color={player.color}
|
||||||
|
stroke="white"
|
||||||
|
strokeWidth={2.5}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onChange={(e) => {
|
||||||
|
analytics.trackEvent('toggle_monarch', {
|
||||||
|
checked: e.target.checked,
|
||||||
|
});
|
||||||
|
setSettings({ ...settings, useMonarch: e.target.checked });
|
||||||
|
}}
|
||||||
|
aria-checked={settings.useMonarch}
|
||||||
|
aria-label="Monarch"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</TogglesSection>
|
||||||
|
<ButtonsSections>
|
||||||
|
<button
|
||||||
|
className="text-primary-main cursor-pointer webkit-user-select-none"
|
||||||
|
onClick={() => endGameDialogRef.current?.show()}
|
||||||
|
aria-label="Back to start"
|
||||||
|
>
|
||||||
|
<Exit size={iconSize} style={{ rotate: '180deg' }} />
|
||||||
|
</button>
|
||||||
|
{(!window.isIOS || window.isIPad) && (
|
||||||
|
<div
|
||||||
|
data-fullscreen={document.fullscreenElement ? true : false}
|
||||||
|
className="flex
|
||||||
|
data-[fullscreen=true]:bg-secondary-dark rounded-lg border border-transparent
|
||||||
|
data-[fullscreen=true]:border-primary-main"
|
||||||
|
>
|
||||||
|
<IconCheckbox
|
||||||
|
className="p-1"
|
||||||
|
name="fullscreen"
|
||||||
|
checked={document.fullscreenElement ? true : false}
|
||||||
|
icon={
|
||||||
|
<FullscreenOff
|
||||||
|
size={iconSize}
|
||||||
|
className="text-primary-main"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
checkedIcon={
|
||||||
|
<FullscreenOn
|
||||||
|
size={iconSize}
|
||||||
|
className="text-primary-main"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onChange={toggleFullscreen}
|
||||||
|
aria-checked={document.fullscreenElement ? true : false}
|
||||||
|
aria-label="Fullscreen"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
data-wake-lock-active={settings.keepAwake}
|
||||||
|
style={{
|
||||||
|
fontSize: buttonFontSize,
|
||||||
|
}}
|
||||||
|
className="text-primary-main px-1 webkit-user-select-none cursor-pointer
|
||||||
|
data-[wake-lock-active=true]:bg-secondary-dark rounded-lg border border-transparent
|
||||||
|
data-[wake-lock-active=true]:border-primary-main
|
||||||
|
"
|
||||||
|
onClick={() => {
|
||||||
|
wakeLock.toggleWakeLock();
|
||||||
|
}}
|
||||||
|
role="checkbox"
|
||||||
|
aria-checked={settings.keepAwake}
|
||||||
|
aria-label="Keep awake"
|
||||||
|
>
|
||||||
|
Keep Awake
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
style={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
userSelect: 'none',
|
||||||
|
fontSize: buttonFontSize,
|
||||||
|
padding: '2px',
|
||||||
|
}}
|
||||||
|
className="text-primary-main"
|
||||||
|
onClick={handleUpdatePlayerName}
|
||||||
|
role="name_tag"
|
||||||
|
aria-label="Name Tag"
|
||||||
|
>
|
||||||
|
<NameTag size={iconSize} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
style={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
userSelect: 'none',
|
||||||
|
fontSize: buttonFontSize,
|
||||||
|
padding: '2px',
|
||||||
|
}}
|
||||||
|
className="text-primary-main"
|
||||||
|
onClick={() => resetGameDialogRef.current?.show()}
|
||||||
|
role="checkbox"
|
||||||
|
aria-label="Reset Game"
|
||||||
|
>
|
||||||
|
<ResetGame size={iconSize} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
style={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
userSelect: 'none',
|
||||||
|
fontSize: buttonFontSize,
|
||||||
|
padding: '2px',
|
||||||
|
}}
|
||||||
|
className="text-red-500"
|
||||||
|
onClick={() => {
|
||||||
|
if (totalPlayers === 2) {
|
||||||
|
forfeitGameDialogRef.current?.show();
|
||||||
|
} else {
|
||||||
|
if (onForfeit) {
|
||||||
|
analytics.trackEvent('forfeit_game', {
|
||||||
|
player: player.index,
|
||||||
|
});
|
||||||
|
onForfeit();
|
||||||
|
setShowPlayerMenu(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
aria-label="Forfeit Game"
|
||||||
|
>
|
||||||
|
<Skull size={iconSize} />
|
||||||
|
</button>
|
||||||
|
</ButtonsSections>
|
||||||
|
</BetterRowContainer>
|
||||||
|
|
||||||
|
<dialog
|
||||||
|
ref={resetGameDialogRef}
|
||||||
|
className="z-[999] size-full bg-background-settings overflow-y-scroll"
|
||||||
|
onClick={() => resetGameDialogRef.current?.close()}
|
||||||
|
>
|
||||||
|
<div className="flex size-full items-center justify-center">
|
||||||
|
<div className="flex flex-col justify-center p-4 gap-2 bg-background-default rounded-xl border-none">
|
||||||
|
<h1
|
||||||
|
className="text-center text-text-primary"
|
||||||
|
style={{ fontSize: extraCountersSize }}
|
||||||
|
>
|
||||||
|
Reset Game?
|
||||||
|
</h1>
|
||||||
|
<div className="flex justify-evenly gap-2">
|
||||||
|
<button
|
||||||
|
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
|
||||||
|
style={{ fontSize: iconSize }}
|
||||||
|
onClick={() => resetGameDialogRef.current?.close()}
|
||||||
|
>
|
||||||
|
No
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
|
||||||
|
onClick={() => {
|
||||||
|
handleResetGame();
|
||||||
|
resetGameDialogRef.current?.close();
|
||||||
|
}}
|
||||||
|
style={{ fontSize: iconSize }}
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<dialog
|
||||||
|
ref={endGameDialogRef}
|
||||||
|
className="z-[999] size-full bg-background-settings overflow-y-scroll"
|
||||||
|
onClick={() => endGameDialogRef.current?.close()}
|
||||||
|
>
|
||||||
|
<div className="flex size-full items-center justify-center">
|
||||||
|
<div className="flex flex-col justify-center p-4 gap-2 bg-background-default rounded-xl border-none">
|
||||||
|
<h1
|
||||||
|
className="text-center text-text-primary"
|
||||||
|
style={{ fontSize: extraCountersSize }}
|
||||||
|
>
|
||||||
|
Go to start?
|
||||||
|
</h1>
|
||||||
|
<div
|
||||||
|
style={{ fontSize: iconSize }}
|
||||||
|
className="text-center text-text-primary"
|
||||||
|
>
|
||||||
|
(Game will be saved)
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-evenly gap-2">
|
||||||
|
<button
|
||||||
|
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
|
||||||
|
style={{ fontSize: iconSize }}
|
||||||
|
onClick={() => endGameDialogRef.current?.close()}
|
||||||
|
>
|
||||||
|
No
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
|
||||||
|
onClick={() => {
|
||||||
|
handleGoToStart();
|
||||||
|
endGameDialogRef.current?.close();
|
||||||
|
}}
|
||||||
|
style={{ fontSize: iconSize }}
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<dialog
|
||||||
|
ref={forfeitGameDialogRef}
|
||||||
|
className="z-[999] size-full bg-background-settings overflow-y-scroll"
|
||||||
|
onClick={() => forfeitGameDialogRef.current?.close()}
|
||||||
|
>
|
||||||
|
<div className="flex size-full items-center justify-center">
|
||||||
|
<div className="flex flex-col justify-center p-4 gap-2 bg-background-default rounded-xl border-none">
|
||||||
|
<h1
|
||||||
|
className="text-center text-text-primary"
|
||||||
|
style={{ fontSize: extraCountersSize }}
|
||||||
|
>
|
||||||
|
Forfeit Game?
|
||||||
|
</h1>
|
||||||
|
<div className="flex justify-evenly gap-2">
|
||||||
|
<button
|
||||||
|
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
|
||||||
|
style={{ fontSize: iconSize }}
|
||||||
|
onClick={() => forfeitGameDialogRef.current?.close()}
|
||||||
|
>
|
||||||
|
No
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
|
||||||
|
onClick={() => {
|
||||||
|
if (onForfeit) {
|
||||||
|
analytics.trackEvent('forfeit_game', {
|
||||||
|
player: player.index,
|
||||||
|
});
|
||||||
|
onForfeit();
|
||||||
|
setShowPlayerMenu(false);
|
||||||
|
forfeitGameDialogRef.current?.close();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{ fontSize: iconSize }}
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
</SettingsContainer>
|
||||||
|
</PlayerMenuWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PlayerMenu;
|
||||||
72
src/Components/Players/Players.tsx
Normal file
72
src/Components/Players/Players.tsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { twc } from 'react-twc';
|
||||||
|
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
||||||
|
import { usePlayers } from '../../Hooks/usePlayers';
|
||||||
|
import { Player as PlayerType } from '../../Types/Player';
|
||||||
|
import { PreStartMode } from '../../Types/Settings';
|
||||||
|
import LifeCounter from '../LifeCounter/LifeCounter';
|
||||||
|
import { RoulettePlayerCard } from '../PreStartGame/Games/RandomKing/RoulettePlayerCard';
|
||||||
|
import { GridLayout } from '../Views/Play';
|
||||||
|
|
||||||
|
const getGridArea = (player: PlayerType) => {
|
||||||
|
switch (player.index) {
|
||||||
|
case 0:
|
||||||
|
return 'grid-in-player0';
|
||||||
|
case 1:
|
||||||
|
return 'grid-in-player1';
|
||||||
|
case 2:
|
||||||
|
return 'grid-in-player2';
|
||||||
|
case 3:
|
||||||
|
return 'grid-in-player3';
|
||||||
|
case 4:
|
||||||
|
return 'grid-in-player4';
|
||||||
|
case 5:
|
||||||
|
return 'grid-in-player5';
|
||||||
|
default:
|
||||||
|
throw new Error('Invalid player index');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const PlayersWrapper = twc.div`w-full h-full bg-black`;
|
||||||
|
|
||||||
|
export const Players = ({ gridLayout }: { gridLayout: GridLayout }) => {
|
||||||
|
const { players } = usePlayers();
|
||||||
|
|
||||||
|
const { playing, settings, preStartCompleted, gameScore } = useGlobalSettings();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PlayersWrapper>
|
||||||
|
<div className={`grid w-full h-full gap-1 box-border ${gridLayout} `}>
|
||||||
|
{players.map((player) => {
|
||||||
|
const gridArea = getGridArea(player);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={player.index}
|
||||||
|
className={`relative flex justify-center items-center align-middle ${gridArea}`}
|
||||||
|
>
|
||||||
|
<LifeCounter
|
||||||
|
player={player}
|
||||||
|
opponents={players.filter(
|
||||||
|
(opponent) => opponent.index !== player.index
|
||||||
|
)}
|
||||||
|
matchScore={
|
||||||
|
settings.showMatchScore
|
||||||
|
? gameScore[player.index]
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{settings.preStartMode === PreStartMode.RandomKing &&
|
||||||
|
!preStartCompleted &&
|
||||||
|
!playing &&
|
||||||
|
settings.showStartingPlayer && (
|
||||||
|
<div className="absolute size-full z-20">
|
||||||
|
<RoulettePlayerCard player={player} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</PlayersWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
202
src/Components/PreStartGame/Games/FingerGame.tsx
Normal file
202
src/Components/PreStartGame/Games/FingerGame.tsx
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useGlobalSettings } from '../../../Hooks/useGlobalSettings';
|
||||||
|
import { usePlayers } from '../../../Hooks/usePlayers';
|
||||||
|
|
||||||
|
type TouchPoint = {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOrientation = () => {
|
||||||
|
return window.matchMedia('(orientation: portrait)').matches
|
||||||
|
? 'portrait'
|
||||||
|
: 'landscape';
|
||||||
|
};
|
||||||
|
|
||||||
|
const ANIMATION_INTRO_LENGTH = 500;
|
||||||
|
|
||||||
|
const BEFORE_INTRO_TIMER_LENGTH = 100;
|
||||||
|
|
||||||
|
export const FingerGame = () => {
|
||||||
|
const { players } = usePlayers();
|
||||||
|
|
||||||
|
const aboutToStartTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
const selectingPlayerTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
|
const [touchPoints, setTouchPoints] = useState<TouchPoint[]>([]);
|
||||||
|
const [selectedTouchPoint, setSelectedTouchPoint] = useState<
|
||||||
|
TouchPoint | undefined
|
||||||
|
>();
|
||||||
|
const [timerStarted, setTimerStarted] = useState(false);
|
||||||
|
|
||||||
|
const { setPlaying, goToStart } = useGlobalSettings();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
//Start playing when someone is selected and any touch point is released
|
||||||
|
if (selectedTouchPoint && touchPoints.length !== players.length) {
|
||||||
|
aboutToStartTimerRef.current = setTimeout(() => {
|
||||||
|
setSelectedTouchPoint(undefined);
|
||||||
|
setPlaying(true);
|
||||||
|
}, ANIMATION_INTRO_LENGTH);
|
||||||
|
|
||||||
|
setTimerStarted(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no touch point is selected, select one with a delay
|
||||||
|
if (touchPoints.length === players.length && !selectedTouchPoint) {
|
||||||
|
selectingPlayerTimerRef.current = setTimeout(() => {
|
||||||
|
const randomIndex = Math.floor(Math.random() * touchPoints.length);
|
||||||
|
const randomTouchPoint = touchPoints[randomIndex];
|
||||||
|
setSelectedTouchPoint(randomTouchPoint);
|
||||||
|
}, BEFORE_INTRO_TIMER_LENGTH);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectingPlayerTimerRef.current) {
|
||||||
|
clearTimeout(selectingPlayerTimerRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (aboutToStartTimerRef.current) {
|
||||||
|
clearTimeout(aboutToStartTimerRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [touchPoints, players.length]);
|
||||||
|
|
||||||
|
const handleOnTouchStart = (e: React.TouchEvent) => {
|
||||||
|
if (selectedTouchPoint) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Get the first touch point id that isn't already in the touchPoints array
|
||||||
|
const touch = Array.from(e.changedTouches).find(
|
||||||
|
(t) => !touchPoints.find((p) => p.id === t.identifier)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!touch) {
|
||||||
|
console.error('No touch found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { clientX, clientY } = touch;
|
||||||
|
|
||||||
|
// Adjust coordinates for portrait mode
|
||||||
|
if (getOrientation() === 'portrait') {
|
||||||
|
const tempX = clientX;
|
||||||
|
clientX = clientY;
|
||||||
|
clientY = window.innerWidth - tempX;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTouchPoints = {
|
||||||
|
x: clientX,
|
||||||
|
y: clientY,
|
||||||
|
id: touch.identifier,
|
||||||
|
};
|
||||||
|
|
||||||
|
setTouchPoints([...touchPoints, newTouchPoints]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnTouchEnd = (e: React.TouchEvent) => {
|
||||||
|
if (selectedTouchPoint) {
|
||||||
|
aboutToStartTimerRef.current = setTimeout(() => {
|
||||||
|
setSelectedTouchPoint(undefined);
|
||||||
|
setPlaying(true);
|
||||||
|
}, ANIMATION_INTRO_LENGTH);
|
||||||
|
setTimerStarted(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the touch point that was just released
|
||||||
|
const touch = e.changedTouches[e.changedTouches.length - 1];
|
||||||
|
|
||||||
|
// Get the index of the touch point that was just released
|
||||||
|
const index = touchPoints.findIndex((p) => p.id === touch.identifier);
|
||||||
|
|
||||||
|
// Remove the touch point that was just released
|
||||||
|
setTouchPoints([
|
||||||
|
...touchPoints.slice(0, index),
|
||||||
|
...touchPoints.slice(index + 1),
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="absolute flex justify-center items-center w-full h-full portrait:h-[100dvw] portrait:w-[100dvh] z-50 bg-secondary-main overflow-hidden"
|
||||||
|
onTouchStart={handleOnTouchStart}
|
||||||
|
onTouchEnd={handleOnTouchEnd}
|
||||||
|
|
||||||
|
// FIXEME: This code is not performant, but updates a touch point's position when it moves
|
||||||
|
// onTouchMove={(e) => {
|
||||||
|
// e.preventDefault();
|
||||||
|
|
||||||
|
// // Get the touch point that was just moved
|
||||||
|
// const touch = Array.from(e.changedTouches).find((t) =>
|
||||||
|
// touchPoints.find((p) => p.id === t.identifier)
|
||||||
|
// );
|
||||||
|
|
||||||
|
// if (!touch) {
|
||||||
|
// console.error('No touch found');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let { clientX, clientY } = touch;
|
||||||
|
|
||||||
|
// // Adjust coordinates for portrait mode
|
||||||
|
// if (getOrientation() === 'portrait') {
|
||||||
|
// const tempX = clientX;
|
||||||
|
// clientX = clientY;
|
||||||
|
// clientY = window.innerWidth - tempX;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Get the index of the touch point that was just moved
|
||||||
|
// const index = touchPoints.findIndex(
|
||||||
|
// (p) => p.id === touch.identifier
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Update the touch point that was just moved
|
||||||
|
// setTouchPoints([
|
||||||
|
// ...touchPoints.slice(0, index),
|
||||||
|
// { x: clientX, y: clientY, id: touch.identifier },
|
||||||
|
// ...touchPoints.slice(index + 1),
|
||||||
|
// ]);
|
||||||
|
// }}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="absolute flex top-4 left-4 rounded-lg px-2 py-1 justify-center bg-primary-main text-text-primary text-xs duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
|
||||||
|
onClick={goToStart}
|
||||||
|
>
|
||||||
|
<div className="text-xl leading-4">{'<'} </div>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
{touchPoints.length !== players.length && (
|
||||||
|
<div className="flex flex-col items-center text-[13vmin] whitespace-nowrap pointer-events-none webkit-user-select-none">
|
||||||
|
Waiting for fingers <br />
|
||||||
|
<div className="tabular-nums">
|
||||||
|
{touchPoints.length}/{players.length}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{touchPoints.map((point, index) => (
|
||||||
|
<div
|
||||||
|
key={`touch-point-${index}`}
|
||||||
|
data-is-selected={selectedTouchPoint?.id === point.id}
|
||||||
|
data-unloading={timerStarted}
|
||||||
|
className="absolute rounded-full translate-x-[-50%] translate-y-[-50%] transition-all duration-500
|
||||||
|
h-[75px] w-[75px]
|
||||||
|
data-[unloading=false]:data-[is-selected=true]:h-[250px] data-[unloading=false]:data-[is-selected=true]:w-[250px]
|
||||||
|
data-[unloading=true]:h-[0px] data-[unloading=true]:w-[0px] data-[unloading=true]:duration-[400ms]
|
||||||
|
pointer-events-none
|
||||||
|
"
|
||||||
|
style={{
|
||||||
|
left: point.x,
|
||||||
|
top: point.y,
|
||||||
|
backgroundColor: players[index]?.color ?? 'red',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { useGlobalSettings } from '../../../../Hooks/useGlobalSettings';
|
||||||
|
import { usePlayers } from '../../../../Hooks/usePlayers';
|
||||||
|
|
||||||
|
export const RandomKingRandomizer = () => {
|
||||||
|
const { setRandomizingPlayer } = useGlobalSettings();
|
||||||
|
|
||||||
|
const randomIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
const prevRandomIndexRef = useRef<number>(-1);
|
||||||
|
|
||||||
|
const { settings, randomizingPlayer, setPreStartCompleted } =
|
||||||
|
useGlobalSettings();
|
||||||
|
|
||||||
|
const { players, setPlayers } = usePlayers();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
players.length > 1 &&
|
||||||
|
settings.showStartingPlayer &&
|
||||||
|
randomizingPlayer
|
||||||
|
) {
|
||||||
|
randomIntervalRef.current = setInterval(() => {
|
||||||
|
let randomIndex: number;
|
||||||
|
|
||||||
|
do {
|
||||||
|
randomIndex = Math.floor(Math.random() * players.length);
|
||||||
|
} while (randomIndex === prevRandomIndexRef.current);
|
||||||
|
|
||||||
|
prevRandomIndexRef.current = randomIndex;
|
||||||
|
setPlayers(
|
||||||
|
players.map((p) =>
|
||||||
|
p.index === prevRandomIndexRef.current
|
||||||
|
? {
|
||||||
|
...p,
|
||||||
|
isStartingPlayer: true,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...p,
|
||||||
|
isStartingPlayer: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomPlayerIndex = Math.floor(Math.random() * players.length);
|
||||||
|
setPlayers(
|
||||||
|
players.map((p) =>
|
||||||
|
p.index === randomPlayerIndex
|
||||||
|
? {
|
||||||
|
...p,
|
||||||
|
isStartingPlayer: true,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...p,
|
||||||
|
isStartingPlayer: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (randomIntervalRef.current) {
|
||||||
|
clearInterval(randomIntervalRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [players.length, setPlayers, randomizingPlayer]);
|
||||||
|
|
||||||
|
const gradientColors = players.map((player) => player.color).join(', ');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="absolute flex justify-center items-center h-screen w-screen portrait:h-[100vw] portrait:w-[100vh] z-40 cursor-pointer text-5xl"
|
||||||
|
onClick={() => {
|
||||||
|
if (randomIntervalRef.current) {
|
||||||
|
clearInterval(randomIntervalRef.current);
|
||||||
|
randomIntervalRef.current = null;
|
||||||
|
}
|
||||||
|
setRandomizingPlayer(false);
|
||||||
|
setPreStartCompleted(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="absolute flex top-[30%] justify-center items-center px-8 py-4">
|
||||||
|
<div
|
||||||
|
className="absolute size-full blur-[3px] rounded-2xl opacity-90 saturate-150"
|
||||||
|
style={{
|
||||||
|
backgroundImage: `linear-gradient(60deg, ${gradientColors})`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<p className="relative z-10 text-[5vmax]">PRESS TO START</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { useGlobalSettings } from '../../../../Hooks/useGlobalSettings';
|
||||||
|
import { Player, Rotation } from '../../../../Types/Player';
|
||||||
|
import { Paragraph } from '../../../Misc/TextComponents';
|
||||||
|
import { DynamicText } from '../../StartingPlayerCard';
|
||||||
|
|
||||||
|
export const RoulettePlayerCard = ({ player }: { player: Player }) => {
|
||||||
|
const startPlayingTimerRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||||
|
|
||||||
|
const { settings, randomizingPlayer, playing, setPlaying } =
|
||||||
|
useGlobalSettings();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
player.isStartingPlayer &&
|
||||||
|
((!playing && randomizingPlayer) || !playing)
|
||||||
|
) {
|
||||||
|
startPlayingTimerRef.current = setTimeout(() => {
|
||||||
|
setPlaying(true);
|
||||||
|
}, 10_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => clearTimeout(startPlayingTimerRef.current);
|
||||||
|
}, [
|
||||||
|
player.isStartingPlayer,
|
||||||
|
playing,
|
||||||
|
setPlaying,
|
||||||
|
settings.preStartMode,
|
||||||
|
randomizingPlayer,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const calcTextRotation =
|
||||||
|
player.settings.rotation === Rotation.SideFlipped ||
|
||||||
|
player.settings.rotation === Rotation.Side
|
||||||
|
? player.settings.rotation - 180
|
||||||
|
: player.settings.rotation;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative flex flex-grow flex-col items-center w-full h-full overflow-hidden bg-black">
|
||||||
|
<div
|
||||||
|
className="flex absolute w-full h-full justify-center items-center pointer-events-none select-none webkit-user-select-none z-10"
|
||||||
|
style={{ backgroundColor: player.color }}
|
||||||
|
>
|
||||||
|
{player.isStartingPlayer && (
|
||||||
|
<DynamicText
|
||||||
|
style={{
|
||||||
|
rotate: `${calcTextRotation}deg`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col justify-center items-center">
|
||||||
|
<Paragraph>👑</Paragraph>
|
||||||
|
</div>
|
||||||
|
</DynamicText>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
159
src/Components/PreStartGame/Games/Trivia.tsx
Normal file
159
src/Components/PreStartGame/Games/Trivia.tsx
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { useGlobalSettings } from '../../../Hooks/useGlobalSettings';
|
||||||
|
|
||||||
|
const questions = [
|
||||||
|
'Who has the most siblings?',
|
||||||
|
'Who has the most pets?',
|
||||||
|
'Who has the most tattoos?',
|
||||||
|
'Who has the most piercings?',
|
||||||
|
'Who has the most expensive shoes?',
|
||||||
|
'Who has the most most amount of teeth?',
|
||||||
|
'Who has the least amount of teeth?',
|
||||||
|
'Who lives closest to the equator?',
|
||||||
|
'Who is the tallest person in the group?',
|
||||||
|
'Who is the shortest person in the group?',
|
||||||
|
'Who speaks the most languages?',
|
||||||
|
'Who has traveled to the most countries?',
|
||||||
|
'Who has the earliest birthday in the year?',
|
||||||
|
'Who has won the most awards or trophies?',
|
||||||
|
'Who is the best cook among you?',
|
||||||
|
'Who is the fastest runner?',
|
||||||
|
'Who has the most unique hobby?',
|
||||||
|
'Who is the biggest movie buff?',
|
||||||
|
'Who is the most tech-savvy?',
|
||||||
|
'Who is the best at solving puzzles?',
|
||||||
|
'Who has the most extensive music collection?',
|
||||||
|
'Who has the most impressive collection of books?',
|
||||||
|
'Who has the most experience in a particular sport or activity?',
|
||||||
|
'Who has the most interesting job or profession?',
|
||||||
|
'Who has the most artistic talent?',
|
||||||
|
'Who is the most organized person?',
|
||||||
|
'Who is the best at keeping secrets?',
|
||||||
|
'Who has the most fascinating family history?',
|
||||||
|
'Who has the most embarrassing childhood nickname?',
|
||||||
|
'Who has the most unusual talent or skill?',
|
||||||
|
'Who has the most interesting family tradition?',
|
||||||
|
'Who has the most impressive celebrity encounter?',
|
||||||
|
'Who has the most unusual phobia?',
|
||||||
|
'Who has the most adventurous spirit?',
|
||||||
|
'Who has the most unique item in their wallet/purse?',
|
||||||
|
'Who has the most daring fashion sense?',
|
||||||
|
'Who has the most impressive party trick?',
|
||||||
|
'Who has the most memorable encounter with a wild animal?',
|
||||||
|
'Who has the most adventurous palate?',
|
||||||
|
'Who has the most unusual collection?',
|
||||||
|
'Who has the most unique bucket list item?',
|
||||||
|
'Who has the most inspiring life motto or mantra?',
|
||||||
|
'Who is the most likely to break out into song or dance in public?',
|
||||||
|
'Who is the most likely to be found binge-watching TV shows?',
|
||||||
|
'Who is the biggest procrastinator?',
|
||||||
|
'Who is the most likely to cry during a movie?',
|
||||||
|
'Who is the most adventurous when it comes to trying new foods?',
|
||||||
|
"Who is the most likely to forget someone's birthday?",
|
||||||
|
'Who is the best at giving advice?',
|
||||||
|
'Who is the worst at giving advice?',
|
||||||
|
'Who is the most likely to be found reading a book at a party?',
|
||||||
|
'Who is the most likely to win in a game of charades?',
|
||||||
|
'Who is the most likely to get lost in their own neighborhood?',
|
||||||
|
'Who is the most sentimental?',
|
||||||
|
'Who is the most likely to become famous?',
|
||||||
|
'Who is the most likely to become a millionaire?',
|
||||||
|
'Who is the most likely to start their own business?',
|
||||||
|
'Who is the most likely to become president?',
|
||||||
|
'Who is the most likely to go viral on social media?',
|
||||||
|
'Who is the most likely to win a Nobel Prize?',
|
||||||
|
'Who is the most likely to be a superhero in disguise?',
|
||||||
|
'Who is the most likely to survive a zombie apocalypse?',
|
||||||
|
'Who is the most likely to believe in aliens?',
|
||||||
|
'Who is the most likely to spend all their money on something silly?',
|
||||||
|
'Who is the most likely to write a bestselling novel?',
|
||||||
|
'Who is the most likely to be a secret agent?',
|
||||||
|
'Who is the most likely to be a professional athlete?',
|
||||||
|
'Who is the most likely to win a game of trivia?',
|
||||||
|
|
||||||
|
'Who is the most likely to win the upcoming game?',
|
||||||
|
'Who is the most likely to win at a game of Pokémon TCG?',
|
||||||
|
'Who has the most valuable card in their collection?',
|
||||||
|
'Who is the best at building decks?',
|
||||||
|
'Who has won the most games?',
|
||||||
|
'Who has the largest collection of cards?',
|
||||||
|
'Who is the most knowledgeable about Magic the Gathering lore?',
|
||||||
|
'Who is the most strategic?',
|
||||||
|
'Who is the most likely to trade away their most valuable card for something silly?',
|
||||||
|
'Who is the most competitive?',
|
||||||
|
'Who would be the most creative when it comes to making up new Magic the Gathering rules?',
|
||||||
|
'Who is the most likely to organize a Magic the Gathering draft tournament?',
|
||||||
|
'Who is the most enthusiastic about opening booster packs?',
|
||||||
|
'Who has the most unique and unusual Magic the Gathering deck?',
|
||||||
|
'Who is the most likely to cosplay as their favorite Magic the Gathering character?',
|
||||||
|
'Who is the most likely to forget to bring their Magic the Gathering deck to a game night?',
|
||||||
|
'Who is the most generous when it comes to lending out their decks?',
|
||||||
|
'Who is the most likely to start their own Magic the Gathering YouTube channel?',
|
||||||
|
'Who is the most skilled at bluffing during a game of Magic the Gathering?',
|
||||||
|
'Who is the most likely to spend all their money on Magic the Gathering cards?',
|
||||||
|
'Who is the most likely to rage quit during a game of Magic the Gathering?',
|
||||||
|
'Who is the most likely to win in a Magic the Gathering trivia contest?',
|
||||||
|
'Who is the most likely to build a themed Magic the Gathering deck?',
|
||||||
|
'Who is the most likely to organize a Magic the Gathering cube draft?',
|
||||||
|
'Who is the most likely to teach new players how to play Magic the Gathering?',
|
||||||
|
'Who is the most likely to build a commander deck with a ridiculous theme?',
|
||||||
|
'Who is the most likely to collect foreign-language Magic the Gathering cards?',
|
||||||
|
'Who is the most likely to participate in a Magic the Gathering charity event?',
|
||||||
|
'Who is the most likely to cosplay as their Magic the Gathering commander?',
|
||||||
|
'Who is the most likely to organize a Magic the Gathering charity tournament?',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const Trivia = () => {
|
||||||
|
const { setPlaying, goToStart } = useGlobalSettings();
|
||||||
|
|
||||||
|
const [randomQuestion, setRandomQuestion] = useState(
|
||||||
|
() => questions[Math.floor(Math.random() * questions.length)]
|
||||||
|
);
|
||||||
|
|
||||||
|
const setUniqueRandomQuestion = () => {
|
||||||
|
let newRandomQuestion =
|
||||||
|
questions[Math.floor(Math.random() * questions.length)];
|
||||||
|
while (newRandomQuestion === randomQuestion) {
|
||||||
|
newRandomQuestion =
|
||||||
|
questions[Math.floor(Math.random() * questions.length)];
|
||||||
|
}
|
||||||
|
setRandomQuestion(newRandomQuestion);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="absolute flex justify-center items-center w-full h-full portrait:h-[100dvw] portrait:w-[100dvh] z-50 bg-primary-dark overflow-hidden"
|
||||||
|
onClick={() => setPlaying(true)}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="absolute flex top-4 left-4 rounded-lg px-2 py-1 justify-center bg-primary-main text-text-primary text-xs duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
|
||||||
|
onClick={goToStart}
|
||||||
|
>
|
||||||
|
<div className="text-xl leading-[0.80rem]">{'<'} </div>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="absolute flex top-4 right-4 rounded-lg px-2 py-1 justify-center bg-primary-main text-text-primary text-xs duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setUniqueRandomQuestion();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reroll
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="size-full flex flex-col justify-between items-center whitespace-nowrap pointer-events-none webkit-user-select-none text-wrap text-center py-[10vmin] px-[10vmax]">
|
||||||
|
<div className="text-[6vmin]">Decide who starts by answering:</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className="text-[8vmin] rotate-180 text-text-primary opacity-60">
|
||||||
|
{randomQuestion}
|
||||||
|
</div>
|
||||||
|
<div className="text-[8vmin]">{randomQuestion}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-[6vmin]">(Tap the screen to dismiss)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
29
src/Components/PreStartGame/PreStart.tsx
Normal file
29
src/Components/PreStartGame/PreStart.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
||||||
|
import { PreStartMode } from '../../Types/Settings';
|
||||||
|
import { FingerGame } from './Games/FingerGame';
|
||||||
|
|
||||||
|
import { RandomKingRandomizer } from './Games/RandomKing/RandomKingSelectWrapper';
|
||||||
|
import { Trivia } from './Games/Trivia';
|
||||||
|
|
||||||
|
export const PreStart = () => {
|
||||||
|
const { settings, randomizingPlayer, goToStart } = useGlobalSettings();
|
||||||
|
|
||||||
|
if (settings.preStartMode === PreStartMode.RandomKing) {
|
||||||
|
if (!randomizingPlayer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <RandomKingRandomizer />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.preStartMode === PreStartMode.FingerGame) {
|
||||||
|
return <FingerGame />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.preStartMode === PreStartMode.Trivia) {
|
||||||
|
return <Trivia />;
|
||||||
|
}
|
||||||
|
|
||||||
|
goToStart();
|
||||||
|
return null;
|
||||||
|
};
|
||||||
53
src/Components/PreStartGame/StartingPlayerCard.tsx
Normal file
53
src/Components/PreStartGame/StartingPlayerCard.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { twc } from 'react-twc';
|
||||||
|
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
||||||
|
import { Player, Rotation } from '../../Types/Player';
|
||||||
|
import { PreStartMode } from '../../Types/Settings';
|
||||||
|
import { Paragraph } from '../Misc/TextComponents';
|
||||||
|
|
||||||
|
export const DynamicText = twc.div`text-[8vmin] whitespace-nowrap`;
|
||||||
|
|
||||||
|
export const StartingPlayerCard = ({ player }: { player: Player }) => {
|
||||||
|
const { settings, setPlaying, randomizingPlayer } = useGlobalSettings();
|
||||||
|
|
||||||
|
const calcTextRotation =
|
||||||
|
player.settings.rotation === Rotation.SideFlipped ||
|
||||||
|
player.settings.rotation === Rotation.Side
|
||||||
|
? player.settings.rotation - 180
|
||||||
|
: player.settings.rotation;
|
||||||
|
|
||||||
|
const calcRotation =
|
||||||
|
player.settings.rotation === Rotation.SideFlipped ||
|
||||||
|
player.settings.rotation === Rotation.Side
|
||||||
|
? player.settings.rotation - 90
|
||||||
|
: player.settings.rotation;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="z-20 flex absolute w-full h-full justify-center items-center select-none cursor-pointer webkit-user-select-none backdrop-blur-xl"
|
||||||
|
style={{
|
||||||
|
rotate: `${calcRotation}deg`,
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setPlaying(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DynamicText
|
||||||
|
style={{
|
||||||
|
rotate: `${calcTextRotation}deg`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col justify-center items-center text-text-primary font-semibold">
|
||||||
|
<Paragraph>👑</Paragraph>
|
||||||
|
{(!randomizingPlayer ||
|
||||||
|
(settings.preStartMode !== PreStartMode.None &&
|
||||||
|
settings.preStartMode !== PreStartMode.FingerGame)) && (
|
||||||
|
<>
|
||||||
|
<Paragraph>You start!</Paragraph>
|
||||||
|
<Paragraph className="text-xl">(Press to hide)</Paragraph>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</DynamicText>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
71
src/Components/ScoreDisplay/ScoreDisplay.tsx
Normal file
71
src/Components/ScoreDisplay/ScoreDisplay.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { twc } from 'react-twc';
|
||||||
|
import { Player } from '../../Types/Player';
|
||||||
|
import { GameScore } from '../../Contexts/GlobalSettingsContext';
|
||||||
|
|
||||||
|
const ScoreContainer = twc.div`
|
||||||
|
absolute bottom-4 left-1/2 -translate-x-1/2
|
||||||
|
bg-background-default/90 backdrop-blur-sm
|
||||||
|
rounded-lg p-4
|
||||||
|
shadow-lg
|
||||||
|
z-40
|
||||||
|
min-w-[200px]
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Title = twc.h3`
|
||||||
|
text-sm font-semibold text-text-secondary
|
||||||
|
uppercase tracking-wide mb-3
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ScoreList = twc.div`
|
||||||
|
flex flex-col gap-2
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ScoreItem = twc.div`
|
||||||
|
flex items-center justify-between gap-4
|
||||||
|
`;
|
||||||
|
|
||||||
|
const PlayerInfo = twc.div`
|
||||||
|
flex items-center gap-2
|
||||||
|
`;
|
||||||
|
|
||||||
|
const PlayerColor = twc.div`
|
||||||
|
w-4 h-4 rounded-full
|
||||||
|
`;
|
||||||
|
|
||||||
|
const PlayerName = twc.span`
|
||||||
|
text-text-primary font-medium
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Score = twc.span`
|
||||||
|
text-text-primary font-bold text-lg
|
||||||
|
`;
|
||||||
|
|
||||||
|
type ScoreDisplayProps = {
|
||||||
|
players: Player[];
|
||||||
|
gameScore: GameScore;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ScoreDisplay = ({ players, gameScore }: ScoreDisplayProps) => {
|
||||||
|
const hasAnyScore = Object.values(gameScore).some((score) => score > 0);
|
||||||
|
|
||||||
|
if (!hasAnyScore) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScoreContainer>
|
||||||
|
<Title>Match Score</Title>
|
||||||
|
<ScoreList>
|
||||||
|
{players.map((player) => (
|
||||||
|
<ScoreItem key={player.index}>
|
||||||
|
<PlayerInfo>
|
||||||
|
<PlayerColor style={{ backgroundColor: player.color }} />
|
||||||
|
<PlayerName>{player.name || `Player ${player.index + 1}`}</PlayerName>
|
||||||
|
</PlayerInfo>
|
||||||
|
<Score>{gameScore[player.index] || 0}</Score>
|
||||||
|
</ScoreItem>
|
||||||
|
))}
|
||||||
|
</ScoreList>
|
||||||
|
</ScoreContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,73 +1,169 @@
|
|||||||
import styled from 'styled-components';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { twc } from 'react-twc';
|
||||||
|
import { twGridTemplateAreas } from '../../../tailwind.config';
|
||||||
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
||||||
import { usePlayers } from '../../Hooks/usePlayers';
|
import { usePlayers } from '../../Hooks/usePlayers';
|
||||||
import { Orientation } from '../../Types/Settings';
|
import { Orientation, PreStartMode } from '../../Types/Settings';
|
||||||
import { Player } from '../Player/Player';
|
import { Players } from '../Players/Players';
|
||||||
|
import { PreStart } from '../PreStartGame/PreStart';
|
||||||
|
import { GameOver } from '../GameOver/GameOver';
|
||||||
|
|
||||||
const MainWrapper = styled.div`
|
const MainWrapper = twc.div`w-[100dvmax] h-[100dvmin] overflow-hidden, setPlayers`;
|
||||||
width: 100vmax;
|
|
||||||
height: 100vmin;
|
type GridTemplateAreasKeys = keyof typeof twGridTemplateAreas;
|
||||||
width: 100dvmax;
|
|
||||||
height: 100dvmin;
|
export type GridLayout = `grid-areas-${GridTemplateAreasKeys}`;
|
||||||
overflow: hidden;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const Play = () => {
|
export const Play = () => {
|
||||||
const { players } = usePlayers();
|
const { players, setPlayers, resetCurrentGame, setStartingPlayerIndex } = usePlayers();
|
||||||
const { initialGameSettings } = useGlobalSettings();
|
const { initialGameSettings, playing, settings, preStartCompleted, gameScore, setGameScore } =
|
||||||
|
useGlobalSettings();
|
||||||
|
const [winner, setWinner] = useState<number | null>(null);
|
||||||
|
|
||||||
let Layout: JSX.Element;
|
let gridLayout: GridLayout;
|
||||||
switch (players.length) {
|
switch (players.length) {
|
||||||
case 1:
|
case 1:
|
||||||
if (initialGameSettings?.orientation === Orientation.Portrait) {
|
if (initialGameSettings?.orientation === Orientation.Portrait) {
|
||||||
Layout = Player(players, 'grid-areas-onePlayerPortrait');
|
gridLayout = 'grid-areas-onePlayerPortrait';
|
||||||
}
|
}
|
||||||
Layout = Player(players, 'grid-areas-onePlayerLandscape');
|
gridLayout = 'grid-areas-onePlayerLandscape';
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
switch (initialGameSettings?.orientation) {
|
switch (initialGameSettings?.orientation) {
|
||||||
case Orientation.Portrait:
|
case Orientation.Portrait:
|
||||||
Layout = Player(players, 'grid-areas-twoPlayersOppositePortrait');
|
gridLayout = 'grid-areas-twoPlayersOppositePortrait';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case Orientation.Landscape:
|
case Orientation.Landscape:
|
||||||
Layout = Player(players, 'grid-areas-twoPlayersSameSideLandscape');
|
gridLayout = 'grid-areas-twoPlayersSameSideLandscape';
|
||||||
break;
|
break;
|
||||||
case Orientation.OppositeLandscape:
|
case Orientation.OppositeLandscape:
|
||||||
Layout = Player(players, 'grid-areas-twoPlayersOppositeLandscape');
|
gridLayout = 'grid-areas-twoPlayersOppositeLandscape';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
if (initialGameSettings?.orientation === Orientation.Portrait) {
|
if (initialGameSettings?.orientation === Orientation.Portrait) {
|
||||||
Layout = Player(players, 'grid-areas-threePlayersSide');
|
gridLayout = 'grid-areas-threePlayersSide';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Layout = Player(players, 'grid-areas-threePlayers');
|
gridLayout = 'grid-areas-threePlayers';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case 4:
|
case 4:
|
||||||
if (initialGameSettings?.orientation === Orientation.Portrait) {
|
if (initialGameSettings?.orientation === Orientation.Portrait) {
|
||||||
Layout = Player(players, 'grid-areas-fourPlayerPortrait');
|
gridLayout = 'grid-areas-fourPlayerPortrait';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Layout = Player(players, 'grid-areas-fourPlayer');
|
gridLayout = 'grid-areas-fourPlayer';
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
if (initialGameSettings?.orientation === Orientation.Portrait) {
|
if (initialGameSettings?.orientation === Orientation.Portrait) {
|
||||||
Layout = Player(players, 'grid-areas-fivePlayersSide');
|
gridLayout = 'grid-areas-fivePlayersSide';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Layout = Player(players, 'grid-areas-fivePlayers');
|
gridLayout = 'grid-areas-fivePlayers';
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
if (initialGameSettings?.orientation === Orientation.Portrait) {
|
if (initialGameSettings?.orientation === Orientation.Portrait) {
|
||||||
Layout = Player(players, 'grid-areas-sixPlayersSide');
|
gridLayout = 'grid-areas-sixPlayersSide';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Layout = Player(players, 'grid-areas-sixPlayers');
|
gridLayout = 'grid-areas-sixPlayers';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <MainWrapper>{Layout}</MainWrapper>;
|
useEffect(() => {
|
||||||
|
if (settings.preStartMode !== PreStartMode.None) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomIndex = Math.floor(Math.random() * players.length);
|
||||||
|
|
||||||
|
setPlayers(
|
||||||
|
players.map((p) =>
|
||||||
|
p.index === randomIndex
|
||||||
|
? {
|
||||||
|
...p,
|
||||||
|
isStartingPlayer: true,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...p,
|
||||||
|
isStartingPlayer: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Check for game over when only one player remains
|
||||||
|
useEffect(() => {
|
||||||
|
if (players.length < 2 || winner !== null || !settings.showMatchScore) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const activePlayers = players.filter((p) => !p.hasLost);
|
||||||
|
|
||||||
|
// If only one player is alive, they are the winner
|
||||||
|
if (activePlayers.length === 1) {
|
||||||
|
setWinner(activePlayers[0].index);
|
||||||
|
}
|
||||||
|
}, [players, winner, settings.showMatchScore]);
|
||||||
|
|
||||||
|
const handleStartNextGame = () => {
|
||||||
|
if (winner === null) return;
|
||||||
|
|
||||||
|
// Update score
|
||||||
|
const newScore = { ...gameScore };
|
||||||
|
newScore[winner] = (newScore[winner] || 0) + 1;
|
||||||
|
setGameScore(newScore);
|
||||||
|
|
||||||
|
// Set the loser as the starting player for next game
|
||||||
|
const loserIndex = players.find((p) => p.index !== winner)?.index ?? 0;
|
||||||
|
setStartingPlayerIndex(loserIndex);
|
||||||
|
|
||||||
|
// Reset game
|
||||||
|
resetCurrentGame();
|
||||||
|
setWinner(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStay = () => {
|
||||||
|
if (winner === null) return;
|
||||||
|
|
||||||
|
// Update score
|
||||||
|
const newScore = { ...gameScore };
|
||||||
|
newScore[winner] = (newScore[winner] || 0) + 1;
|
||||||
|
setGameScore(newScore);
|
||||||
|
|
||||||
|
// Reset hasLost state for all players
|
||||||
|
setPlayers(
|
||||||
|
players.map((p) => ({
|
||||||
|
...p,
|
||||||
|
hasLost: false,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Clear winner to allow new game over detection
|
||||||
|
setWinner(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MainWrapper>
|
||||||
|
{players.length > 1 &&
|
||||||
|
!preStartCompleted &&
|
||||||
|
settings.preStartMode !== PreStartMode.None &&
|
||||||
|
!playing &&
|
||||||
|
settings.showStartingPlayer && <PreStart />}
|
||||||
|
|
||||||
|
<Players gridLayout={gridLayout} />
|
||||||
|
|
||||||
|
{winner !== null && (
|
||||||
|
<GameOver
|
||||||
|
winner={players[winner]}
|
||||||
|
onStartNextGame={handleStartNextGame}
|
||||||
|
onStay={handleStay}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</MainWrapper>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,28 +1,30 @@
|
|||||||
import { FormControlLabel, Radio, RadioGroup } from '@mui/material';
|
import { twc } from 'react-twc';
|
||||||
import React from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { theme } from '../../../Data/theme';
|
|
||||||
import {
|
import {
|
||||||
FivePlayers,
|
FivePlayers,
|
||||||
|
FivePlayersSide,
|
||||||
FourPlayers,
|
FourPlayers,
|
||||||
FourPlayersSide,
|
FourPlayersSide,
|
||||||
OnePlayerPortrait,
|
OnePlayerPortrait,
|
||||||
SixPlayers,
|
SixPlayers,
|
||||||
|
SixPlayersSide,
|
||||||
ThreePlayers,
|
ThreePlayers,
|
||||||
ThreePlayersSide,
|
ThreePlayersSide,
|
||||||
TwoPlayersOppositeLandscape,
|
TwoPlayersOppositeLandscape,
|
||||||
TwoPlayersOppositePortrait,
|
TwoPlayersOppositePortrait,
|
||||||
TwoPlayersSameSide,
|
TwoPlayersSameSide,
|
||||||
} from '../../../Icons/generated/Layouts';
|
} from '../../../Icons/generated/Layouts';
|
||||||
|
|
||||||
import OnePlayerLandscape from '../../../Icons/generated/Layouts/OnePlayerLandscape';
|
import OnePlayerLandscape from '../../../Icons/generated/Layouts/OnePlayerLandscape';
|
||||||
import { Orientation } from '../../../Types/Settings';
|
import { Orientation } from '../../../Types/Settings';
|
||||||
|
|
||||||
const LayoutWrapper = styled.div`
|
const LayoutsRadioGroup = twc.div`flex flex-row justify-center items-center gap-4 self-center w-full`;
|
||||||
flex-direction: row;
|
|
||||||
display: flex;
|
const Label = twc.label`flex flex-row relative max-w-[118px] hover:bg-white/[0.03] rounded-2xl cursor-pointer`;
|
||||||
justify-content: space-evenly;
|
|
||||||
`;
|
const Input = twc.input`peer sr-only`;
|
||||||
|
|
||||||
|
const IconWrapper = twc(
|
||||||
|
'div'
|
||||||
|
)`peer-checked:fill-primary-main fill-primary-dark max-h-[200px] h-[40vmin] w-[21vmin]`;
|
||||||
|
|
||||||
type LayoutOptionsProps = {
|
type LayoutOptionsProps = {
|
||||||
numberOfPlayers: number;
|
numberOfPlayers: number;
|
||||||
@@ -30,366 +32,212 @@ type LayoutOptionsProps = {
|
|||||||
onChange: (orientation: Orientation) => void;
|
onChange: (orientation: Orientation) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
|
export const LayoutOptions = ({
|
||||||
numberOfPlayers,
|
numberOfPlayers,
|
||||||
selectedOrientation,
|
selectedOrientation,
|
||||||
onChange,
|
onChange,
|
||||||
}) => {
|
}: LayoutOptionsProps) => {
|
||||||
const iconHeight = '30vmin';
|
switch (numberOfPlayers) {
|
||||||
const iconWidth = '20vmin';
|
case 1:
|
||||||
|
return (
|
||||||
|
<LayoutsRadioGroup>
|
||||||
|
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Landscape}`}>
|
||||||
|
<Input
|
||||||
|
type="radio"
|
||||||
|
id={`${numberOfPlayers}p-${Orientation.Landscape}`}
|
||||||
|
checked={selectedOrientation === Orientation.Landscape}
|
||||||
|
value={Orientation.Landscape}
|
||||||
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
|
/>
|
||||||
|
<IconWrapper>
|
||||||
|
<OnePlayerLandscape className="w-full h-full" />
|
||||||
|
</IconWrapper>
|
||||||
|
</Label>
|
||||||
|
|
||||||
const renderLayoutOptions = () => {
|
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Portrait}`}>
|
||||||
switch (numberOfPlayers) {
|
<Input
|
||||||
case 1:
|
type="radio"
|
||||||
return (
|
id={`${numberOfPlayers}p-${Orientation.Portrait}`}
|
||||||
<>
|
checked={selectedOrientation === Orientation.Portrait}
|
||||||
<FormControlLabel
|
|
||||||
value={Orientation.Landscape}
|
|
||||||
control={
|
|
||||||
<Radio
|
|
||||||
icon={
|
|
||||||
<OnePlayerLandscape
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<OnePlayerLandscape
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/>
|
|
||||||
<FormControlLabel
|
|
||||||
value={Orientation.Portrait}
|
value={Orientation.Portrait}
|
||||||
control={
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
<Radio
|
|
||||||
icon={
|
|
||||||
<OnePlayerPortrait
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<OnePlayerPortrait
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/>
|
/>
|
||||||
</>
|
<IconWrapper>
|
||||||
);
|
<OnePlayerPortrait className="w-full h-full" />
|
||||||
case 2:
|
</IconWrapper>
|
||||||
return (
|
</Label>
|
||||||
<>
|
</LayoutsRadioGroup>
|
||||||
<FormControlLabel
|
);
|
||||||
|
case 2:
|
||||||
|
return (
|
||||||
|
<LayoutsRadioGroup>
|
||||||
|
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Landscape}`}>
|
||||||
|
<Input
|
||||||
|
type="radio"
|
||||||
|
id={`${numberOfPlayers}p-${Orientation.Landscape}`}
|
||||||
|
checked={selectedOrientation === Orientation.Landscape}
|
||||||
value={Orientation.Landscape}
|
value={Orientation.Landscape}
|
||||||
control={
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
<Radio
|
|
||||||
icon={
|
|
||||||
<TwoPlayersSameSide
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<TwoPlayersSameSide
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/>
|
/>
|
||||||
<FormControlLabel
|
<IconWrapper>
|
||||||
|
<TwoPlayersSameSide className="w-full h-full" />
|
||||||
|
</IconWrapper>
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Portrait}`}>
|
||||||
|
<Input
|
||||||
|
type="radio"
|
||||||
|
id={`${numberOfPlayers}p-${Orientation.Portrait}`}
|
||||||
|
checked={selectedOrientation === Orientation.Portrait}
|
||||||
value={Orientation.Portrait}
|
value={Orientation.Portrait}
|
||||||
control={
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
<Radio
|
|
||||||
icon={
|
|
||||||
<TwoPlayersOppositePortrait
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<TwoPlayersOppositePortrait
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/>
|
/>
|
||||||
<FormControlLabel
|
<IconWrapper>
|
||||||
|
<TwoPlayersOppositePortrait className="w-full h-full" />
|
||||||
|
</IconWrapper>
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<Label
|
||||||
|
htmlFor={`${numberOfPlayers}p-${Orientation.OppositeLandscape}`}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="radio"
|
||||||
|
id={`${numberOfPlayers}p-${Orientation.OppositeLandscape}`}
|
||||||
|
checked={selectedOrientation === Orientation.OppositeLandscape}
|
||||||
value={Orientation.OppositeLandscape}
|
value={Orientation.OppositeLandscape}
|
||||||
control={
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
<Radio
|
|
||||||
icon={
|
|
||||||
<TwoPlayersOppositeLandscape
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<TwoPlayersOppositeLandscape
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/>
|
/>
|
||||||
</>
|
<IconWrapper>
|
||||||
);
|
<TwoPlayersOppositeLandscape className="w-full h-full" />
|
||||||
case 3:
|
</IconWrapper>
|
||||||
return (
|
</Label>
|
||||||
<>
|
</LayoutsRadioGroup>
|
||||||
<FormControlLabel
|
);
|
||||||
|
case 3:
|
||||||
|
return (
|
||||||
|
<LayoutsRadioGroup>
|
||||||
|
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Landscape}`}>
|
||||||
|
<Input
|
||||||
|
type="radio"
|
||||||
|
id={`${numberOfPlayers}p-${Orientation.Landscape}`}
|
||||||
|
checked={selectedOrientation === Orientation.Landscape}
|
||||||
value={Orientation.Landscape}
|
value={Orientation.Landscape}
|
||||||
control={
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
<Radio
|
|
||||||
icon={
|
|
||||||
<ThreePlayers
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<ThreePlayers
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/>
|
/>
|
||||||
<FormControlLabel
|
<IconWrapper>
|
||||||
|
<ThreePlayers className="w-full h-full" />
|
||||||
|
</IconWrapper>
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Portrait}`}>
|
||||||
|
<Input
|
||||||
|
type="radio"
|
||||||
|
id={`${numberOfPlayers}p-${Orientation.Portrait}`}
|
||||||
|
checked={selectedOrientation === Orientation.Portrait}
|
||||||
value={Orientation.Portrait}
|
value={Orientation.Portrait}
|
||||||
control={
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
<Radio
|
|
||||||
icon={
|
|
||||||
<ThreePlayersSide
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<ThreePlayersSide
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/>
|
/>
|
||||||
</>
|
<IconWrapper>
|
||||||
);
|
<ThreePlayersSide className="w-full h-full" />
|
||||||
|
</IconWrapper>
|
||||||
|
</Label>
|
||||||
|
</LayoutsRadioGroup>
|
||||||
|
);
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
return (
|
return (
|
||||||
<>
|
<LayoutsRadioGroup>
|
||||||
<FormControlLabel
|
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Landscape}`}>
|
||||||
|
<Input
|
||||||
|
type="radio"
|
||||||
|
id={`${numberOfPlayers}p-${Orientation.Landscape}`}
|
||||||
|
checked={selectedOrientation === Orientation.Landscape}
|
||||||
value={Orientation.Landscape}
|
value={Orientation.Landscape}
|
||||||
control={
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
<Radio
|
|
||||||
icon={
|
|
||||||
<FourPlayers
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<FourPlayers
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/>
|
/>
|
||||||
<FormControlLabel
|
<IconWrapper>
|
||||||
|
<FourPlayers className="w-full h-full" />
|
||||||
|
</IconWrapper>
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Portrait}`}>
|
||||||
|
<Input
|
||||||
|
type="radio"
|
||||||
|
id={`${numberOfPlayers}p-${Orientation.Portrait}`}
|
||||||
|
checked={selectedOrientation === Orientation.Portrait}
|
||||||
value={Orientation.Portrait}
|
value={Orientation.Portrait}
|
||||||
control={
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
<Radio
|
|
||||||
icon={
|
|
||||||
<FourPlayersSide
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<FourPlayersSide
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/>
|
/>
|
||||||
</>
|
<IconWrapper>
|
||||||
);
|
<FourPlayersSide className="w-full h-full" />
|
||||||
|
</IconWrapper>
|
||||||
|
</Label>
|
||||||
|
</LayoutsRadioGroup>
|
||||||
|
);
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
return (
|
return (
|
||||||
<>
|
<LayoutsRadioGroup>
|
||||||
<FormControlLabel
|
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Landscape}`}>
|
||||||
|
<Input
|
||||||
|
type="radio"
|
||||||
|
id={`${numberOfPlayers}p-${Orientation.Landscape}`}
|
||||||
|
checked={selectedOrientation === Orientation.Landscape}
|
||||||
value={Orientation.Landscape}
|
value={Orientation.Landscape}
|
||||||
control={
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
<Radio
|
|
||||||
icon={
|
|
||||||
<FivePlayers
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<FivePlayers
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/>
|
/>
|
||||||
{/* <FormControlLabel
|
<IconWrapper>
|
||||||
value={GridTemplateAreas.FivePlayersSide}
|
<FivePlayers className="w-full h-full" />
|
||||||
control={
|
</IconWrapper>
|
||||||
<Radio
|
</Label>
|
||||||
icon={
|
|
||||||
<FivePlayersSide
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<FivePlayersSide
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/> */}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
case 6:
|
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Portrait}`}>
|
||||||
return (
|
<Input
|
||||||
<>
|
type="radio"
|
||||||
<FormControlLabel
|
id={`${numberOfPlayers}p-${Orientation.Portrait}`}
|
||||||
|
checked={selectedOrientation === Orientation.Portrait}
|
||||||
|
value={Orientation.Portrait}
|
||||||
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
|
/>
|
||||||
|
<IconWrapper>
|
||||||
|
<FivePlayersSide className="w-full h-full" />
|
||||||
|
</IconWrapper>
|
||||||
|
</Label>
|
||||||
|
</LayoutsRadioGroup>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
return (
|
||||||
|
<LayoutsRadioGroup>
|
||||||
|
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Landscape}`}>
|
||||||
|
<Input
|
||||||
|
type="radio"
|
||||||
|
id={`${numberOfPlayers}p-${Orientation.Landscape}`}
|
||||||
|
checked={selectedOrientation === Orientation.Landscape}
|
||||||
value={Orientation.Landscape}
|
value={Orientation.Landscape}
|
||||||
control={
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
<Radio
|
|
||||||
icon={
|
|
||||||
<SixPlayers
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<SixPlayers
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/>
|
/>
|
||||||
{/* <FormControlLabel
|
<IconWrapper>
|
||||||
value={GridTemplateAreas.SixPlayersSide}
|
<SixPlayers className="w-full h-full" />
|
||||||
control={
|
</IconWrapper>
|
||||||
<Radio
|
</Label>
|
||||||
icon={
|
|
||||||
<SixPlayersSide
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.secondary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
checkedIcon={
|
|
||||||
<SixPlayersSide
|
|
||||||
height={iconHeight}
|
|
||||||
width={iconWidth}
|
|
||||||
fill={theme.palette.primary.main}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
TouchRippleProps={{ style: { display: 'none' } }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label=""
|
|
||||||
/> */}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Portrait}`}>
|
||||||
return null;
|
<Input
|
||||||
}
|
type="radio"
|
||||||
};
|
id={`${numberOfPlayers}p-${Orientation.Portrait}`}
|
||||||
|
checked={selectedOrientation === Orientation.Portrait}
|
||||||
|
value={Orientation.Portrait}
|
||||||
|
onChange={(e) => onChange(e.target.value as Orientation)}
|
||||||
|
/>
|
||||||
|
<IconWrapper>
|
||||||
|
<SixPlayersSide className="w-full h-full" />
|
||||||
|
</IconWrapper>
|
||||||
|
</Label>
|
||||||
|
</LayoutsRadioGroup>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
default:
|
||||||
<LayoutWrapper>
|
return null;
|
||||||
<RadioGroup
|
}
|
||||||
row
|
|
||||||
onChange={(_e, value) => {
|
|
||||||
onChange(value as Orientation);
|
|
||||||
}}
|
|
||||||
value={selectedOrientation}
|
|
||||||
style={{ justifyContent: 'center' }}
|
|
||||||
>
|
|
||||||
{renderLayoutOptions()}
|
|
||||||
</RadioGroup>
|
|
||||||
</LayoutWrapper>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,100 +1,52 @@
|
|||||||
import { Button, FormControl, FormLabel, Switch } from '@mui/material';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import Slider from '@mui/material/Slider';
|
import { twc } from 'react-twc';
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { createInitialPlayers } from '../../../Data/getInitialPlayers';
|
import { createInitialPlayers } from '../../../Data/getInitialPlayers';
|
||||||
import { theme } from '../../../Data/theme';
|
|
||||||
import { useAnalytics } from '../../../Hooks/useAnalytics';
|
import { useAnalytics } from '../../../Hooks/useAnalytics';
|
||||||
import { useGlobalSettings } from '../../../Hooks/useGlobalSettings';
|
import { useGlobalSettings } from '../../../Hooks/useGlobalSettings';
|
||||||
import { usePlayers } from '../../../Hooks/usePlayers';
|
import { usePlayers } from '../../../Hooks/usePlayers';
|
||||||
import { Cog, Info } from '../../../Icons/generated';
|
import { Cog, Info, Trinket } from '../../../Icons/generated';
|
||||||
import { InitialGameSettings, Orientation } from '../../../Types/Settings';
|
import {
|
||||||
import { InfoModal } from '../../Misc/InfoModal';
|
InitialGameSettings,
|
||||||
import { SettingsModal } from '../../Misc/SettingsModal';
|
Orientation,
|
||||||
import { Spacer } from '../../Misc/Spacer';
|
PreStartMode,
|
||||||
import { SupportMe } from '../../Misc/SupportMe';
|
defaultInitialGameSettings,
|
||||||
import { H1, Paragraph } from '../../Misc/TextComponents';
|
} from '../../../Types/Settings';
|
||||||
|
|
||||||
|
import { baseColors } from '../../../../tailwind.config';
|
||||||
|
import { InfoDialog } from '../../Dialogs/InfoDialog';
|
||||||
|
import { SettingsDialog } from '../../Dialogs/SettingsDialog';
|
||||||
|
import { ToggleButton } from '../../Misc/ToggleButton';
|
||||||
import { LayoutOptions } from './LayoutOptions';
|
import { LayoutOptions } from './LayoutOptions';
|
||||||
|
|
||||||
const MainWrapper = styled.div`
|
const commanderSettings: Pick<
|
||||||
width: 100dvw;
|
InitialGameSettings,
|
||||||
height: fit-content;
|
'numberOfPlayers' | 'startingLifeTotal' | 'orientation'
|
||||||
padding-bottom: 58px;
|
> = {
|
||||||
overflow: hidden;
|
numberOfPlayers: 4,
|
||||||
align-items: center;
|
startingLifeTotal: 40,
|
||||||
display: flex;
|
orientation: Orientation.Landscape,
|
||||||
flex-direction: column;
|
};
|
||||||
`;
|
|
||||||
|
|
||||||
const StartButtonFooter = styled.div`
|
const standardSettings: Pick<
|
||||||
position: fixed;
|
InitialGameSettings,
|
||||||
bottom: 1rem;
|
'numberOfPlayers' | 'startingLifeTotal' | 'orientation'
|
||||||
translate: -50%, -50%;
|
> = {
|
||||||
z-index: 1;
|
numberOfPlayers: 2,
|
||||||
`;
|
startingLifeTotal: 20,
|
||||||
|
orientation: Orientation.Landscape,
|
||||||
|
};
|
||||||
|
|
||||||
const ToggleButtonsWrapper = styled.div`
|
const MainWrapper = twc.div`h-fit w-full pb-24 overflow-hidden items-center flex flex-col min-[349px]:pb-10`;
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ToggleContainer = styled.div`
|
const StartButtonFooter = twc.div`w-full max-w-[548px] fixed bottom-4 z-1 items-center flex flex-row flex-wrap px-4 z-10 gap-4`;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const playerMarks = [
|
const SliderWrapper = twc.div`mx-8 relative`;
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
label: '1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 2,
|
|
||||||
label: '2',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 3,
|
|
||||||
label: '3',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 4,
|
|
||||||
label: '4',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 5,
|
|
||||||
label: '5',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 6,
|
|
||||||
label: '6',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const healthMarks = [
|
const ToggleButtonsWrapper = twc.div`flex flex-row justify-between items-center`;
|
||||||
{
|
|
||||||
value: 20,
|
export const LabelText = twc.div`text-md text-text-primary font-medium`;
|
||||||
label: '20',
|
|
||||||
},
|
let tracked = false;
|
||||||
{
|
|
||||||
value: 30,
|
|
||||||
label: '30',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 40,
|
|
||||||
label: '40',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 50,
|
|
||||||
label: '50',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 60,
|
|
||||||
label: '60',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Start = () => {
|
const Start = () => {
|
||||||
const { setPlayers } = usePlayers();
|
const { setPlayers } = usePlayers();
|
||||||
@@ -107,27 +59,115 @@ const Start = () => {
|
|||||||
setInitialGameSettings,
|
setInitialGameSettings,
|
||||||
settings,
|
settings,
|
||||||
isPWA,
|
isPWA,
|
||||||
|
setRandomizingPlayer,
|
||||||
|
version,
|
||||||
|
setPlaying,
|
||||||
|
savedGame,
|
||||||
|
saveCurrentGame,
|
||||||
|
setGameScore,
|
||||||
} = useGlobalSettings();
|
} = useGlobalSettings();
|
||||||
|
|
||||||
const [openInfoModal, setOpenInfoModal] = useState(false);
|
const infoDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||||
const [openSettingsModal, setOpenSettingsModal] = useState(false);
|
const settingsDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||||
|
const playersSliderRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
const healthSliderRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
const [playerOptions, setPlayerOptions] = useState<InitialGameSettings>(
|
const [playerOptions, setPlayerOptions] = useState<InitialGameSettings>(
|
||||||
initialGameSettings || {
|
initialGameSettings || defaultInitialGameSettings
|
||||||
numberOfPlayers: 4,
|
|
||||||
startingLifeTotal: 40,
|
|
||||||
useCommanderDamage: true,
|
|
||||||
orientation: Orientation.Portrait,
|
|
||||||
gameFormat: 'commander',
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const doStartGame = () => {
|
// Check for new version on mount
|
||||||
|
useEffect(() => {
|
||||||
|
if (!tracked) {
|
||||||
|
version.checkForNewVersion('start_menu');
|
||||||
|
|
||||||
|
tracked = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!playersSliderRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let progress = 0;
|
||||||
|
|
||||||
|
switch (playerOptions?.numberOfPlayers) {
|
||||||
|
case 1:
|
||||||
|
progress = 0;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
progress = 20;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
progress = 40;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
progress = 60;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
progress = 80;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
progress = 100;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
playersSliderRef.current.style.background = `linear-gradient(to right, ${baseColors.secondary.main} ${progress}%, ${baseColors.secondary.dark} ${progress}%)`;
|
||||||
|
}, [playerOptions?.numberOfPlayers]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!healthSliderRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let progress = 0;
|
||||||
|
switch (playerOptions?.startingLifeTotal) {
|
||||||
|
case 20:
|
||||||
|
progress = 0;
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
progress = 25;
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
progress = 50;
|
||||||
|
break;
|
||||||
|
case 50:
|
||||||
|
progress = 75;
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
progress = 100;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
healthSliderRef.current.style.background = `linear-gradient(to right, ${baseColors.secondary.main} ${progress}%, ${baseColors.secondary.dark} ${progress}%)`;
|
||||||
|
}, [playerOptions?.startingLifeTotal]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setInitialGameSettings(playerOptions);
|
||||||
|
}, [playerOptions, setInitialGameSettings]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setPlayerOptions({
|
||||||
|
...playerOptions,
|
||||||
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [playerOptions.numberOfPlayers]);
|
||||||
|
|
||||||
|
const doStartNewGame = () => {
|
||||||
if (!initialGameSettings) {
|
if (!initialGameSettings) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
analytics.trackEvent('game_started', { ...initialGameSettings });
|
analytics.trackEvent('game_started', {
|
||||||
|
...initialGameSettings,
|
||||||
|
...settings,
|
||||||
|
isPWA,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (settings.goFullscreenOnStart) {
|
if (settings.goFullscreenOnStart) {
|
||||||
@@ -143,182 +183,265 @@ const Start = () => {
|
|||||||
|
|
||||||
setInitialGameSettings(initialGameSettings);
|
setInitialGameSettings(initialGameSettings);
|
||||||
setPlayers(createInitialPlayers(initialGameSettings));
|
setPlayers(createInitialPlayers(initialGameSettings));
|
||||||
|
setRandomizingPlayer(settings.preStartMode === PreStartMode.RandomKing);
|
||||||
setShowPlay(true);
|
setShowPlay(true);
|
||||||
localStorage.setItem('playing', 'false');
|
setPlaying(false);
|
||||||
localStorage.setItem('showPlay', 'true');
|
tracked = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const doResumeGame = () => {
|
||||||
setInitialGameSettings(playerOptions);
|
if (!savedGame) {
|
||||||
}, [playerOptions, setInitialGameSettings]);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const valuetext = (value: number) => {
|
analytics.trackEvent('game_resumed', {
|
||||||
return `${value}`;
|
...initialGameSettings,
|
||||||
};
|
...settings,
|
||||||
|
isPWA,
|
||||||
useEffect(() => {
|
|
||||||
setPlayerOptions({
|
|
||||||
...playerOptions,
|
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [playerOptions.numberOfPlayers]);
|
try {
|
||||||
|
if (settings.goFullscreenOnStart) {
|
||||||
|
fullscreen.enableFullscreen();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.keepAwake && !wakeLock.active) {
|
||||||
|
wakeLock.request();
|
||||||
|
}
|
||||||
|
|
||||||
|
setInitialGameSettings(savedGame.initialGameSettings);
|
||||||
|
setPlayers(savedGame.players);
|
||||||
|
if (savedGame.gameScore) {
|
||||||
|
setGameScore(savedGame.gameScore);
|
||||||
|
}
|
||||||
|
saveCurrentGame(null);
|
||||||
|
setRandomizingPlayer(false);
|
||||||
|
setShowPlay(true);
|
||||||
|
setPlaying(true);
|
||||||
|
tracked = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const openInfo = () => {
|
||||||
|
if (infoDialogRef.current) {
|
||||||
|
infoDialogRef.current.showModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openSettings = () => {
|
||||||
|
if (settingsDialogRef.current) {
|
||||||
|
settingsDialogRef.current.showModal();
|
||||||
|
version.checkForNewVersion('settings');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainWrapper>
|
<>
|
||||||
<Info
|
<InfoDialog dialogRef={infoDialogRef} />
|
||||||
color={theme.palette.primary.light}
|
{settings.showAnimations && (
|
||||||
size="2rem"
|
<>
|
||||||
style={{ position: 'absolute', top: '1rem', left: '1rem' }}
|
<div className="spotlight1" />
|
||||||
onClick={() => {
|
<div className="spotlight2" />
|
||||||
setOpenInfoModal(!openInfoModal);
|
</>
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<InfoModal
|
|
||||||
closeModal={() => {
|
|
||||||
setOpenInfoModal(false);
|
|
||||||
}}
|
|
||||||
isOpen={openInfoModal}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingsModal
|
|
||||||
closeModal={() => {
|
|
||||||
setOpenSettingsModal(false);
|
|
||||||
}}
|
|
||||||
isOpen={openSettingsModal}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SupportMe />
|
|
||||||
|
|
||||||
<H1>Life Trinket</H1>
|
|
||||||
<FormControl focused={false} style={{ width: '80vw' }}>
|
|
||||||
<FormLabel>Number of Players</FormLabel>
|
|
||||||
<Slider
|
|
||||||
title="Number of Players"
|
|
||||||
max={6}
|
|
||||||
min={1}
|
|
||||||
aria-label="Custom marks"
|
|
||||||
value={playerOptions?.numberOfPlayers ?? 4}
|
|
||||||
getAriaValueText={valuetext}
|
|
||||||
step={null}
|
|
||||||
marks={playerMarks}
|
|
||||||
onChange={(_e, value) => {
|
|
||||||
setPlayerOptions({
|
|
||||||
...playerOptions,
|
|
||||||
numberOfPlayers: value as number,
|
|
||||||
orientation: Orientation.Landscape,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Spacer height="0.7rem" />
|
|
||||||
<FormLabel>Starting Health</FormLabel>
|
|
||||||
<Slider
|
|
||||||
title="Starting Health"
|
|
||||||
max={60}
|
|
||||||
min={20}
|
|
||||||
aria-label="Custom marks"
|
|
||||||
value={playerOptions?.startingLifeTotal ?? 40}
|
|
||||||
getAriaValueText={valuetext}
|
|
||||||
step={10}
|
|
||||||
marks={healthMarks}
|
|
||||||
onChange={(_e, value) =>
|
|
||||||
setPlayerOptions({
|
|
||||||
...playerOptions,
|
|
||||||
startingLifeTotal: value as number,
|
|
||||||
orientation: Orientation.Landscape,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Spacer height="1rem" />
|
|
||||||
|
|
||||||
<ToggleButtonsWrapper>
|
|
||||||
<ToggleContainer>
|
|
||||||
<FormLabel>Commander</FormLabel>
|
|
||||||
<Switch
|
|
||||||
checked={
|
|
||||||
playerOptions.useCommanderDamage ??
|
|
||||||
initialGameSettings?.useCommanderDamage ??
|
|
||||||
true
|
|
||||||
}
|
|
||||||
onChange={(_e, value) => {
|
|
||||||
if (value) {
|
|
||||||
setPlayerOptions({
|
|
||||||
...playerOptions,
|
|
||||||
useCommanderDamage: value,
|
|
||||||
numberOfPlayers: 4,
|
|
||||||
startingLifeTotal: 40,
|
|
||||||
orientation: Orientation.Landscape,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setPlayerOptions({
|
|
||||||
...playerOptions,
|
|
||||||
useCommanderDamage: value,
|
|
||||||
numberOfPlayers: 2,
|
|
||||||
startingLifeTotal: 20,
|
|
||||||
orientation: Orientation.Landscape,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ToggleContainer>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
style={{ height: '2rem' }}
|
|
||||||
onClick={() => {
|
|
||||||
setOpenSettingsModal(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Cog /> Other settings
|
|
||||||
</Button>
|
|
||||||
</ToggleButtonsWrapper>
|
|
||||||
|
|
||||||
<FormLabel>Layout</FormLabel>
|
|
||||||
{/* <LayoutOptions
|
|
||||||
numberOfPlayers={playerOptions.numberOfPlayers}
|
|
||||||
gridAreas={playerOptions.gridAreas}
|
|
||||||
onChange={(gridAreas) =>
|
|
||||||
setPlayerOptions({
|
|
||||||
...playerOptions,
|
|
||||||
gridAreas,
|
|
||||||
//TODO fix the layout selection
|
|
||||||
orientation: Orientation.Portrait,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/> */}
|
|
||||||
<LayoutOptions
|
|
||||||
numberOfPlayers={playerOptions.numberOfPlayers}
|
|
||||||
selectedOrientation={playerOptions.orientation}
|
|
||||||
onChange={(orientation) => {
|
|
||||||
console.log('orientation', { orientation });
|
|
||||||
setPlayerOptions({
|
|
||||||
...playerOptions,
|
|
||||||
orientation,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
{!isPWA && (
|
|
||||||
<Paragraph
|
|
||||||
style={{ textAlign: 'center', maxWidth: '75%', fontSize: '0.7rem' }}
|
|
||||||
>
|
|
||||||
If you're on iOS, this page works better if you{' '}
|
|
||||||
<strong>hide the toolbar</strong> or{' '}
|
|
||||||
<strong>add the app to your home screen</strong>.
|
|
||||||
</Paragraph>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<StartButtonFooter>
|
<SettingsDialog dialogRef={settingsDialogRef} />
|
||||||
<Button
|
<div className="flex justify-center items-center w-screen">
|
||||||
size="large"
|
<MainWrapper>
|
||||||
variant="contained"
|
<Info
|
||||||
onClick={doStartGame}
|
className="size-8 absolute top-7 left-4 text-primary-main"
|
||||||
style={{ width: '90dvw' }}
|
onClick={() => {
|
||||||
>
|
openInfo();
|
||||||
START GAME
|
}}
|
||||||
</Button>
|
/>
|
||||||
</StartButtonFooter>
|
<a href="https://lifetrinket.com/">
|
||||||
</MainWrapper>
|
<Trinket className="absolute w-12 h-12 top-4 right-4" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<h1 className="relative flex flex-col text-3xl font-bold mt-6 mb-6 text-text-primary justify-center items-center">
|
||||||
|
<div className="flex flex-row items-center">Life Trinket</div>
|
||||||
|
<div className="h-[1px] w-[120%] bg-common-white opacity-50" />
|
||||||
|
<div className="flex absolute text-xs font-medium -bottom-4">
|
||||||
|
v{version.installedVersion}
|
||||||
|
</div>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div className="overflow-hidden items-center flex flex-col max-w-[548px] w-full mb-8 px-4">
|
||||||
|
<div className="w-full">
|
||||||
|
<ToggleButtonsWrapper className="mt-4">
|
||||||
|
<ToggleButton
|
||||||
|
label="Commander"
|
||||||
|
checked={
|
||||||
|
playerOptions.useCommanderDamage ??
|
||||||
|
initialGameSettings?.useCommanderDamage ??
|
||||||
|
true
|
||||||
|
}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
setPlayerOptions({
|
||||||
|
...playerOptions,
|
||||||
|
useCommanderDamage: e.target.checked,
|
||||||
|
...commanderSettings,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setPlayerOptions({
|
||||||
|
...playerOptions,
|
||||||
|
useCommanderDamage: e.target.checked,
|
||||||
|
...standardSettings,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex flex-nowrap text-nowrap relative justify-center items-start">
|
||||||
|
<button
|
||||||
|
className="flex justify-center self-center items-center mt-1 mb-1 bg-primary-main px-3 py-2 rounded-md transition-colors duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
|
||||||
|
onClick={openSettings}
|
||||||
|
>
|
||||||
|
<span className="text-sm flex flex-row items-center text-text-primary font-bold">
|
||||||
|
<Cog />
|
||||||
|
Game Settings
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div
|
||||||
|
data-not-latest-version={
|
||||||
|
!version.isLatest && !!version.remoteVersion
|
||||||
|
}
|
||||||
|
className="absolute flex justify-center text-text-primary text-xxs -bottom-5 bg-primary-dark px-2 rounded-md
|
||||||
|
opacity-0 transition-all duration-200 delay-500
|
||||||
|
data-[not-latest-version=true]:opacity-100
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div className="absolute bg-primary-dark rotate-45 size-2 -top-[2px] z-0" />
|
||||||
|
<span className="z-10">
|
||||||
|
v{version.remoteVersion} available!
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ToggleButtonsWrapper>
|
||||||
|
<LabelText className="mt-4">Number of Players</LabelText>
|
||||||
|
<SliderWrapper>
|
||||||
|
<input
|
||||||
|
ref={playersSliderRef}
|
||||||
|
className="accent-primary-main text-primary-dark w-full h-3 rounded-lg cursor-pointer"
|
||||||
|
title="Number of Players"
|
||||||
|
type="range"
|
||||||
|
max={6}
|
||||||
|
min={1}
|
||||||
|
value={playerOptions?.numberOfPlayers ?? 4}
|
||||||
|
onChange={(e) => {
|
||||||
|
setPlayerOptions({
|
||||||
|
...playerOptions,
|
||||||
|
numberOfPlayers: Number.parseInt(e.target.value),
|
||||||
|
orientation: Orientation.Landscape,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="flex w-full justify-between px-[6px] text-text-primary pointer-events-none">
|
||||||
|
<div>1</div>
|
||||||
|
<div>2</div>
|
||||||
|
<div>3</div>
|
||||||
|
<div>4</div>
|
||||||
|
<div>5</div>
|
||||||
|
<div>6</div>
|
||||||
|
</div>
|
||||||
|
</SliderWrapper>
|
||||||
|
|
||||||
|
<LabelText className="mt-4">Starting Health</LabelText>
|
||||||
|
<SliderWrapper>
|
||||||
|
<input
|
||||||
|
ref={healthSliderRef}
|
||||||
|
className="accent-primary-main text-primary-dark w-full h-3 rounded-lg cursor-pointer"
|
||||||
|
title="Starting Health"
|
||||||
|
type="range"
|
||||||
|
max={60}
|
||||||
|
min={20}
|
||||||
|
aria-label="Custom marks"
|
||||||
|
value={playerOptions?.startingLifeTotal ?? 40}
|
||||||
|
step={10}
|
||||||
|
onChange={(e) =>
|
||||||
|
setPlayerOptions({
|
||||||
|
...playerOptions,
|
||||||
|
startingLifeTotal: Number.parseInt(e.target.value),
|
||||||
|
orientation: Orientation.Landscape,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div className="flex w-full justify-between text-text-primary pointer-events-none">
|
||||||
|
<div>20</div>
|
||||||
|
<div>30</div>
|
||||||
|
<div>40</div>
|
||||||
|
<div>50</div>
|
||||||
|
<div>60</div>
|
||||||
|
</div>
|
||||||
|
</SliderWrapper>
|
||||||
|
|
||||||
|
<LabelText className="mt-4">Layout</LabelText>
|
||||||
|
<LayoutOptions
|
||||||
|
numberOfPlayers={playerOptions.numberOfPlayers}
|
||||||
|
selectedOrientation={playerOptions.orientation}
|
||||||
|
onChange={(orientation) => {
|
||||||
|
setPlayerOptions({
|
||||||
|
...playerOptions,
|
||||||
|
orientation,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{!isPWA && window.isIOS && (
|
||||||
|
<p className="text-center text-xs text-text-primary w-11/12 mt-4">
|
||||||
|
If you're on iOS, this page works better if you{' '}
|
||||||
|
<strong>hide the toolbar</strong> or{' '}
|
||||||
|
<strong>add the app to your home screen</strong>.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<StartButtonFooter>
|
||||||
|
<button
|
||||||
|
className="flex flex-grow basis-0 justify-center self-center items-center bg-primary-main px-3 py-2 rounded-md text-text-primary min-w-[150px] duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark font-bold"
|
||||||
|
onClick={doStartNewGame}
|
||||||
|
>
|
||||||
|
NEW GAME
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{savedGame && (
|
||||||
|
<button
|
||||||
|
className="flex flex-grow basis-0 justify-center self-center items-center bg-secondary-main px-3 py-2 rounded-md text-text-primary min-w-[150px]
|
||||||
|
|
||||||
|
duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-secondary-dark font-bold"
|
||||||
|
onClick={doResumeGame}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<div>
|
||||||
|
RESUME
|
||||||
|
<span className="text-xs">
|
||||||
|
({savedGame.players.length}
|
||||||
|
{savedGame.players.length > 1 ? 'players' : 'player'})
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{savedGame.gameScore && Object.keys(savedGame.gameScore).length > 0 && (
|
||||||
|
<div className="text-xs opacity-75">
|
||||||
|
Score: {Object.entries(savedGame.gameScore)
|
||||||
|
.map(([playerIndex, score]) => {
|
||||||
|
const player = savedGame.players.find(
|
||||||
|
(p) => p.index === Number(playerIndex)
|
||||||
|
);
|
||||||
|
return `${player?.name || `P${Number(playerIndex) + 1}`}: ${score}`;
|
||||||
|
})
|
||||||
|
.join(' | ')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</StartButtonFooter>
|
||||||
|
</MainWrapper>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,23 @@
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
import { InitialGameSettings, Settings } from '../Types/Settings';
|
import { InitialGameSettings, Settings } from '../Types/Settings';
|
||||||
|
import { Player } from '../Types/Player';
|
||||||
|
|
||||||
|
type Version = {
|
||||||
|
installedVersion: string;
|
||||||
|
isLatest: boolean;
|
||||||
|
checkForNewVersion: (source: 'settings' | 'start_menu') => Promise<void>;
|
||||||
|
remoteVersion?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SavedGame = {
|
||||||
|
initialGameSettings: InitialGameSettings;
|
||||||
|
players: Player[];
|
||||||
|
gameScore?: GameScore;
|
||||||
|
} | null;
|
||||||
|
|
||||||
|
export type GameScore = {
|
||||||
|
[playerIndex: number]: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type GlobalSettingsContextType = {
|
export type GlobalSettingsContextType = {
|
||||||
fullscreen: {
|
fullscreen: {
|
||||||
@@ -18,11 +36,23 @@ export type GlobalSettingsContextType = {
|
|||||||
goToStart: () => void;
|
goToStart: () => void;
|
||||||
showPlay: boolean;
|
showPlay: boolean;
|
||||||
setShowPlay: (showPlay: boolean) => void;
|
setShowPlay: (showPlay: boolean) => void;
|
||||||
initialGameSettings: InitialGameSettings | null;
|
initialGameSettings: InitialGameSettings;
|
||||||
setInitialGameSettings: (initialGameSettings: InitialGameSettings) => void;
|
setInitialGameSettings: (initialGameSettings: InitialGameSettings) => void;
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
setSettings: (settings: Settings) => void;
|
setSettings: (settings: Settings) => void;
|
||||||
|
playing: boolean;
|
||||||
|
setPlaying: (playing: boolean) => void;
|
||||||
|
randomizingPlayer: boolean;
|
||||||
|
setRandomizingPlayer: (stopRandom: boolean) => void;
|
||||||
isPWA: boolean;
|
isPWA: boolean;
|
||||||
|
preStartCompleted: boolean;
|
||||||
|
setPreStartCompleted: (completed: boolean) => void;
|
||||||
|
version: Version;
|
||||||
|
savedGame: SavedGame;
|
||||||
|
saveCurrentGame: (currentGame: SavedGame) => void;
|
||||||
|
gameScore: GameScore;
|
||||||
|
setGameScore: (score: GameScore) => void;
|
||||||
|
resetGameScore: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GlobalSettingsContext =
|
export const GlobalSettingsContext =
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export type PlayersContextType = {
|
|||||||
updatePlayer: (updatedPlayer: Player) => void;
|
updatePlayer: (updatedPlayer: Player) => void;
|
||||||
updateLifeTotal: (player: Player, updatedLifeTotal: number) => number;
|
updateLifeTotal: (player: Player, updatedLifeTotal: number) => number;
|
||||||
resetCurrentGame: () => void;
|
resetCurrentGame: () => void;
|
||||||
|
startingPlayerIndex: number;
|
||||||
|
setStartingPlayerIndex: (index: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PlayersContext = createContext<PlayersContextType | null>(null);
|
export const PlayersContext = createContext<PlayersContextType | null>(null);
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { Player, Rotation } from '../Types/Player';
|
import { Player, Rotation } from '../Types/Player';
|
||||||
import { InitialGameSettings, Orientation } from '../Types/Settings';
|
import { InitialGameSettings, Orientation } from '../Types/Settings';
|
||||||
|
import { checkContrast } from '../Utils/checkContrast';
|
||||||
|
|
||||||
const presetColors = [
|
export const presetColors = [
|
||||||
'#F06292', // Light Pink
|
'#D08182', // Muted Pink
|
||||||
'#4DB6AC', // Teal
|
'#5AAB9E', // Teal
|
||||||
'#FFA726', // Orange
|
'#D58B5A', // Burnt Orange
|
||||||
'#7986CB', // Indigo
|
'#697A9A', // Soft Indigo
|
||||||
'#FFCC80', // Peach
|
'#78B2D3', // Pastel Blue
|
||||||
'#90CAF9', // Pastel Blue
|
'#A785BA', // Lilac
|
||||||
'#CE93D8', // Lilac
|
'#D9C17C', // Muted Yellow
|
||||||
'#FF8A80', // Coral
|
'#FF907F', // Light Coral
|
||||||
];
|
];
|
||||||
|
|
||||||
const getOrientationRotations = (
|
const getOrientationRotations = (
|
||||||
@@ -127,15 +128,15 @@ const getOrientationRotations = (
|
|||||||
case Orientation.Portrait:
|
case Orientation.Portrait:
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
return Rotation.Side;
|
return Rotation.Flipped;
|
||||||
case 1:
|
case 1:
|
||||||
return Rotation.Side;
|
return Rotation.Flipped;
|
||||||
case 2:
|
case 2:
|
||||||
return Rotation.SideFlipped;
|
return Rotation.Side;
|
||||||
case 3:
|
case 3:
|
||||||
return Rotation.SideFlipped;
|
return Rotation.Normal;
|
||||||
case 4:
|
case 4:
|
||||||
return Rotation.SideFlipped;
|
return Rotation.Normal;
|
||||||
default:
|
default:
|
||||||
return Rotation.Normal;
|
return Rotation.Normal;
|
||||||
}
|
}
|
||||||
@@ -163,17 +164,17 @@ const getOrientationRotations = (
|
|||||||
case Orientation.Portrait:
|
case Orientation.Portrait:
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
return Rotation.Side;
|
return Rotation.SideFlipped;
|
||||||
case 1:
|
case 1:
|
||||||
return Rotation.Side;
|
return Rotation.Flipped;
|
||||||
case 2:
|
case 2:
|
||||||
return Rotation.Side;
|
return Rotation.Flipped;
|
||||||
case 3:
|
case 3:
|
||||||
return Rotation.SideFlipped;
|
return Rotation.Side;
|
||||||
case 4:
|
case 4:
|
||||||
return Rotation.SideFlipped;
|
return Rotation.Normal;
|
||||||
case 5:
|
case 5:
|
||||||
return Rotation.SideFlipped;
|
return Rotation.Normal;
|
||||||
default:
|
default:
|
||||||
return Rotation.Normal;
|
return Rotation.Normal;
|
||||||
}
|
}
|
||||||
@@ -191,10 +192,8 @@ export const createInitialPlayers = ({
|
|||||||
}: InitialGameSettings): Player[] => {
|
}: InitialGameSettings): Player[] => {
|
||||||
const players: Player[] = [];
|
const players: Player[] = [];
|
||||||
const availableColors = [...presetColors]; // Create a copy of the colors array
|
const availableColors = [...presetColors]; // Create a copy of the colors array
|
||||||
const firstPlayerIndex = Math.floor(Math.random() * numberOfPlayers);
|
|
||||||
|
|
||||||
for (let i = 0; i <= numberOfPlayers - 1; i++) {
|
for (let i = 0; i <= numberOfPlayers - 1; i++) {
|
||||||
const isStartingPlayer = i === firstPlayerIndex;
|
|
||||||
const colorIndex = Math.floor(Math.random() * availableColors.length);
|
const colorIndex = Math.floor(Math.random() * availableColors.length);
|
||||||
const color = availableColors[colorIndex];
|
const color = availableColors[colorIndex];
|
||||||
|
|
||||||
@@ -216,6 +215,8 @@ export const createInitialPlayers = ({
|
|||||||
lifeTotal: startingLifeTotal,
|
lifeTotal: startingLifeTotal,
|
||||||
index: i,
|
index: i,
|
||||||
color,
|
color,
|
||||||
|
iconTheme:
|
||||||
|
checkContrast(color, '#00000080') === 'Fail' ? 'light' : 'dark',
|
||||||
settings: {
|
settings: {
|
||||||
useCommanderDamage,
|
useCommanderDamage,
|
||||||
usePartner: false,
|
usePartner: false,
|
||||||
@@ -224,11 +225,13 @@ export const createInitialPlayers = ({
|
|||||||
usePoison: false,
|
usePoison: false,
|
||||||
rotation,
|
rotation,
|
||||||
},
|
},
|
||||||
isStartingPlayer,
|
|
||||||
showStartingPlayer: isStartingPlayer,
|
|
||||||
extraCounters: [],
|
extraCounters: [],
|
||||||
commanderDamage,
|
commanderDamage,
|
||||||
hasLost: false,
|
hasLost: false,
|
||||||
|
isStartingPlayer: false,
|
||||||
|
isSide: rotation === Rotation.Side || rotation === Rotation.SideFlipped,
|
||||||
|
name: '',
|
||||||
|
isMonarch: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
players.push(player);
|
players.push(player);
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
import { createTheme } from '@mui/material';
|
|
||||||
|
|
||||||
export const theme = createTheme({
|
|
||||||
palette: {
|
|
||||||
primary: {
|
|
||||||
main: '#7F9172',
|
|
||||||
},
|
|
||||||
secondary: {
|
|
||||||
main: '#5E714C',
|
|
||||||
},
|
|
||||||
background: {
|
|
||||||
default: '#495E35',
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
primary: '#F5F5F5',
|
|
||||||
secondary: '#b3b39b',
|
|
||||||
},
|
|
||||||
action: {
|
|
||||||
disabled: '#5E714C',
|
|
||||||
},
|
|
||||||
common: {
|
|
||||||
white: '#F9FFE3',
|
|
||||||
black: '#000000',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
MuiFormLabel: {
|
|
||||||
styleOverrides: {
|
|
||||||
root: {
|
|
||||||
fontSize: '1rem',
|
|
||||||
color: '#F5F5F5',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiSlider: {
|
|
||||||
styleOverrides: {
|
|
||||||
markLabel: {
|
|
||||||
fontSize: '1rem',
|
|
||||||
color: '#F5F5F5',
|
|
||||||
},
|
|
||||||
valueLabel: {
|
|
||||||
display: 'none',
|
|
||||||
color: '#F5F5F5',
|
|
||||||
background: '#5E714C',
|
|
||||||
},
|
|
||||||
track: {
|
|
||||||
height: '0.7rem',
|
|
||||||
},
|
|
||||||
rail: {
|
|
||||||
height: '0.7rem',
|
|
||||||
},
|
|
||||||
mark: {
|
|
||||||
width: '0.5rem',
|
|
||||||
height: '0.5rem',
|
|
||||||
borderRadius: '50%',
|
|
||||||
display: 'none',
|
|
||||||
},
|
|
||||||
thumb: {
|
|
||||||
width: '1.3rem',
|
|
||||||
height: '1.3rem',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiFormControlLabel: {
|
|
||||||
styleOverrides: {
|
|
||||||
root: {
|
|
||||||
margin: '0',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiFormGroup: {
|
|
||||||
styleOverrides: {
|
|
||||||
root: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiDrawer: {
|
|
||||||
styleOverrides: {
|
|
||||||
paper: {
|
|
||||||
top: '1rem',
|
|
||||||
background: '#495E35',
|
|
||||||
height: 'auto',
|
|
||||||
borderRadius: '8px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiBackdrop: {
|
|
||||||
styleOverrides: {
|
|
||||||
root: {
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiButton: {
|
|
||||||
styleOverrides: {
|
|
||||||
root: {
|
|
||||||
textTransform: 'none',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiSwitch: {
|
|
||||||
styleOverrides: {
|
|
||||||
colorPrimary: {
|
|
||||||
color: '#5E714C',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,24 +1,53 @@
|
|||||||
import { initializeApp } from 'firebase/app';
|
import { initializeApp } from 'firebase/app';
|
||||||
import { getAnalytics, logEvent } from 'firebase/analytics';
|
import { Analytics, getAnalytics, logEvent } from 'firebase/analytics';
|
||||||
|
|
||||||
const firebaseConfig = {
|
const firebaseConfig = {
|
||||||
apiKey: 'AIzaSyCZ1AHMb5zmWS4VoRnC-OBxTswUfrJ0mlY',
|
apiKey: 'AIzaSyCZ1AHMb5zmWS4VoRnC-OBxTswUfrJ0mlY',
|
||||||
authDomain: 'life-trinket.firebaseapp.com',
|
authDomain: 'life-trinket.firebaseapp.com',
|
||||||
projectId: 'life-trinket',
|
projectId: 'life-trinket',
|
||||||
storageBucket: 'life-trinket.appspot.com',
|
storageBucket: 'life-trinket.firebasestorage.app',
|
||||||
messagingSenderId: '508011650619',
|
messagingSenderId: '508011650619',
|
||||||
appId: '1:508011650619:web:bdc7d0b6f8707b1f9e861e',
|
appId: '1:508011650619:web:bdc7d0b6f8707b1f9e861e',
|
||||||
|
measurementId: 'G-BE86QSSG14',
|
||||||
};
|
};
|
||||||
|
|
||||||
const app = initializeApp(firebaseConfig);
|
const app = initializeApp(firebaseConfig);
|
||||||
const analytics = getAnalytics(app);
|
let analytics: Analytics | null = null;
|
||||||
|
|
||||||
|
const getAnalyticsInstance = () => {
|
||||||
|
if (!analytics) {
|
||||||
|
try {
|
||||||
|
analytics = getAnalytics(app);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize Firebase Analytics:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return analytics;
|
||||||
|
};
|
||||||
|
|
||||||
export const useAnalytics = () => {
|
export const useAnalytics = () => {
|
||||||
const trackEvent = (
|
const trackEvent = (
|
||||||
eventName: string,
|
eventName: string,
|
||||||
eventParams?: { [key: string]: unknown }
|
eventParams?: { [key: string]: unknown }
|
||||||
) => {
|
) => {
|
||||||
logEvent(analytics, eventName, eventParams);
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
console.info('Event not tracked:', { eventName, eventParams });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const analyticsInstance = getAnalyticsInstance();
|
||||||
|
if (!analyticsInstance) {
|
||||||
|
console.warn('Analytics not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const paramsWithVersion = {
|
||||||
|
...eventParams,
|
||||||
|
app_version: import.meta.env.VITE_APP_VERSION,
|
||||||
|
};
|
||||||
|
|
||||||
|
logEvent(analyticsInstance, eventName, paramsWithVersion);
|
||||||
};
|
};
|
||||||
|
|
||||||
return { trackEvent };
|
return { trackEvent };
|
||||||
|
|||||||
52
src/Hooks/useOrientation.ts
Normal file
52
src/Hooks/useOrientation.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export interface OrientationState {
|
||||||
|
angle: number;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultState: OrientationState = {
|
||||||
|
angle: 0,
|
||||||
|
type: 'landscape-primary',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function useOrientation(
|
||||||
|
initialState: OrientationState = defaultState
|
||||||
|
) {
|
||||||
|
const [state, setState] = useState(initialState);
|
||||||
|
const [isLandscape, setIsLandscape] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const screen = window.screen;
|
||||||
|
let mounted = true;
|
||||||
|
|
||||||
|
const onChange = () => {
|
||||||
|
if (mounted) {
|
||||||
|
const { orientation } = screen;
|
||||||
|
|
||||||
|
if (orientation) {
|
||||||
|
const { angle, type } = orientation;
|
||||||
|
setState({ angle, type });
|
||||||
|
if (type.includes('landscape')) {
|
||||||
|
setIsLandscape(true);
|
||||||
|
} else if (type.includes('portrait')) {
|
||||||
|
setIsLandscape(false);
|
||||||
|
}
|
||||||
|
} else if (window.orientation !== undefined) {
|
||||||
|
setState({
|
||||||
|
angle:
|
||||||
|
typeof window.orientation === 'number' ? window.orientation : 0,
|
||||||
|
type: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
onChange();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mounted = false;
|
||||||
|
};
|
||||||
|
}, [isLandscape]);
|
||||||
|
|
||||||
|
return { state, isLandscape };
|
||||||
|
}
|
||||||
45
src/Icons/generated/Close.tsx
Normal file
45
src/Icons/generated/Close.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { SVGProps } from 'react';
|
||||||
|
interface SVGRProps {
|
||||||
|
title?: string;
|
||||||
|
titleId?: string;
|
||||||
|
size?: string;
|
||||||
|
}
|
||||||
|
const Close = ({
|
||||||
|
title,
|
||||||
|
titleId,
|
||||||
|
...props
|
||||||
|
}: SVGProps<SVGSVGElement> & SVGRProps) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={props.size || 16}
|
||||||
|
height={props.size || 16}
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 52 52"
|
||||||
|
aria-labelledby={titleId}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M26 48c12.15 0 22-9.85 22-22S38.15 4 26 4 4 13.85 4 26s9.85 22 22 22m0 4c14.36 0 26-11.64 26-26S40.36 0 26 0 0 11.64 0 26s11.64 26 26 26"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M15.586 15.586a2 2 0 0 1 2.828 0l18 18a2 2 0 1 1-2.828 2.828l-18-18a2 2 0 0 1 0-2.828"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M36.414 15.586a2 2 0 0 1 0 2.828l-18 18a2 2 0 1 1-2.828-2.828l18-18a2 2 0 0 1 2.828 0"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
Close.propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
};
|
||||||
|
export default Close;
|
||||||
@@ -22,7 +22,7 @@ const Cog = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M80.7 1c-4.6 1.4-9.4 5.7-11.6 10.3-1.6 3.4-2.1 6.2-2.1 12.8v8.5l-5.7-5.4C48.2 15 38.1 15 26.3 27.1 14.9 38.9 15.2 49.2 27.4 61l5.7 5.5-10.1.6c-9 .5-10.4.8-14.2 3.5-2.3 1.6-5.2 4.9-6.6 7.4-2.1 4-2.3 5.5-2 14 .4 11.1 2.4 15.4 9 19.8 3.5 2.2 5.3 2.7 13.4 3l9.4.4-5.2 5.7c-11.6 12.7-11.8 22.4-.5 34 5.7 5.8 11.5 9 16.3 9.1 7 0 9.7-1.3 16.9-8.2l7.3-6.8.4 9.9c.3 8.6.7 10.3 3 13.8 1.5 2.2 4.4 5.1 6.5 6.4 3.4 2.1 5 2.4 14.3 2.4s10.9-.3 14.3-2.4c2.1-1.3 5-4.1 6.5-6.3 2.3-3.5 2.7-5.3 3-13.5l.4-9.5 4.2 4c7.3 6.9 11.1 9.2 16.4 9.9 7 .8 11.7-1.1 18.6-7.6 12.7-12.1 12.9-22.2.5-34.7l-6.1-6.2 9.9-.4c8.9-.3 10.2-.6 14.2-3.3 6.2-4.2 8.5-9.1 8.9-19.2.5-10.7-2.4-17.7-9-21.9-3.8-2.5-5.5-2.9-14.2-3.2l-9.8-.4 6.1-6.2c12.2-12.4 12.2-22.4 0-34.3-11.6-11.2-20.5-11.2-33.4.2l-6.3 5.5-.4-9.4c-.3-8.1-.8-9.9-3-13.4-4.4-6.6-8.7-8.6-19.3-8.9-4.9-.1-10.3.2-11.8.7zm24.4 59.9c5.9 3 12.1 9.2 15 14.9 2.1 4 2.4 6.1 2.4 14.7 0 8.5-.3 10.7-2.3 14.5-3.2 6.1-8.7 11.6-14.7 14.8-4.5 2.4-6.1 2.7-15 2.7s-10.5-.3-15-2.7c-6-3.2-11.5-8.7-14.7-14.8-3.1-6-3.7-18.6-1.3-25.9 2.7-8.3 12.1-17.4 20.6-20 6-1.8 19.9-.8 25 1.8z" />
|
<path d="M80.7 1c-4.6 1.4-9.4 5.7-11.6 10.3-1.6 3.4-2.1 6.2-2.1 12.8v8.5l-5.7-5.4C48.2 15 38.1 15 26.3 27.1 14.9 38.9 15.2 49.2 27.4 61l5.7 5.5-10.1.6c-9 .5-10.4.8-14.2 3.5-2.3 1.6-5.2 4.9-6.6 7.4-2.1 4-2.3 5.5-2 14 .4 11.1 2.4 15.4 9 19.8 3.5 2.2 5.3 2.7 13.4 3l9.4.4-5.2 5.7c-11.6 12.7-11.8 22.4-.5 34 5.7 5.8 11.5 9 16.3 9.1 7 0 9.7-1.3 16.9-8.2l7.3-6.8.4 9.9c.3 8.6.7 10.3 3 13.8 1.5 2.2 4.4 5.1 6.5 6.4 3.4 2.1 5 2.4 14.3 2.4s10.9-.3 14.3-2.4c2.1-1.3 5-4.1 6.5-6.3 2.3-3.5 2.7-5.3 3-13.5l.4-9.5 4.2 4c7.3 6.9 11.1 9.2 16.4 9.9 7 .8 11.7-1.1 18.6-7.6 12.7-12.1 12.9-22.2.5-34.7l-6.1-6.2 9.9-.4c8.9-.3 10.2-.6 14.2-3.3 6.2-4.2 8.5-9.1 8.9-19.2.5-10.7-2.4-17.7-9-21.9-3.8-2.5-5.5-2.9-14.2-3.2l-9.8-.4 6.1-6.2c12.2-12.4 12.2-22.4 0-34.3-11.6-11.2-20.5-11.2-33.4.2l-6.3 5.5-.4-9.4c-.3-8.1-.8-9.9-3-13.4-4.4-6.6-8.7-8.6-19.3-8.9-4.9-.1-10.3.2-11.8.7m24.4 59.9c5.9 3 12.1 9.2 15 14.9 2.1 4 2.4 6.1 2.4 14.7 0 8.5-.3 10.7-2.3 14.5-3.2 6.1-8.7 11.6-14.7 14.8-4.5 2.4-6.1 2.7-15 2.7s-10.5-.3-15-2.7c-6-3.2-11.5-8.7-14.7-14.8-3.1-6-3.7-18.6-1.3-25.9 2.7-8.3 12.1-17.4 20.6-20 6-1.8 19.9-.8 25 1.8" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,13 +16,16 @@ const CommanderTax = ({
|
|||||||
width={props.size || 16}
|
width={props.size || 16}
|
||||||
height={props.size || 16}
|
height={props.size || 16}
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
paintOrder="stroke fill markers"
|
viewBox="0 0 52 52"
|
||||||
viewBox="0 0 524 524"
|
|
||||||
aria-labelledby={titleId}
|
aria-labelledby={titleId}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M256 23.9c-1.9.4-5.5 1.8-7.9 3.2-5.1 2.8-130.3 133.3-135.8 141.5-5.4 8.1-7.5 14.7-8.1 25.5-.5 11.3.7 17.6 5.2 26.9 3.7 7.8 13.8 18.4 21.1 22.1 2.8 1.4 20.4 7.5 39.3 13.4 18.8 6 33.7 11.2 33 11.6-.7.3-24.2 9.3-52.3 19.9-32.8 12.4-52.9 20.5-56.3 22.7-6 4-12 10.7-15.6 17.4-1.5 2.7-9.7 31.1-20.6 71.5C41.3 461.3 39.9 467 40.3 473c.9 12.3 7.2 21.7 18.5 27.2l6.7 3.3H262c190.8 0 196.7-.1 202-1.9 7.5-2.6 15.9-11 18.8-18.9 4.1-10.8 3.9-11.8-15.3-81.5-9.6-35-18.7-66.6-20.2-70.1-3.4-8.3-13-18.6-21-22.8-3.2-1.7-27.9-11.5-55-21.9-27.1-10.4-48.9-19.2-48.5-19.5.4-.4 15.1-5.3 32.7-10.8 17.6-5.6 34.5-11.4 37.5-12.9 11.4-5.6 23-20.5 25.9-33.2 2.7-11.5.7-27.5-4.7-37.3-3.6-6.7-5.2-8.5-73.7-80.2-45.1-47.2-61.4-63.7-65.1-65.7-5.3-2.8-13.7-4.1-19.4-2.9zm32.5 97.6c9.3 1.8 31.6 8.3 32.9 9.5 1.6 1.5-12.1 43.6-18.3 56.5-6 12.3-16.1 27.7-22.6 34.7-7.3 7.7-16.8 10.3-26.6 7.5-7.3-2.2-11.4-5.8-19.6-17.4-16.1-22.8-22.4-36.2-29.9-64.3-2.4-9-4.1-16.7-3.6-17 2.3-2.1 30.7-9.3 44.7-11.4 6.3-.9 35.2.3 43 1.9zM325.1 225l62.1-31.1-.7 3.3c-1 4.7-2.1 6.4-5.8 9.1-1.7 1.3-28.8 16-60.1 32.7-51.8 27.7-57.1 30.2-59.6 29.3-5.3-2-114.5-60.6-116.9-62.7-2.5-2.2-5.1-7.4-5.1-10.1 0-1.1 18.7 7.8 62 29.5l61.9 31 62.2-31zm-194 131.4c1.1 3 .1 11.2-8.7 69.9-5.4 36.7-10.2 66.3-10.5 65.9-.4-.4-3-8-5.8-16.9l-5.1-16.2 11-57.8c6.1-31.8 11.4-58.5 11.6-59.3.5-1.4 4.6 6.4 7.5 14.4zm280.8 44.2 11.2 58.1-5.2 16.6c-2.9 9.2-5.6 16.4-6 15.9-.3-.4-5-30.4-10.4-66.7l-9.8-66 3.9-8.7c2.2-4.8 4.2-8.4 4.5-8 .4.4 5.7 26.8 11.8 58.8z" />
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M13.856 22.918c-2.404-1.215-2.98-4.403-1.131-6.362L24.546 4.04a2 2 0 0 1 2.908 0l11.848 12.544c1.842 1.95 1.28 5.125-1.12 6.325L37 23.5 31.5 26l8.735 3.744a4 4 0 0 1 2.279 2.606l3.782 13.615a2 2 0 0 1-1.927 2.535H7.63a2 2 0 0 1-1.927-2.535L9.486 32.35a4 4 0 0 1 2.279-2.606L20.5 26s-3.4-1.424-5.5-2.5c-.36-.185-.747-.381-1.144-.582M28 11.5c.945.199 2.14.773 2.86 1.15a.92.92 0 0 1 .466 1.054C30.769 15.824 28.899 22 26 22c-2.9 0-4.77-6.176-5.326-8.296a.92.92 0 0 1 .466-1.054c.72-.377 1.915-.951 2.86-1.15 1.529-.322 2.471-.322 4 0m9.062 23.387a1 1 0 0 1 .155-.713L38 33l2.42 10.651a1 1 0 0 1-.08.669L39 47zM16 19.5l.232.464a2 2 0 0 0 .787.836l8.48 4.91a1 1 0 0 0 1.002 0l8.48-4.91a2 2 0 0 0 .787-.836L36 19.5l-9.553 4.776a1 1 0 0 1-.894 0zm-4.34 24.82a1 1 0 0 1-.08-.669L14 33l.783 1.174a1 1 0 0 1 .155.713L13 47z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const Cross = ({
|
|||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path
|
<path
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
d="M85.8 34.4c-5.3 1.9-9.2 5.3-30 26.3C35.9 80.8 33 84.8 33 93c0 2.5.9 6.4 1.9 8.6 1.2 2.7 27.7 32.1 74.5 82.7 39.9 43.3 72.6 79.1 72.6 79.7 0 .6-32.3 35.2-71.7 77-48.5 51.3-72.7 77.7-74.6 81.1-3.4 6.4-3.5 11.7-.3 18.4 3.3 6.8 42 45.5 48.6 48.4 6.4 2.9 12.7 2.7 18.8-.7 3.3-1.7 31.6-27.6 81.3-74.5 42-39.4 76.7-71.6 77.1-71.5.4.2 35.5 32.5 77.9 71.8 42.4 39.3 79.2 72.7 81.8 74.2 3.2 1.9 6 2.8 9.1 2.8 10.1 0 10.9-.6 34.9-24.3 14.2-14.1 23-23.7 24.2-26.2 2.4-5.1 2.4-12.8 0-18.1-1.2-2.6-27.1-30.8-74.5-81.2-40-42.5-72.6-77.6-72.4-78.1.2-.4 32.5-34.7 71.8-76.1 39.3-41.4 72.5-76.7 73.8-78.4 4.5-6.3 4.4-17.9-.1-24.2-5.1-7.2-43.3-46.7-46.9-48.5-2.1-1.1-5.8-2.2-8.2-2.5-9.6-1.4-5.3-5.2-158.3 138.2L262.2 183l-78.4-73.1c-49.6-46.5-79.8-73.9-82.3-75-4.7-2.2-10.5-2.3-15.7-.5z"
|
d="M85.8 34.4c-5.3 1.9-9.2 5.3-30 26.3C35.9 80.8 33 84.8 33 93c0 2.5.9 6.4 1.9 8.6 1.2 2.7 27.7 32.1 74.5 82.7 39.9 43.3 72.6 79.1 72.6 79.7s-32.3 35.2-71.7 77c-48.5 51.3-72.7 77.7-74.6 81.1-3.4 6.4-3.5 11.7-.3 18.4 3.3 6.8 42 45.5 48.6 48.4 6.4 2.9 12.7 2.7 18.8-.7 3.3-1.7 31.6-27.6 81.3-74.5 42-39.4 76.7-71.6 77.1-71.5.4.2 35.5 32.5 77.9 71.8s79.2 72.7 81.8 74.2c3.2 1.9 6 2.8 9.1 2.8 10.1 0 10.9-.6 34.9-24.3 14.2-14.1 23-23.7 24.2-26.2 2.4-5.1 2.4-12.8 0-18.1-1.2-2.6-27.1-30.8-74.5-81.2-40-42.5-72.6-77.6-72.4-78.1.2-.4 32.5-34.7 71.8-76.1s72.5-76.7 73.8-78.4c4.5-6.3 4.4-17.9-.1-24.2-5.1-7.2-43.3-46.7-46.9-48.5-2.1-1.1-5.8-2.2-8.2-2.5-9.6-1.4-5.3-5.2-158.3 138.2L262.2 183l-78.4-73.1c-49.6-46.5-79.8-73.9-82.3-75-4.7-2.2-10.5-2.3-15.7-.5"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,14 +15,16 @@ const Energy = ({
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width={props.size || 16}
|
width={props.size || 16}
|
||||||
height={props.size || 16}
|
height={props.size || 16}
|
||||||
viewBox="0 0 524 524"
|
fill="currentColor"
|
||||||
|
viewBox="0 0 52 52"
|
||||||
aria-labelledby={titleId}
|
aria-labelledby={titleId}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path
|
<path
|
||||||
fill="currentColor"
|
fillRule="evenodd"
|
||||||
d="M115 56.7c0 43.4-3.9 70.1-15.2 105.3-13.2 41.2-31 72.7-64.7 114.9-9.4 11.8-16.8 21.8-16.4 22.1.5.4 15.9 7.1 34.3 15 45.7 19.5 62.7 28.1 85.5 43.3 20.7 13.8 37.9 28 56 46.2 25.5 25.5 37.6 41.5 64.9 86.1l2.8 4.7 4.2-6.7c23.6-38.7 58.8-78.7 94.3-107.4 36.6-29.6 69.6-48.8 120.8-70.6 12.7-5.4 23.4-10.2 23.8-10.6.5-.4-6.1-9.5-14.6-20.1-18.3-23.1-25-32.4-34.6-48.3-22.9-38.1-38.1-82-44.6-128.6-1.1-7.4-1.8-22.4-2.2-41.8l-.6-30.3-2.6.5c-1.4.3-24.6 4.1-51.6 8.5-92 15-94.4 15-199.8-2.5-21.3-3.5-39-6.4-39.2-6.4-.3 0-.5 12-.5 26.7zm129 28.7c14.8 6.1 22.9 7.8 39.5 8.3 13.2.4 16.9.2 30.8-2.2 8.7-1.5 16-2.6 16.2-2.4.2.2-4 4-9.2 8.4-19.6 16.6-37.7 36.8-50.7 56.6-9.5 14.7-24.6 45.4-24.6 50.2 0 1 3.2 1.8 12.3 3 42.6 5.8 64 5.9 108 .6 9.3-1.1 17-1.9 17.2-1.7.2.2-7.6 9.3-17.2 20.3-28.6 32.6-40.9 48.8-55.3 72.5-10.5 17.4-14.4 26.9-26.6 64-6.3 19.5-13.9 42.7-16.8 51.5l-5.3 16-.9-28c-1.2-36.9-1-59 .5-70 2.3-17.3 7.5-35.6 15.1-53.8 4.5-10.5 3.9-11.7-6.9-14.6-22.9-6.2-71.6-4.5-114.5 3.9-8.3 1.6-15.1 2.8-15.3 2.6-.2-.1 3-4.1 7-8.7 31.2-35.8 55.4-78.7 69.6-123.3 2.1-6.6 6.4-22.4 9.6-34.9 4.8-18.9 6.1-22.8 7.4-22.3.9.2 5.4 2 10.1 4z"
|
d="M18.9 3.05c.243-.654.912-1.08 1.647-1.048l15.16.651c1.284.055 2.019 1.4 1.318 2.413L26.463 20.34l15.006 1.135c1.242.093 1.937 1.392 1.276 2.386l-16.891 25.4c-.972 1.46-3.374.586-3.046-1.11l3.55-18.305-15.494 1.758c-1.221.138-2.174-.974-1.77-2.066z"
|
||||||
|
clipRule="evenodd"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ const Exit = ({
|
|||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<g fill="currentColor">
|
<g fill="currentColor">
|
||||||
<path d="M149.3 6.5c-20.9 4.6-40.3 24.4-44.8 46-2.2 10.6-2.2 408.5 0 419 2.4 11.3 7.7 20.9 16.4 29.6 9 9 16.2 13.2 26.7 15.9 7.5 1.9 11.2 2 113.3 2 87.8 0 106.7-.3 112.4-1.5 20-4.2 38.2-21.9 44.4-43.1 1.5-5.2 1.7-12.9 2.1-68.7l.3-62.7h-30l-.3 60.7c-.3 59.8-.3 60.9-2.5 65.5-2.6 5.7-9.4 12.4-15.3 15.2-4.5 2.1-4.9 2.1-110.5 2.1h-106l-5.7-2.8c-3.2-1.6-7.5-4.8-9.7-7.3-7.7-8.8-7.1 9.5-6.9-215.9.3-199 .3-202.1 2.3-206 2.7-5.5 9.7-12.2 15.3-14.8l4.7-2.2h211l5.6 2.6c6.8 3.2 12.4 8.7 15.3 14.8 2 4.5 2.1 6.1 2.4 54.3l.3 49.8h30l-.3-51.8c-.4-45.1-.7-52.5-2.1-57.6-6.1-20.9-22.8-37.4-43.2-42.6-7.6-1.9-11-2-113.4-1.9-84.8 0-106.8.3-111.8 1.4z" />
|
<path d="M149.3 6.5c-20.9 4.6-40.3 24.4-44.8 46-2.2 10.6-2.2 408.5 0 419 2.4 11.3 7.7 20.9 16.4 29.6 9 9 16.2 13.2 26.7 15.9 7.5 1.9 11.2 2 113.3 2 87.8 0 106.7-.3 112.4-1.5 20-4.2 38.2-21.9 44.4-43.1 1.5-5.2 1.7-12.9 2.1-68.7l.3-62.7h-30l-.3 60.7c-.3 59.8-.3 60.9-2.5 65.5-2.6 5.7-9.4 12.4-15.3 15.2-4.5 2.1-4.9 2.1-110.5 2.1h-106l-5.7-2.8c-3.2-1.6-7.5-4.8-9.7-7.3-7.7-8.8-7.1 9.5-6.9-215.9.3-199 .3-202.1 2.3-206 2.7-5.5 9.7-12.2 15.3-14.8l4.7-2.2h211l5.6 2.6c6.8 3.2 12.4 8.7 15.3 14.8 2 4.5 2.1 6.1 2.4 54.3l.3 49.8h30l-.3-51.8c-.4-45.1-.7-52.5-2.1-57.6-6.1-20.9-22.8-37.4-43.2-42.6-7.6-1.9-11-2-113.4-1.9-84.8 0-106.8.3-111.8 1.4" />
|
||||||
<path d="M426 188.7c-.8.3-2.5 1.5-3.7 2.6-2.2 1.9-2.3 2.8-2.3 16.8 0 8.1-.3 15.4-.6 16.3-.6 1.4-9.5 1.6-91.3 1.6-56.9 0-91.9.4-94.2 1-1.9.6-4.6 2.2-6 3.6-2.3 2.5-2.4 3.2-2.7 19.1-.4 18 .2 21 4.7 24 2.4 1.7 8 1.8 95.6 2.1 72.2.2 93.1.5 93.7 1.5.4.6.8 7.8.8 15.9 0 8 .3 15.4.6 16.3 1.6 4.1 11.6 6.1 17 3.2 5.2-2.7 67.2-56.1 68.4-58.9.7-1.5 1-3.8.6-5.2-.7-2.9-65-57.5-69.9-59.3-3.4-1.2-8.2-1.5-10.7-.6z" />
|
<path d="M426 188.7c-.8.3-2.5 1.5-3.7 2.6-2.2 1.9-2.3 2.8-2.3 16.8 0 8.1-.3 15.4-.6 16.3-.6 1.4-9.5 1.6-91.3 1.6-56.9 0-91.9.4-94.2 1-1.9.6-4.6 2.2-6 3.6-2.3 2.5-2.4 3.2-2.7 19.1-.4 18 .2 21 4.7 24 2.4 1.7 8 1.8 95.6 2.1 72.2.2 93.1.5 93.7 1.5.4.6.8 7.8.8 15.9 0 8 .3 15.4.6 16.3 1.6 4.1 11.6 6.1 17 3.2 5.2-2.7 67.2-56.1 68.4-58.9.7-1.5 1-3.8.6-5.2-.7-2.9-65-57.5-69.9-59.3-3.4-1.2-8.2-1.5-10.7-.6" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,14 +15,17 @@ const Experience = ({
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width={props.size || 16}
|
width={props.size || 16}
|
||||||
height={props.size || 16}
|
height={props.size || 16}
|
||||||
viewBox="0 0 524 524"
|
fill="currentColor"
|
||||||
|
viewBox="0 0 52 52"
|
||||||
aria-labelledby={titleId}
|
aria-labelledby={titleId}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<g fill="currentColor">
|
<path
|
||||||
<path d="M24.2 73.7c14 18.5 27.2 42.3 30.4 55.3 2.8 11.1 1.5 23.8-6.1 58.5-15 69-16.8 93.2-7.9 111.3 8.7 17.8 45 48.1 121.5 101.4 23.5 16.4 84.8 56.8 86.2 56.8.4 0 .6-64.2.5-142.6l-.3-142.6-7-2.2c-25.3-8-38.8-24.2-41.1-49-.8-8.6 1.4-20.7 5.2-29.4 1.3-3 2.1-5.9 1.7-6.3-.5-.4-9-4.6-19-9.3L170.1 67h-151l5.1 6.7zM334.3 76c-10.1 4.9-18.3 9.1-18.3 9.3 0 .3 1.1 2.9 2.4 5.8 3.7 8.5 5.1 15.7 5 25.9-.1 16.1-4.7 27.6-15 37.7-6.9 6.8-18.7 13.3-28.1 15.4l-5.3 1.2v142.9c0 78.5.3 142.8.8 142.8.4 0 11.8-7.3 25.4-16.3C409.9 369.3 469 323.6 482.5 300.6c9.9-16.9 8.4-41.8-6.6-111.1-8.5-39-9.6-50.6-5.9-62.6 4.3-14.5 13.5-31.3 27-49.5 3.9-5.2 7-9.6 7-9.9 0-.3-34.1-.5-75.7-.4l-75.8.1-18.2 8.8z" />
|
fillRule="evenodd"
|
||||||
</g>
|
d="m24.104 9.28.855.386.04.923v.016l.002.04a30 30 0 0 1 .017.637c.006.38.007.894-.022 1.32-.119 1.762-.87 3.038-1.442 4.011l-.083.143c-.596 1.017-.971 1.733-.971 2.744 0 1.241.626 2.945 1.303 4.373.694 1.463 1.197 3.12 1.197 4.875 0 5.155-.526 10.296-1.571 15.343l-.38 1.84-.023-.008-.058-.018-.214-.07a61.78 61.78 0 0 1-3.344-1.222c-1.98-.79-4.494-1.922-6.247-3.199-2.474-1.802-4.341-3.56-5.758-5.606-1.421-2.053-2.336-4.32-3.009-7.07-.592-2.421-.199-4.073.175-5.627l.01-.042c.348-1.448.695-2.89.413-5.254-.09-.758-.335-1.558-.653-2.427-.095-.26-.203-.542-.315-.832a31 31 0 0 1-.65-1.8c-.267-.857-.5-1.892-.302-2.916.222-1.144.932-2.047 2.07-2.657 1.796-.964 3.931-1.233 5.994-1.176 2.08.058 4.217.45 6.117.933a46.6 46.6 0 0 1 6.85 2.34M29.939 9.28l-.855.386-.04.923v.016l-.002.04a20 20 0 0 0-.017.637c-.006.38-.007.894.022 1.32.119 1.762.87 3.038 1.442 4.011l.083.143c.596 1.017.971 1.733.971 2.744 0 1.241-.626 2.945-1.303 4.373-.694 1.463-1.197 3.12-1.197 4.875a75.7 75.7 0 0 0 1.571 15.343l.38 1.84.023-.008.058-.018.214-.07a61.776 61.776 0 0 0 3.344-1.222c1.98-.79 4.494-1.922 6.247-3.199 2.474-1.802 4.34-3.56 5.758-5.606 1.421-2.053 2.336-4.32 3.008-7.07.593-2.421.2-4.073-.174-5.627l-.01-.042c-.349-1.448-.695-2.89-.413-5.254.09-.758.335-1.558.653-2.427.095-.26.203-.542.315-.832.23-.599.473-1.235.65-1.8.267-.857.5-1.892.302-2.916-.222-1.144-.932-2.047-2.07-2.657-1.796-.964-3.931-1.233-5.994-1.176-2.08.058-4.218.45-6.117.933a46.6 46.6 0 0 0-6.71 2.28z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const FullscreenOff = ({
|
|||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<g fill="currentColor">
|
<g fill="currentColor">
|
||||||
<path d="M29.3 20c-1.3.5-3.2 2.4-4.3 4.2-2 3.2-2 5-2 85.6V192l3.4 3.7c3.2 3.5 3.6 3.7 10 3.7 6.2 0 6.9-.3 9.9-3.2l3.2-3.2.2-69.8c.2-52.4.6-70.3 1.5-72 2.8-5.3 1.4-5.2 75.9-5.2h69.1l3.4-3.4c3.2-3.2 3.4-3.8 3.4-10.4 0-6.8-.1-7.1-3.5-10.1l-3.6-3.1-82.2.1c-46.4 0-83.2.4-84.4.9zM323.7 22.1c-5.5 4.2-5.8 16-.6 21.1l2.7 2.8h67.9c43.4 0 69.1.4 71.5 1 2 .6 4.6 2.2 5.8 3.7 2 2.5 2 3.6 2 71.5 0 75.9-.2 73.1 6.2 76.4 4.9 2.5 14.1 1.5 17.6-1.9l2.7-2.7.3-83.5.2-83.4-3.1-3.5-3.1-3.6H410c-82.9 0-83.8 0-86.3 2.1zM26.5 320.9c-1.1.5-2.9 1.9-4 3.1-2 2.2-2 3.8-2.3 85.6l-.2 83.3 3.1 3.5 3.1 3.6H110c82.9 0 83.8 0 86.3-2.1 5.5-4.2 5.8-16 .6-21.1l-2.7-2.8h-67.9c-43.4 0-69.1-.4-71.5-1-2-.6-4.6-2.2-5.8-3.7-2-2.5-2-3.6-2-71.5 0-75.6.2-73.1-6-76.3-3.1-1.6-11.2-1.9-14.5-.6zM477.2 323.4l-3.7 3.4-.5 71.1c-.6 79.2 0 73.4-7.6 75.1-2.1.5-34.3 1-71.5 1h-67.7l-3.1 3.5c-2.7 3.1-3.1 4.3-3.1 9.5s.4 6.4 3.1 9.5l3.1 3.5h168l2.9-2.9 2.9-2.9V326.8l-3.4-3.4c-3.1-3.1-3.9-3.4-9.5-3.4s-6.5.3-9.9 3.4z" />
|
<path d="M29.3 20c-1.3.5-3.2 2.4-4.3 4.2-2 3.2-2 5-2 85.6V192l3.4 3.7c3.2 3.5 3.6 3.7 10 3.7 6.2 0 6.9-.3 9.9-3.2l3.2-3.2.2-69.8c.2-52.4.6-70.3 1.5-72 2.8-5.3 1.4-5.2 75.9-5.2h69.1l3.4-3.4c3.2-3.2 3.4-3.8 3.4-10.4 0-6.8-.1-7.1-3.5-10.1l-3.6-3.1-82.2.1c-46.4 0-83.2.4-84.4.9M323.7 22.1c-5.5 4.2-5.8 16-.6 21.1l2.7 2.8h67.9c43.4 0 69.1.4 71.5 1 2 .6 4.6 2.2 5.8 3.7 2 2.5 2 3.6 2 71.5 0 75.9-.2 73.1 6.2 76.4 4.9 2.5 14.1 1.5 17.6-1.9l2.7-2.7.3-83.5.2-83.4-3.1-3.5-3.1-3.6H410c-82.9 0-83.8 0-86.3 2.1M26.5 320.9c-1.1.5-2.9 1.9-4 3.1-2 2.2-2 3.8-2.3 85.6l-.2 83.3 3.1 3.5 3.1 3.6H110c82.9 0 83.8 0 86.3-2.1 5.5-4.2 5.8-16 .6-21.1l-2.7-2.8h-67.9c-43.4 0-69.1-.4-71.5-1-2-.6-4.6-2.2-5.8-3.7-2-2.5-2-3.6-2-71.5 0-75.6.2-73.1-6-76.3-3.1-1.6-11.2-1.9-14.5-.6M477.2 323.4l-3.7 3.4-.5 71.1c-.6 79.2 0 73.4-7.6 75.1-2.1.5-34.3 1-71.5 1h-67.7l-3.1 3.5c-2.7 3.1-3.1 4.3-3.1 9.5s.4 6.4 3.1 9.5l3.1 3.5h168l2.9-2.9 2.9-2.9V326.8l-3.4-3.4c-3.1-3.1-3.9-3.4-9.5-3.4s-6.5.3-9.9 3.4" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const FullscreenOn = ({
|
|||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<g fill="currentColor">
|
<g fill="currentColor">
|
||||||
<path d="M185.3 20c-1.8.4-4 1.4-4.9 2.1-4.3 3.6-4.4 4.5-4.4 76.3v68.5l-2.4 2.8-2.4 2.8-71.2.5-71.2.5-2.9 3.3c-2.6 2.9-2.9 4-2.9 9.7 0 5.7.3 6.8 2.9 9.7l2.9 3.3h168.4l2.9-3.3 2.9-3.2v-83.3c0-89.3.2-86-4.8-88.6-2.7-1.4-9.4-2-12.9-1.1zM328 20c-1.9.5-4.5 2-5.7 3.5l-2.3 2.6v167.1l3.4 3.4 3.4 3.4 83.7-.2 83.7-.3 3-3.4c2.8-3.2 3-3.7 2.6-10.2-.3-5.9-.8-7.3-3.1-9.7l-2.7-2.7-70.9-.5-70.9-.5-2.6-2.4-2.5-2.4-.3-70.1c-.3-64.9-.4-70.4-2-72.9-1-1.4-2.6-3-3.5-3.5-2.9-1.5-9.5-2.1-13.3-1.2zM26.4 321c-1.2.4-3.1 2.1-4.4 3.7-1.9 2.4-2.1 3.7-1.8 9.6.3 5.7.8 7.1 3.1 9.5l2.7 2.7 70.9.5 70.9.5 2.6 2.4 2.6 2.4v67.6c0 37.2.5 69.4 1 71.5.5 2.1 1.7 4.8 2.8 5.8 4.6 4.7 16.7 4.2 21-.7l2.2-2.6V326.8l-3.4-3.4-3.4-3.4-82.3.1c-45.3 0-83.4.4-84.5.9zM326.3 321c-1.3.5-3.2 2.4-4.3 4.2-2 3.2-2 5-2 85.6V493l3.4 3.7c3.2 3.5 3.6 3.7 10 3.7 6.2 0 6.9-.3 9.9-3.2l3.2-3.2.2-69.8c.2-52.4.6-70.3 1.5-72 2.8-5.3 1.4-5.2 75.9-5.2h69.1l3.4-3.4c3.2-3.2 3.4-3.8 3.4-10.4 0-6.8-.1-7.1-3.5-10.1l-3.6-3.1-82.2.1c-46.4 0-83.2.4-84.4.9z" />
|
<path d="M185.3 20c-1.8.4-4 1.4-4.9 2.1-4.3 3.6-4.4 4.5-4.4 76.3v68.5l-2.4 2.8-2.4 2.8-71.2.5-71.2.5-2.9 3.3c-2.6 2.9-2.9 4-2.9 9.7s.3 6.8 2.9 9.7l2.9 3.3h168.4l2.9-3.3 2.9-3.2v-83.3c0-89.3.2-86-4.8-88.6-2.7-1.4-9.4-2-12.9-1.1M328 20c-1.9.5-4.5 2-5.7 3.5l-2.3 2.6v167.1l3.4 3.4 3.4 3.4 83.7-.2 83.7-.3 3-3.4c2.8-3.2 3-3.7 2.6-10.2-.3-5.9-.8-7.3-3.1-9.7l-2.7-2.7-70.9-.5-70.9-.5-2.6-2.4-2.5-2.4-.3-70.1c-.3-64.9-.4-70.4-2-72.9-1-1.4-2.6-3-3.5-3.5-2.9-1.5-9.5-2.1-13.3-1.2M26.4 321c-1.2.4-3.1 2.1-4.4 3.7-1.9 2.4-2.1 3.7-1.8 9.6.3 5.7.8 7.1 3.1 9.5l2.7 2.7 70.9.5 70.9.5 2.6 2.4 2.6 2.4v67.6c0 37.2.5 69.4 1 71.5s1.7 4.8 2.8 5.8c4.6 4.7 16.7 4.2 21-.7l2.2-2.6V326.8l-3.4-3.4-3.4-3.4-82.3.1c-45.3 0-83.4.4-84.5.9M326.3 321c-1.3.5-3.2 2.4-4.3 4.2-2 3.2-2 5-2 85.6V493l3.4 3.7c3.2 3.5 3.6 3.7 10 3.7 6.2 0 6.9-.3 9.9-3.2l3.2-3.2.2-69.8c.2-52.4.6-70.3 1.5-72 2.8-5.3 1.4-5.2 75.9-5.2h69.1l3.4-3.4c3.2-3.2 3.4-3.8 3.4-10.4 0-6.8-.1-7.1-3.5-10.1l-3.6-3.1-82.2.1c-46.4 0-83.2.4-84.4.9" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,15 +15,17 @@ const Info = ({
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width={props.size || 16}
|
width={props.size || 16}
|
||||||
height={props.size || 16}
|
height={props.size || 16}
|
||||||
viewBox="0 0 524 524"
|
fill="currentColor"
|
||||||
|
viewBox="0 0 52 52"
|
||||||
aria-labelledby={titleId}
|
aria-labelledby={titleId}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<g fill="currentColor">
|
<path
|
||||||
<path d="M236.7 33.5c-28.8 3.6-51.4 10.2-75.4 22C92.7 89.2 46.2 152.7 34.4 229c-2.5 16-2.5 50 0 66C46 370 91.6 433.3 158.5 467.2c32.9 16.7 65.2 24.3 103.5 24.3 22.1 0 34.2-1.4 54.5-6.2 31.1-7.3 65.4-24.3 90.8-45.2 9.1-7.4 27-25.5 34.3-34.6 25-31.3 41.8-70.1 48-111 2.5-16.4 2.5-48.3 0-65-11.4-76.2-58.6-140.8-126.9-174-23.2-11.2-42.1-17.1-67.7-21-14.5-2.2-44.7-2.8-58.3-1zM294 78.9c74.6 13.2 133.6 70.2 149.6 144.4 8.6 40.3 3.5 82-14.6 119.3C390.5 422 302.6 463 216.5 441.9c-88.7-21.8-148.8-107.6-139.4-199 7.3-71.5 56.7-133.8 124.4-156.8 12.2-4.1 27.1-7.4 40.5-9.1 11.8-1.4 38.8-.4 52 1.9z" />
|
fillRule="evenodd"
|
||||||
<path d="M246.5 112c-20.8 2-36.2 8.6-48.5 21.1-11.5 11.6-19.3 26.4-27.5 52-3.4 10.6-3.6 11.9-2.4 14.4 2 4.1 5.8 6.5 11.4 7.1 7.4.9 9.2-1 17.8-19 14.9-30.8 25.7-41.9 46-47.3 8.4-2.3 29.8-2.2 38.6.1 21.2 5.5 34.6 19.5 35.9 37.6.8 11.1-2.3 16.4-20.5 35.2-26 26.8-38 45.6-44.2 69.2-3 11.3-4.6 31.6-3.6 46 .7 11.3.9 12.1 3.5 14.8 2.6 2.5 3.5 2.8 9.5 2.8 5.7 0 6.9-.3 9-2.5 1.4-1.3 2.5-3.2 2.6-4.2.1-1 .2-9.9.3-19.8.2-20.3 1.6-28.1 7.1-40.2 7.6-16.6 23.7-36 42.5-51.3 22.4-18.2 28.1-27.9 28-47.8-.1-19.8-6.1-35.4-17.9-46.5-8.6-8.1-14.8-11.8-26.6-15.7-16.5-5.4-41.3-7.8-61-6zM252.4 372.9c-4.2 1.8-5.4 5-5.4 13.9 0 4.6.5 9.2 1 10.3 1.8 3.3 6.4 4.9 14 4.9 12.6 0 15.5-3.3 14.9-16.9-.6-11.3-1.9-12.5-13.7-12.8-4.8-.2-9.7.1-10.8.6z" />
|
d="M26 48c12.15 0 22-9.85 22-22S38.15 4 26 4 4 13.85 4 26s9.85 22 22 22m0 4c14.36 0 26-11.64 26-26S40.36 0 26 0 0 11.64 0 26s11.64 26 26 26m-4.191-36.87-.264.172c-2.513 1.644-2.671 5.27-.31 7.125a2 2 0 1 1-2.47 3.146c-4.514-3.547-4.213-10.477.591-13.619l.264-.172a11.66 11.66 0 0 1 12.76 0l.77.503c4.556 2.98 4.841 9.551.561 12.914-.422.332-.877.62-1.357.86l-.54.27A6.9 6.9 0 0 0 28 32.5a2 2 0 1 1-4 0 10.9 10.9 0 0 1 6.025-9.748l.54-.27q.36-.18.675-.428c2.128-1.672 1.986-4.94-.28-6.421l-.769-.503a7.66 7.66 0 0 0-8.382 0M26 42a3 3 0 1 0 0-6 3 3 0 0 0 0 6"
|
||||||
</g>
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const FivePlayers = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM396 269v221H57V409.3l11.3-.6c18.4-1 32.5-6 42.3-15C125.7 380 138.1 334 138.1 292c0-23.9-3.5-47.1-10.7-71-9.5-31.4-25.9-44.2-58.8-45.7l-11.9-.6.6-11.6c1.1-18.8 4.8-33.4 12.7-49.2C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48v221zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.2 6.9 1.5 19.8 1.5 68.3v59.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v353.4l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v59.7c0 65.6-.2 69-6.1 86.4-13.7 41-49.7 71.9-92.9 79.7-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-638.8-.2-638.8 118.7.4 118.8.3 9 2.3zM396 692v192H57V805.3l11.3-.6c18.4-1 32.5-6 42.3-15C125.7 776 138.1 730 138.1 688c0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V500h339v192zm0 417.5V1326H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-15-19.7-24.6-47.5-24.6-71.6v-6.6l11.3-.6c18.4-1 32.5-6 42.3-15 15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V893h339v216.5z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M396 269v221H57v-80.7l11.3-.6q27.6-1.5 42.3-15C125.7 380 138.1 334 138.1 292c0-23.9-3.5-47.1-10.7-71-9.5-31.4-25.9-44.2-58.8-45.7l-11.9-.6.6-11.6c1.1-18.8 4.8-33.4 12.7-49.2C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.2 6.9 1.5 19.8 1.5 68.3v59.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v353.4l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v59.7c0 65.6-.2 69-6.1 86.4-13.7 41-49.7 71.9-92.9 79.7-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-638.8-.2-638.8 118.7.4 118.8.3zM396 692v192H57v-78.7l11.3-.6q27.6-1.5 42.3-15C125.7 776 138.1 730 138.1 688c0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V500h339zm0 417.5V1326H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-15-19.7-24.6-47.5-24.6-71.6v-6.6l11.3-.6q27.6-1.5 42.3-15c15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V893h339z" />
|
||||||
<path d="M195 236.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM574.1 340.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM574.1 928.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM195 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM195 1027.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 4-19-1.3-36.4-15.6-50.7-11.5-11.6-24.1-16.8-40.2-16.6-4 0-9.7.7-12.7 1.5z" />
|
<path d="M195 236.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M574.1 340.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M574.1 928.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M195 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M195 1027.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 4-19-1.3-36.4-15.6-50.7-11.5-11.6-24.1-16.8-40.2-16.6-4 0-9.7.7-12.7 1.5" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const FivePlayersSide = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM396 285.5V523H57V418.3l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6.1-15c.1-21.7 3.3-36.1 12.1-54 14.1-28.4 39.2-50.1 70-60.5 17.7-5.9 15.3-5.8 141.1-5.9L396 48v237.5zM651.5 50.9c36 9.4 63.5 30.9 79.4 62 9.1 17.9 13.1 35 13.1 56.6v13.2l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V523H405V47.9l118.8.4 118.7.3 9 2.3zM395.8 767.2l.2 234.8H57V872.3l11.8-.6c18.9-1 32.8-5.9 42.8-15C126.7 843 139.1 797 139.1 755c0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.1-45.7l-12.3-.6V532l169.3.2 169.2.3.3 234.7zM744 584.4v52.3l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V1002H405V532h339v52.4zm0 530c0 113.4.1 111.6-6.1 130-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.6-10.3c-.7-11.8-4-24.6-8.3-32.3-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-19.1 9-28.7 25.4-30.4 52l-.7 10-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-4.9-18.3-5-19.6-5-127.7V1011h687v103.4z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M396 285.5V523H57V418.3l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6.1-15c.1-21.7 3.3-36.1 12.1-54 14.1-28.4 39.2-50.1 70-60.5 17.7-5.9 15.3-5.8 141.1-5.9L396 48zM651.5 50.9c36 9.4 63.5 30.9 79.4 62 9.1 17.9 13.1 35 13.1 56.6v13.2l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V523H405V47.9l118.8.4 118.7.3zM395.8 767.2l.2 234.8H57V872.3l11.8-.6c18.9-1 32.8-5.9 42.8-15C126.7 843 139.1 797 139.1 755c0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.1-45.7l-12.3-.6V532l169.3.2 169.2.3zM744 584.4v52.3l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V1002H405V532h339zm0 530c0 113.4.1 111.6-6.1 130-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.6-10.3c-.7-11.8-4-24.6-8.3-32.3-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-19.1 9-28.7 25.4-30.4 52l-.7 10-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-4.9-18.3-5-19.6-5-127.7V1011h687z" />
|
||||||
<path d="M192 245.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM578.1 247.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM196 699.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM578.1 701.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM388.3 1121.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7z" />
|
<path d="M192 245.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M578.1 247.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M196 699.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M578.1 701.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M388.3 1121.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const FourPlayers = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM396 365v317H57V511.3l11.3-.6c18.4-1 32.5-6 42.3-15C125.7 482 138.1 436 138.1 394c0-23.9-3.5-47.1-10.7-71-9.4-31.4-25.9-44.2-58.7-45.7l-11.8-.6.4-62.6c.3-59.6.4-63 2.5-71.6 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48v317zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.2 6.9 1.5 19.8 1.5 68.3v59.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V682H405V47.9l118.8.4 118.7.3 9 2.3zM396 1009v317H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-7.7-10.1-16.6-27.7-19.6-38.6-4.6-17.2-5-22.8-5-83.5v-57.1l11.3-.6c18.4-1 32.5-6 42.3-15 15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V692h339v317zm348-231.2v85.9l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v59.7c0 65.6-.2 69-6.1 86.4-13.7 41-49.7 71.9-92.9 79.7-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-316.8L405 692h339v85.8z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M396 365v317H57V511.3l11.3-.6q27.6-1.5 42.3-15C125.7 482 138.1 436 138.1 394c0-23.9-3.5-47.1-10.7-71-9.4-31.4-25.9-44.2-58.7-45.7l-11.8-.6.4-62.6c.3-59.6.4-63 2.5-71.6 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.2 6.9 1.5 19.8 1.5 68.3v59.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V682H405V47.9l118.8.4 118.7.3zM396 1009v317H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-7.7-10.1-16.6-27.7-19.6-38.6-4.6-17.2-5-22.8-5-83.5v-57.1l11.3-.6q27.6-1.5 42.3-15c15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V692h339zm348-231.2v85.9l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v59.7c0 65.6-.2 69-6.1 86.4-13.7 41-49.7 71.9-92.9 79.7-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-316.8L405 692h339z" />
|
||||||
<path d="M195 338.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM574.1 340.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM195 926.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM574.1 928.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2z" />
|
<path d="M195 338.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M574.1 340.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M195 926.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M574.1 928.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const FourPlayersSide = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zm124 41.2c.1 21 5.7 36.8 16.7 47.4 14.3 13.6 60.7 25.4 100.3 25.4 23.7 0 42.4-2.7 66.7-9.7 23.4-6.7 35.7-15 42.3-28.8 5-10.6 6.6-17.6 7.2-32.9l.6-13.8 62.1.3c61.6.3 62.2.3 71.6 2.7 48.7 12.2 83.1 48.6 91.5 96.8 1.3 7.3 1.5 26.3 1.5 124.9V389l-343.2-.2-343.3-.3V271c0-109.1.1-118.1 1.8-126 9.2-43.6 38.7-77 80.4-91 16.6-5.5 20.3-5.8 84.8-5.9l59-.1v12.3zM396 687v289H57V805.3l12.8-.6c19.8-.9 33.6-5.6 43.8-15C128.7 776 141.1 730 141.1 688c0-23.9-3.5-47.1-10.7-71-9.6-31.9-26-44.4-60.1-45.7l-13.3-.6V398h339v289zm348-202.8v86.3h-12.3c-14.9.1-24.8 2.2-35.5 7.5-13.1 6.6-19.9 15.8-26.6 36-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 11.1 11 23.1 15.3 45.4 16.1l14.2.6V976H405V398h339v86.2zm0 617.2c0 127.7.1 124.4-6.1 143-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.7-8.6c-.8-10.3-4.3-23-8.2-30-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-18.5 8.7-28.1 24.5-30.3 49.6l-.8 8.4-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-5-18.4-5-18.6-5-140.7V985h687v116.4z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7m124 41.2c.1 21 5.7 36.8 16.7 47.4 14.3 13.6 60.7 25.4 100.3 25.4 23.7 0 42.4-2.7 66.7-9.7 23.4-6.7 35.7-15 42.3-28.8 5-10.6 6.6-17.6 7.2-32.9l.6-13.8 62.1.3c61.6.3 62.2.3 71.6 2.7 48.7 12.2 83.1 48.6 91.5 96.8 1.3 7.3 1.5 26.3 1.5 124.9V389l-343.2-.2-343.3-.3V271c0-109.1.1-118.1 1.8-126 9.2-43.6 38.7-77 80.4-91 16.6-5.5 20.3-5.8 84.8-5.9l59-.1zM396 687v289H57V805.3l12.8-.6c19.8-.9 33.6-5.6 43.8-15C128.7 776 141.1 730 141.1 688c0-23.9-3.5-47.1-10.7-71-9.6-31.9-26-44.4-60.1-45.7l-13.3-.6V398h339zm348-202.8v86.3h-12.3c-14.9.1-24.8 2.2-35.5 7.5-13.1 6.6-19.9 15.8-26.6 36-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 11.1 11 23.1 15.3 45.4 16.1l14.2.6V976H405V398h339zm0 617.2c0 127.7.1 124.4-6.1 143-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.7-8.6c-.8-10.3-4.3-23-8.2-30-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-18.5 8.7-28.1 24.5-30.3 49.6l-.8 8.4-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-5-18.4-5-18.6-5-140.7V985h687z" />
|
||||||
<path d="M391.8 148c-16.5 3-30 12.2-39.1 26.7-7.9 12.6-10 31-5.2 46.1 4.5 14.4 15.5 26.9 29.1 33.4 22.1 10.4 46.1 6.2 63.5-11.1 11.5-11.5 16.9-24.2 16.9-39.9 0-23.4-16.1-45.5-38.6-53.1-7.1-2.3-19.8-3.4-26.6-2.1zM198 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM570.1 634.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM388.3 1125.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7z" />
|
<path d="M391.8 148c-16.5 3-30 12.2-39.1 26.7-7.9 12.6-10 31-5.2 46.1 4.5 14.4 15.5 26.9 29.1 33.4 22.1 10.4 46.1 6.2 63.5-11.1 11.5-11.5 16.9-24.2 16.9-39.9 0-23.4-16.1-45.5-38.6-53.1-7.1-2.3-19.8-3.4-26.6-2.1M198 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M570.1 634.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M388.3 1125.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const OnePlayerLandscape = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zm492 31.8c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.5 1.5 73.4 1.5 539.3 0 474.6-.2 531.7-1.5 539.6-8.5 48.4-45 85.9-94.5 97.1-6.9 1.6-26.1 1.7-239 2-154.7.3-235.3 0-243-.7-20.8-1.8-38.3-8-55.8-19.7-28.1-18.7-46.6-48.2-51.8-82.8-1.1-7.4-1.4-45.9-1.4-213.6V804.3l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6v-208c0-196.2.1-208.5 1.8-216.5 9.2-43.6 38.7-77 80.4-91 18.5-6.2 3.5-5.8 264.3-5.6l238.5.1 9 2.4z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7m492 31.8c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.5 1.5 73.4 1.5 539.3 0 474.6-.2 531.7-1.5 539.6-8.5 48.4-45 85.9-94.5 97.1-6.9 1.6-26.1 1.7-239 2-154.7.3-235.3 0-243-.7-20.8-1.8-38.3-8-55.8-19.7-28.1-18.7-46.6-48.2-51.8-82.8-1.1-7.4-1.4-45.9-1.4-213.6V804.3l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6v-208c0-196.2.1-208.5 1.8-216.5 9.2-43.6 38.7-77 80.4-91 18.5-6.2 3.5-5.8 264.3-5.6l238.5.1z" />
|
||||||
<path d="M192 631.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5z" />
|
<path d="M192 631.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const OnePlayerPortrait = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zm492 31.8c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.5 1.5 73.4 1.5 539.3 0 474.6-.2 531.7-1.5 539.6-8.5 48.4-45.4 86.3-94.5 97-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.7-9c-.8-10.6-4.3-23.4-8.2-30.6-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-18.6 8.8-28.2 24.8-30.4 50.2l-.7 8.8-53 .3c-30.9.2-57.8-.2-64.5-.8-56.5-5-101.1-48.5-108-105.2-.8-6.8-1-151.9-.8-538.8l.3-529.5 2.2-9c2.9-12 5.2-18.5 10.3-28.6C83.9 86 109.2 64.3 139.7 54c18.5-6.2 3.5-5.8 264.3-5.6l238.5.1 9 2.4z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7m492 31.8c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.5 1.5 73.4 1.5 539.3 0 474.6-.2 531.7-1.5 539.6-8.5 48.4-45.4 86.3-94.5 97-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.7-9c-.8-10.6-4.3-23.4-8.2-30.6-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-18.6 8.8-28.2 24.8-30.4 50.2l-.7 8.8-53 .3c-30.9.2-57.8-.2-64.5-.8-56.5-5-101.1-48.5-108-105.2-.8-6.8-1-151.9-.8-538.8l.3-529.5 2.2-9c2.9-12 5.2-18.5 10.3-28.6C83.9 86 109.2 64.3 139.7 54c18.5-6.2 3.5-5.8 264.3-5.6l238.5.1z" />
|
||||||
<path d="M388.3 1124.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7z" />
|
<path d="M388.3 1124.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const SixPlayers = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM396 266.5V485l-169.2-.2-169.3-.3-.3-37.6-.2-37.6 11.2-.6c18.5-1 32.6-6 42.4-15C125.7 380 138.1 334 138.1 292c0-23.9-3.5-47.1-10.7-71-9.5-31.4-25.9-44.2-58.8-45.7l-11.9-.6.6-11.6c1.1-18.8 4.8-33.4 12.7-49.2C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48v218.5zM651.5 50.9c36 9.4 63.5 30.9 79.4 62C739.3 129.4 744 148 744 165v8.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V485l-169.2-.2-169.3-.3-.3-218.3-.2-218.3 118.7.4 118.8.3 9 2.3zM396 687v193H57V805.3l11.3-.6c18.4-1 32.5-6 42.3-15C125.7 776 138.1 730 138.1 688c0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V494h339v193zm348-155.2v37.9l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V880H405V494h339v37.8zm-348 575.7V1326H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-15-19.7-24.6-47.5-24.6-71.6v-6.6l11.3-.6c18.4-1 32.5-6 42.3-15 15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V889h339v218.5zm348-180.7v37.9l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v9.2c0 55.8-42.5 105.3-99 115.6-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-217c-.1-119.4 0-217.6.3-218.3.3-.9 35.2-1.2 169.5-1.2h169v37.8z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M396 266.5V485l-169.2-.2-169.3-.3-.3-37.6-.2-37.6 11.2-.6c18.5-1 32.6-6 42.4-15C125.7 380 138.1 334 138.1 292c0-23.9-3.5-47.1-10.7-71-9.5-31.4-25.9-44.2-58.8-45.7l-11.9-.6.6-11.6c1.1-18.8 4.8-33.4 12.7-49.2C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48zM651.5 50.9c36 9.4 63.5 30.9 79.4 62C739.3 129.4 744 148 744 165v8.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V485l-169.2-.2-169.3-.3-.3-218.3-.2-218.3 118.7.4 118.8.3zM396 687v193H57v-74.7l11.3-.6q27.6-1.5 42.3-15C125.7 776 138.1 730 138.1 688c0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V494h339zm348-155.2v37.9l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V880H405V494h339zm-348 575.7V1326H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-15-19.7-24.6-47.5-24.6-71.6v-6.6l11.3-.6q27.6-1.5 42.3-15c15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V889h339zm348-180.7v37.9l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v9.2c0 55.8-42.5 105.3-99 115.6-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-217c-.1-119.4 0-217.6.3-218.3.3-.9 35.2-1.2 169.5-1.2h169z" />
|
||||||
<path d="M195 236.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM574.1 238.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM195 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM574.1 634.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM195 1027.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 4-19-1.3-36.4-15.6-50.7-11.5-11.6-24.1-16.8-40.2-16.6-4 0-9.7.7-12.7 1.5zM574.1 1029.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2z" />
|
<path d="M195 236.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M574.1 238.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M195 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M574.1 634.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M195 1027.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 4-19-1.3-36.4-15.6-50.7-11.5-11.6-24.1-16.8-40.2-16.6-4 0-9.7.7-12.7 1.5M574.1 1029.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const SixPlayersSide = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zm124 42.1c0 22 5.5 37.7 16.7 48.5 14.3 13.6 60.7 25.4 100.3 25.4 23.7 0 42.4-2.7 66.7-9.7 23.4-6.7 35.7-15 42.3-28.8 5.2-10.9 6.6-17.5 7.2-33.9l.6-14.8 62.1.3c61.6.3 62.2.3 71.6 2.7 48.7 12.2 83.1 48.6 91.5 96.8 1.2 7 1.5 20.5 1.5 73.9V287H56.9l.4-67.8c.3-64.8.4-68.1 2.5-76.7 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c16.6-5.5 20.3-5.8 84.8-5.9l59-.1v13.2zM396 489v193H57v-42.7l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6-.3-54.3L57 296h339v193zm348-139.1v53.8l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V682H405V296h339v53.9zM395.8 880.2l-.3 188.3-169.2.3-169.3.2V985.3l11.8-.6c18.9-1 32.8-5.9 42.8-15C126.7 956 139.1 910 139.1 868c0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.1-45.7l-12.3-.6V692h339l-.2 188.2zM744 720.9v28.8l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V1069H405V692h339v28.9zm0 427c0 76.6-.1 78.7-6.1 96.5-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.6-10.3c-.7-11.8-4-24.6-8.3-32.3-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-19.1 9-28.7 25.4-30.4 52l-.7 10-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-4.7-17.6-5-22.1-5-94.2V1078h687v69.9z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7m124 42.1c0 22 5.5 37.7 16.7 48.5 14.3 13.6 60.7 25.4 100.3 25.4 23.7 0 42.4-2.7 66.7-9.7 23.4-6.7 35.7-15 42.3-28.8 5.2-10.9 6.6-17.5 7.2-33.9l.6-14.8 62.1.3c61.6.3 62.2.3 71.6 2.7 48.7 12.2 83.1 48.6 91.5 96.8 1.2 7 1.5 20.5 1.5 73.9V287H56.9l.4-67.8c.3-64.8.4-68.1 2.5-76.7 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c16.6-5.5 20.3-5.8 84.8-5.9l59-.1zM396 489v193H57v-42.7l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6-.3-54.3L57 296h339zm348-139.1v53.8l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V682H405V296h339zM395.8 880.2l-.3 188.3-169.2.3-169.3.2v-83.7l11.8-.6c18.9-1 32.8-5.9 42.8-15C126.7 956 139.1 910 139.1 868c0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.1-45.7l-12.3-.6V692h339zM744 720.9v28.8l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6v84.7H405V692h339zm0 427c0 76.6-.1 78.7-6.1 96.5-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.6-10.3c-.7-11.8-4-24.6-8.3-32.3-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-19.1 9-28.7 25.4-30.4 52l-.7 10-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-4.7-17.6-5-22.1-5-94.2V1078h687z" />
|
||||||
<path d="M391.8 150c-16.5 3-30 12.2-39.1 26.7-7.9 12.6-10 31-5.2 46.1 4.5 14.4 15.5 26.9 29.1 33.4 22.1 10.4 46.1 6.2 63.5-11.1 11.5-11.5 16.9-24.2 16.9-39.9 0-23.4-16.1-45.5-38.6-53.1-7.1-2.3-19.8-3.4-26.6-2.1zM192 466.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM578.1 468.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM196 812.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM578.1 814.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM388.3 1121.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7z" />
|
<path d="M391.8 150c-16.5 3-30 12.2-39.1 26.7-7.9 12.6-10 31-5.2 46.1 4.5 14.4 15.5 26.9 29.1 33.4 22.1 10.4 46.1 6.2 63.5-11.1 11.5-11.5 16.9-24.2 16.9-39.9 0-23.4-16.1-45.5-38.6-53.1-7.1-2.3-19.8-3.4-26.6-2.1M192 466.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M578.1 468.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M196 812.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M578.1 814.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M388.3 1121.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const ThreePlayers = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM396 365v317H57V511.3l11.3-.6c18.4-1 32.5-6 42.3-15C125.7 482 138.1 436 138.1 394c0-23.9-3.5-47.1-10.7-71-9.4-31.4-25.9-44.2-58.7-45.7l-11.8-.6.4-62.6c.3-59.6.4-63 2.5-71.6 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48v317zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.4 1.5 36.6 1.5 215.3v206.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V1011c0 227.3.3 214.2-6.1 233.4-13.4 40-47 69.6-89.9 79.3-6.9 1.6-26.1 1.7-239 2-154.7.3-235.3 0-243-.7-20.8-1.8-38.3-8-55.8-19.7-28.1-18.7-46.6-48.2-51.8-82.8-1.1-6.8-1.4-23-1.4-66.1v-57.1l11.3-.6c18.4-1 32.5-6 42.3-15 15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V691h339v634h9V47.9l118.8.4 118.7.3 9 2.3z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M396 365v317H57V511.3l11.3-.6q27.6-1.5 42.3-15C125.7 482 138.1 436 138.1 394c0-23.9-3.5-47.1-10.7-71-9.4-31.4-25.9-44.2-58.7-45.7l-11.8-.6.4-62.6c.3-59.6.4-63 2.5-71.6 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.4 1.5 36.6 1.5 215.3v206.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V1011c0 227.3.3 214.2-6.1 233.4-13.4 40-47 69.6-89.9 79.3-6.9 1.6-26.1 1.7-239 2-154.7.3-235.3 0-243-.7-20.8-1.8-38.3-8-55.8-19.7-28.1-18.7-46.6-48.2-51.8-82.8-1.1-6.8-1.4-23-1.4-66.1v-57.1l11.3-.6q27.6-1.5 42.3-15c15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V691h339v634h9V47.9l118.8.4 118.7.3z" />
|
||||||
<path d="M195 338.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM574.1 634.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM195 926.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5z" />
|
<path d="M195 338.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M574.1 634.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M195 926.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const ThreePlayersSide = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM397 471.5V895H57V592.3l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6v-102c0-94.2.1-102.6 1.8-110.5 9.2-43.6 38.7-77 80.4-91 17.7-5.9 15.2-5.8 141.6-5.9L397 48v423.5zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.2 1.5 24.4 1.5 108.8v100.2l-8.2.8c-17.1 1.4-32.5 7.5-40.5 15.8-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 9.7 9.7 22.6 14.9 39.9 16.1l8.7.6V895H407V47.9l117.8.4 117.7.3 9 2.3zm92.5 1010c0 172.4.3 164.6-6.1 183.5-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.7-8.6c-.8-10.3-4.3-23-8.2-30-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-18.5 8.7-28.1 24.5-30.3 49.6l-.8 8.4-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-5.1-18.8-5-15.5-5-181.2V904h687v156.9z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M397 471.5V895H57V592.3l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6v-102c0-94.2.1-102.6 1.8-110.5 9.2-43.6 38.7-77 80.4-91 17.7-5.9 15.2-5.8 141.6-5.9L397 48zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.2 1.5 24.4 1.5 108.8v100.2l-8.2.8c-17.1 1.4-32.5 7.5-40.5 15.8-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 9.7 9.7 22.6 14.9 39.9 16.1l8.7.6V895H407V47.9l117.8.4 117.7.3zm92.5 1010c0 172.4.3 164.6-6.1 183.5-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.7-8.6c-.8-10.3-4.3-23-8.2-30-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-18.5 8.7-28.1 24.5-30.3 49.6l-.8 8.4-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-5.1-18.8-5-15.5-5-181.2V904h687z" />
|
||||||
<path d="M192 419.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM581.1 421.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM388.3 1125.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7z" />
|
<path d="M192 419.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M581.1 421.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M388.3 1125.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const TwoPlayersOppositeLandscape = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM396 687v639H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-7.7-10.1-16.6-27.7-19.6-38.6-5.1-19-5-11.7-5-230.5V805.3l11.3-.6c18.4-1 32.5-6 42.3-15C125.7 776 138.1 730 138.1 688c0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.7-45.7l-11.7-.6.3-209.6.3-209.6 2.1-9c2.9-11.9 5.2-18.4 10.3-28.6C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48v639zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.4 1.5 36.5 1.5 214.8v206.2l-11.7.6c-14 .7-24.9 3.3-34.2 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.7 10.7 23.1 15.2 43.9 16.1l12.7.6v207.2c0 227.9.3 214.7-6.1 233.9-13.7 41-49.7 71.9-92.9 79.7-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-638.8-.2-638.8 118.7.4 118.8.3 9 2.3z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M396 687v639H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-7.7-10.1-16.6-27.7-19.6-38.6-5.1-19-5-11.7-5-230.5V805.3l11.3-.6q27.6-1.5 42.3-15C125.7 776 138.1 730 138.1 688c0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.7-45.7l-11.7-.6.3-209.6.3-209.6 2.1-9c2.9-11.9 5.2-18.4 10.3-28.6C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.4 1.5 36.5 1.5 214.8v206.2l-11.7.6c-14 .7-24.9 3.3-34.2 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.7 10.7 23.1 15.2 43.9 16.1l12.7.6v207.2c0 227.9.3 214.7-6.1 233.9-13.7 41-49.7 71.9-92.9 79.7-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-638.8-.2-638.8 118.7.4 118.8.3z" />
|
||||||
<path d="M195 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM573.1 633.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2z" />
|
<path d="M195 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M573.1 633.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const TwoPlayersOppositePortrait = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zm123.9 39.6c1.2 20.3 6.6 34.2 16.8 44 10.3 9.8 39.8 19.5 72.3 23.8 13.9 1.8 40.1 2.1 53 .5 21.2-2.5 48-9.3 60.9-15.5 19.3-9.2 28.8-25.6 30.3-52.3l.6-11.3 62.1.3c61.6.3 62.2.3 71.6 2.7 48.7 12.2 83.1 48.6 91.5 96.8 1.3 7.4 1.5 43.1 1.5 272.9V685H57l.3-266.8.3-266.7 2.1-9c2.9-12 5.2-18.4 10.3-28.6C83.9 86 109.2 64.3 139.7 54c16.6-5.5 20.4-5.8 84.4-5.9l58.6-.1.7 10.7zM744 955.9c0 288.1.4 269.2-6.1 288.5-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.6-11.3c-1.5-27-10.8-43.5-29.8-52.6-13.9-6.7-36.6-12.7-59.4-15.7-13.9-1.8-40-2.1-53-.5-21.2 2.5-48 9.3-60.9 15.5-19.4 9.2-28.8 25.7-30.5 53l-.6 11-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-5.2-19.2-5-7.3-5-286.2V694h687v261.9z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7m123.9 39.6c1.2 20.3 6.6 34.2 16.8 44 10.3 9.8 39.8 19.5 72.3 23.8 13.9 1.8 40.1 2.1 53 .5 21.2-2.5 48-9.3 60.9-15.5 19.3-9.2 28.8-25.6 30.3-52.3l.6-11.3 62.1.3c61.6.3 62.2.3 71.6 2.7 48.7 12.2 83.1 48.6 91.5 96.8 1.3 7.4 1.5 43.1 1.5 272.9V685H57l.3-266.8.3-266.7 2.1-9c2.9-12 5.2-18.4 10.3-28.6C83.9 86 109.2 64.3 139.7 54c16.6-5.5 20.4-5.8 84.4-5.9l58.6-.1zM744 955.9c0 288.1.4 269.2-6.1 288.5-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.6-11.3c-1.5-27-10.8-43.5-29.8-52.6-13.9-6.7-36.6-12.7-59.4-15.7-13.9-1.8-40-2.1-53-.5-21.2 2.5-48 9.3-60.9 15.5-19.4 9.2-28.8 25.7-30.5 53l-.6 11-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-5.2-19.2-5-7.3-5-286.2V694h687z" />
|
||||||
<path d="M391.8 143c-16.5 3-30 12.2-39.1 26.7-7.9 12.6-10 31-5.2 46.1 4.5 14.4 15.5 26.9 29.1 33.4 22.1 10.4 46.1 6.2 63.5-11.1 11.5-11.5 16.9-24.2 16.9-39.9 0-23.4-16.1-45.5-38.6-53.1-7.1-2.3-19.8-3.4-26.6-2.1zM388.3 1119.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7z" />
|
<path d="M391.8 143c-16.5 3-30 12.2-39.1 26.7-7.9 12.6-10 31-5.2 46.1 4.5 14.4 15.5 26.9 29.1 33.4 22.1 10.4 46.1 6.2 63.5-11.1 11.5-11.5 16.9-24.2 16.9-39.9 0-23.4-16.1-45.5-38.6-53.1-7.1-2.3-19.8-3.4-26.6-2.1M388.3 1119.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ const TwoPlayersSameSide = ({
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zm492 31.8c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.4 1.5 42.9 1.5 271.4V682H57V511.3l11.8-.6c18.9-1 32.8-5.9 42.8-15C126.7 482 139.1 436 139.1 394c0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.2-45.7l-12.3-.6.4-62.6c.3-59.6.4-63 2.5-71.6 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c18.5-6.2 3.5-5.8 264.3-5.6l238.5.1 9 2.4zm92.5 904c0 289.2.4 270.2-6.1 289.5-13.4 40-47 69.6-89.9 79.3-6.9 1.6-26.1 1.7-239 2-154.7.3-235.3 0-243-.7-20.8-1.8-38.3-8-55.8-19.7-28.1-18.7-46.6-48.2-51.8-82.8-1.1-6.8-1.4-23.1-1.4-67.1v-58.1l11.8-.6c18.9-1 32.8-5.9 42.8-15 15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.1-45.7l-12.3-.6V692h687v262.9z" />
|
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7m492 31.8c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.4 1.5 42.9 1.5 271.4V682H57V511.3l11.8-.6c18.9-1 32.8-5.9 42.8-15C126.7 482 139.1 436 139.1 394c0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.2-45.7l-12.3-.6.4-62.6c.3-59.6.4-63 2.5-71.6 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c18.5-6.2 3.5-5.8 264.3-5.6l238.5.1zm92.5 904c0 289.2.4 270.2-6.1 289.5-13.4 40-47 69.6-89.9 79.3-6.9 1.6-26.1 1.7-239 2-154.7.3-235.3 0-243-.7-20.8-1.8-38.3-8-55.8-19.7-28.1-18.7-46.6-48.2-51.8-82.8-1.1-6.8-1.4-23.1-1.4-67.1v-58.1l11.8-.6c18.9-1 32.8-5.9 42.8-15 15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.1-45.7l-12.3-.6V692h687z" />
|
||||||
<path d="M196 338.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM196 924.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5z" />
|
<path d="M196 338.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M196 924.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ const LittleGuy = ({
|
|||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<g fill="currentColor">
|
<g fill="currentColor">
|
||||||
<path d="M233.8 9.7c-18.1 17.5-25.6 32.5-25.8 51.5 0 5.9.7 7.8 3.1 7.8 2.8 0 3.6-2.6 4.1-13.2.4-8.9.7-10.2 4.7-18.3 3.6-7.4 6.2-10.7 15.7-20.5C241.9 10.6 247 4.6 247 3.7c0-1.2-1.9-2.7-3.6-2.7-.3 0-4.6 3.9-9.6 8.7zM36.5 54.7c-8.4.6-31.6 4.3-33.6 5.4-2.3 1.2-2.5 4.5-.3 5.3.9.3 1.9.4 2.3.2.3-.2 7.2-1.4 15.1-2.6 12.9-2 17.1-2.2 37.5-1.6 24.9.6 31.9 1.5 52.6 6.9 16.2 4.3 29 8.8 42.3 15.1 8.7 4 10.2 4.5 11.4 3.2 2.3-2.3.8-4.3-5.5-7.4C141.9 70.9 111 61 90.5 57.5c-7.8-1.4-41.4-3.7-47-3.3-1.1.1-4.2.3-7 .5zM205.8 87.6c-16.9 3-35.3 12.3-44.8 22.7-4.5 4.8-9.9 16.6-11.6 25.4-2.6 12.8-.2 40.8 4.7 56.2 8.1 25.4 34.3 55.1 55.6 63.1 12.4 4.6 17.3 5.5 31.3 5.4 7.9 0 14.6-.5 16.3-1.2 2.5-1.1 2.7-1.5 2.7-7.6v-6.5l-4.2 1.6c-6.1 2.3-24.2 2.2-32.7-.1-13.5-3.7-26.2-12.2-36.9-24.5-16.9-19.7-22.9-34.2-25.1-60.6-1.4-17.8 0-27.4 5.6-37.6 3.6-6.8 9.5-11.8 19.5-16.7 21.8-10.7 42.8-11.5 66.3-2.3l7.5 2.9V94.2l-8-2.7c-16.2-5.4-31.1-6.7-46.2-3.9z" />
|
<path d="M233.8 9.7c-18.1 17.5-25.6 32.5-25.8 51.5 0 5.9.7 7.8 3.1 7.8 2.8 0 3.6-2.6 4.1-13.2.4-8.9.7-10.2 4.7-18.3 3.6-7.4 6.2-10.7 15.7-20.5C241.9 10.6 247 4.6 247 3.7c0-1.2-1.9-2.7-3.6-2.7-.3 0-4.6 3.9-9.6 8.7M36.5 54.7c-8.4.6-31.6 4.3-33.6 5.4-2.3 1.2-2.5 4.5-.3 5.3.9.3 1.9.4 2.3.2.3-.2 7.2-1.4 15.1-2.6 12.9-2 17.1-2.2 37.5-1.6 24.9.6 31.9 1.5 52.6 6.9 16.2 4.3 29 8.8 42.3 15.1 8.7 4 10.2 4.5 11.4 3.2 2.3-2.3.8-4.3-5.5-7.4C141.9 70.9 111 61 90.5 57.5c-7.8-1.4-41.4-3.7-47-3.3-1.1.1-4.2.3-7 .5M205.8 87.6c-16.9 3-35.3 12.3-44.8 22.7-4.5 4.8-9.9 16.6-11.6 25.4-2.6 12.8-.2 40.8 4.7 56.2 8.1 25.4 34.3 55.1 55.6 63.1 12.4 4.6 17.3 5.5 31.3 5.4 7.9 0 14.6-.5 16.3-1.2 2.5-1.1 2.7-1.5 2.7-7.6v-6.5l-4.2 1.6c-6.1 2.3-24.2 2.2-32.7-.1-13.5-3.7-26.2-12.2-36.9-24.5-16.9-19.7-22.9-34.2-25.1-60.6-1.4-17.8 0-27.4 5.6-37.6 3.6-6.8 9.5-11.8 19.5-16.7 21.8-10.7 42.8-11.5 66.3-2.3l7.5 2.9V94.2l-8-2.7c-16.2-5.4-31.1-6.7-46.2-3.9" />
|
||||||
<path d="M222.5 132.5c-4.8 4.7-1.9 11.5 4.9 11.5 6.4 0 9-7.9 4-11.9-3.5-2.7-5.9-2.6-8.9.4zM184.5 152.5c-3 3-3.1 5.4-.4 8.9 4 5 11.9 2.4 11.9-4 0-6.8-6.8-9.7-11.5-4.9zM255.8 171.8c-4.7 9.7-13.3 20.3-19.5 23.8-5.9 3.3-7.3 5-7.3 8.6 0 3.6 3.2 6.8 6.8 6.8 4.1 0 12-5.1 18.5-11.8l5.7-6.1v-14.5c0-8-.1-14.6-.2-14.5-.2 0-2 3.5-4 7.7z" />
|
<path d="M222.5 132.5c-4.8 4.7-1.9 11.5 4.9 11.5 6.4 0 9-7.9 4-11.9-3.5-2.7-5.9-2.6-8.9.4M184.5 152.5c-3 3-3.1 5.4-.4 8.9 4 5 11.9 2.4 11.9-4 0-6.8-6.8-9.7-11.5-4.9M255.8 171.8c-4.7 9.7-13.3 20.3-19.5 23.8-5.9 3.3-7.3 5-7.3 8.6s3.2 6.8 6.8 6.8c4.1 0 12-5.1 18.5-11.8l5.7-6.1v-14.5c0-8-.1-14.6-.2-14.5-.2 0-2 3.5-4 7.7" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const Logo = ({
|
|||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path
|
<path
|
||||||
d="M0-196.791c108.629 0 196.791 88.162 196.791 196.791 0 108.629-88.162 196.791-196.791 196.791-108.629 0-196.791-88.162-196.791-196.791 0-108.629 88.162-196.791 196.791-196.791z"
|
d="M0-196.791c108.629 0 196.791 88.162 196.791 196.791S108.63 196.791 0 196.791-196.791 108.63-196.791 0-108.63-196.791 0-196.791z"
|
||||||
style={{
|
style={{
|
||||||
stroke: '#590713',
|
stroke: '#590713',
|
||||||
strokeWidth: 0,
|
strokeWidth: 0,
|
||||||
@@ -39,7 +39,7 @@ const Logo = ({
|
|||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
d="M-17.706-50.704s-4.378 12.973.446 25.583c2.124 5.55 12.255 17.109 17.107 36.364 4.852 19.255 19.679 39.46 19.679 39.46"
|
d="M-17.706-50.704s-4.378 12.973.446 25.583c2.124 5.55 12.255 17.109 17.107 36.364s19.679 39.46 19.679 39.46"
|
||||||
style={{
|
style={{
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
strokeWidth: 4,
|
strokeWidth: 4,
|
||||||
@@ -52,12 +52,12 @@ const Logo = ({
|
|||||||
fillRule: 'nonzero',
|
fillRule: 'nonzero',
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
}}
|
}}
|
||||||
transform="rotate(1 -17129.538 10072.475) scale(2.11279)"
|
transform="rotate(1 -17129.538 10072.475)scale(2.11279)"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
fill="none"
|
fill="none"
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
d="M50.163 56.172S31.68 34 24.46 3.6C17.239-26.8 13.467-35.306 2.42-47.373c-11.046-12.066-26.224-12.189-25.26 2.534.966 14.722 12.84 12.598 14.365 1.93 1.526-10.667-21.8-10.696-32.304 1.854C-53.573-25.77-49.57-2.79-49.57-2.79"
|
d="M50.163 56.172S31.68 34 24.46 3.6 13.467-35.306 2.42-47.373c-11.046-12.066-26.224-12.189-25.26 2.534.966 14.722 12.84 12.598 14.365 1.93 1.526-10.667-21.8-10.696-32.304 1.854C-53.573-25.77-49.57-2.79-49.57-2.79"
|
||||||
style={{
|
style={{
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
strokeWidth: 6,
|
strokeWidth: 6,
|
||||||
@@ -76,7 +76,7 @@ const Logo = ({
|
|||||||
<path
|
<path
|
||||||
fill="none"
|
fill="none"
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
d="M-61.541-71.832s39.965 62.644 41.684 89.093c1.718 26.45 7.326 33.328 17.516 44.667 10.19 11.339 36.055 16.684 32.478-4.845-3.578-21.53-29.514-8.071-16.735 4.07 12.779 12.143 30.514 9.462 39.88-2.368 6.188-7.814 9.732-26.682 7.67-41.524-1.059-7.627-14.72-28.434-14.72-28.434"
|
d="M-61.541-71.832s39.965 62.644 41.684 89.093 7.326 33.328 17.516 44.667 36.055 16.684 32.478-4.845c-3.578-21.53-29.514-8.071-16.735 4.07 12.779 12.143 30.514 9.462 39.88-2.368 6.188-7.814 9.732-26.682 7.67-41.524-1.059-7.627-14.72-28.434-14.72-28.434"
|
||||||
style={{
|
style={{
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
strokeWidth: 6,
|
strokeWidth: 6,
|
||||||
@@ -95,7 +95,7 @@ const Logo = ({
|
|||||||
<path
|
<path
|
||||||
fill="none"
|
fill="none"
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
d="M-26.889-69.574S-9.544-51.826-3.454-23.78c6.09 28.046 24.518 40.828 27.8 61.071 3.284 20.244 3.494 23.63 0 32.283"
|
d="M-26.889-69.574S-9.544-51.826-3.454-23.78s24.518 40.828 27.8 61.071c3.284 20.244 3.494 23.63 0 32.283"
|
||||||
style={{
|
style={{
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
strokeWidth: 6,
|
strokeWidth: 6,
|
||||||
@@ -114,7 +114,7 @@ const Logo = ({
|
|||||||
<path
|
<path
|
||||||
fill="none"
|
fill="none"
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
d="M30.481-85.77S31.897-63.74-.082-1.032C-32.06 61.673-30.478 85.769-30.478 85.769"
|
d="M30.481-85.77S31.897-63.74-.082-1.032s-30.396 86.802-30.396 86.802"
|
||||||
style={{
|
style={{
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
strokeWidth: 6,
|
strokeWidth: 6,
|
||||||
@@ -133,7 +133,7 @@ const Logo = ({
|
|||||||
<path
|
<path
|
||||||
fill="none"
|
fill="none"
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
d="M-30.017 97.228S-.607 52.493 6.833 6.645c7.44-45.849 23.184-103.873 23.184-103.873"
|
d="M-30.017 97.228S-.607 52.493 6.833 6.645 30.016-97.228 30.016-97.228"
|
||||||
style={{
|
style={{
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
strokeWidth: 6,
|
strokeWidth: 6,
|
||||||
@@ -152,7 +152,7 @@ const Logo = ({
|
|||||||
<path
|
<path
|
||||||
fill="none"
|
fill="none"
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
d="M-24.23-79.242s-7.454 19.782-.69 39.5c2.979 8.68 17.988 27.06 24.437 57.033 6.45 29.973 28.24 61.951 28.24 61.951"
|
d="M-24.23-79.242s-7.454 19.782-.69 39.5c2.979 8.68 17.988 27.06 24.437 57.033s28.24 61.951 28.24 61.951"
|
||||||
style={{
|
style={{
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
@@ -166,11 +166,11 @@ const Logo = ({
|
|||||||
fillRule: 'nonzero',
|
fillRule: 'nonzero',
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
}}
|
}}
|
||||||
transform="rotate(-1.005 17359.092 -9769.972) scale(1.36839)"
|
transform="rotate(-1.005 17359.092 -9769.972)scale(1.36839)"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
d="M-45.63-48.464C-71.24-37.592-84.397-27.166-91.043-20.02c-4.07 4.376-3.966 4.338-5.24 6.898a4.617 4.617 0 0 0 .396 4.766c.894 1.234.96 1.326 2.899 3.595C-83.146 6.756-56.46 32.52-4.814 47.45c44.828 12.96 74.528 12.437 89.557 10.58 2.68-.332 2.726-.3 4.041-.593a10.178 10.178 0 0 0 7.957-9.425c.047-.93.022-1.057-.024-2.821-.34-12.824-3.372-35.645-18.26-58.984-22.56-35.37-46.732-40.492-46.732-40.492S.657-68.114-45.63-48.464z"
|
d="M-45.63-48.464C-71.24-37.592-84.397-27.166-91.043-20.02c-4.07 4.376-3.966 4.338-5.24 6.898a4.62 4.62 0 0 0 .396 4.766c.894 1.234.96 1.326 2.899 3.595C-83.146 6.756-56.46 32.52-4.814 47.45c44.828 12.96 74.528 12.437 89.557 10.58 2.68-.332 2.726-.3 4.041-.593a10.18 10.18 0 0 0 7.957-9.425c.047-.93.022-1.057-.024-2.821-.34-12.824-3.372-35.645-18.26-58.984-22.56-35.37-46.732-40.492-46.732-40.492S.657-68.114-45.63-48.464z"
|
||||||
style={{
|
style={{
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
strokeWidth: 6,
|
strokeWidth: 6,
|
||||||
@@ -225,7 +225,7 @@ const Logo = ({
|
|||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
d="M-90.654-77.091S-74.41-28.509-9.055-2.309C56.299 23.89 100.897 9.078 100.897 9.078S61.451 101.65-37.09 70.793C-135.63 39.936-90.654-77.09-90.654-77.09z"
|
d="M-90.654-77.091S-74.41-28.509-9.055-2.309C56.299 23.89 100.897 9.078 100.897 9.078S61.451 101.65-37.09 70.793-90.654-77.09-90.654-77.09z"
|
||||||
style={{
|
style={{
|
||||||
stroke: '#000',
|
stroke: '#000',
|
||||||
strokeWidth: 6,
|
strokeWidth: 6,
|
||||||
|
|||||||
34
src/Icons/generated/Monarch.tsx
Normal file
34
src/Icons/generated/Monarch.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { SVGProps } from 'react';
|
||||||
|
interface SVGRProps {
|
||||||
|
title?: string;
|
||||||
|
titleId?: string;
|
||||||
|
size?: string;
|
||||||
|
}
|
||||||
|
const Monarch = ({
|
||||||
|
title,
|
||||||
|
titleId,
|
||||||
|
...props
|
||||||
|
}: SVGProps<SVGSVGElement> & SVGRProps) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={props.size || 16}
|
||||||
|
height={props.size || 16}
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 52 52"
|
||||||
|
aria-labelledby={titleId}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M46.163 38.82s-8.614 2.73-14.234 3.106c-2.508.167-3.918 0-6.429 0-2.51 0-3.921.167-6.429 0-5.62-.376-14.234-3.107-14.234-3.107s.637-3.944.459-6.471C5.053 28.888 3 24.038 3 24.038s2.897 2.25 4.592 1.294C9.78 24.098 10.5 20 10.5 20s3.006 6.024 7 5.332c2.386-.414 3.327-1.974 4.5-4.016.97-1.69 1.27-4.827 1.27-4.827l1.77-4.827L25.5 10l.46 1.662 1.77 4.827s.3 3.136 1.27 4.827c1.173 2.042 2.388 3.353 4.5 4.016 4.051 1.273 7-5.332 7-5.332s.72 4.098 2.908 5.332c1.695.956 4.592-1.294 4.592-1.294s-2.053 4.85-2.296 8.31c-.178 2.527.46 6.471.46 6.471"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
Monarch.propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
};
|
||||||
|
export default Monarch;
|
||||||
36
src/Icons/generated/NameTag.tsx
Normal file
36
src/Icons/generated/NameTag.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { SVGProps } from 'react';
|
||||||
|
interface SVGRProps {
|
||||||
|
title?: string;
|
||||||
|
titleId?: string;
|
||||||
|
size?: string;
|
||||||
|
}
|
||||||
|
const NameTag = ({
|
||||||
|
title,
|
||||||
|
titleId,
|
||||||
|
...props
|
||||||
|
}: SVGProps<SVGSVGElement> & SVGRProps) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={props.size || 16}
|
||||||
|
height={props.size || 16}
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 52 52"
|
||||||
|
aria-labelledby={titleId}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M33.228 2H48a2 2 0 0 1 2 2v14.772a2 2 0 0 1-.586 1.414L21.721 47.879a3 3 0 0 1-4.242 0L4.12 34.52a3 3 0 0 1 0-4.242L31.814 2.586A2 2 0 0 1 33.228 2m14.105 6a3.333 3.333 0 1 1-6.666 0 3.333 3.333 0 0 1 6.666 0m-16.6 6.644 6.171 6.17 3.773-3.772-.663-.663-3.025 3.026-2.097-2.098 2.784-2.784-.663-.663-2.784 2.785-2.085-2.086 2.977-2.977-.663-.663zm-7.549 7.549.892-.892 7.22 3.025.072-.072-3.025-7.22.892-.892 6.171 6.171-.699.7-4.689-4.69-.06.06 2.76 6.618-.675.675-6.617-2.76-.06.06 4.689 4.689-.7.699zm.69 11.652-.783.783-3.905-8.437.771-.771 8.437 3.905-.783.783-2.368-1.127-2.496 2.496zm.47-5.291-2.024 2.024-1.796-3.772.048-.048zm-2.218 7.04-6.171-6.172-.735.735 4.857 4.858-.06.06-8.232-1.483-.724.724 6.172 6.17.747-.746-4.845-4.846.06-.06 8.208 1.482z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
NameTag.propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
};
|
||||||
|
export default NameTag;
|
||||||
@@ -16,13 +16,21 @@ const PartnerTax = ({
|
|||||||
width={props.size || 16}
|
width={props.size || 16}
|
||||||
height={props.size || 16}
|
height={props.size || 16}
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
paintOrder="stroke fill markers"
|
viewBox="0 0 52 52"
|
||||||
viewBox="0 0 524 524"
|
|
||||||
aria-labelledby={titleId}
|
aria-labelledby={titleId}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M196.7 79.1c-2.1.5-5 1.5-6.5 2.3-3.3 1.8-17.6 16.3-59.3 60.1-60 63.1-60.2 63.3-62.2 78.5-1.7 13.3 1.4 24.2 10 34.7 6.5 7.9 10.6 9.9 40.8 19.8 29.9 9.8 33 11 30.5 11.9-.8.3-8 3.1-16 6.1-8 3.1-19.7 7.5-26 9.9-20.7 7.9-40.3 15.7-45 18.1-2.6 1.3-7.2 5.1-10.2 8.4-6.6 7.1-7.2 8.7-17.9 47.6-19.1 69.1-21.3 78.3-20.3 84.8 1.6 9.6 7.1 16.9 16.3 21.7l4 2 233.6-.2 233.7-.3 3.3-3.7c5.2-5.6 5.3-7.6 1.6-22.3-5.9-23.2-13.2-47.9-15.3-51.3-3.3-5.3-7.3-7.5-30-16.3-12-4.6-21.8-8.6-21.8-8.9 0-.3 6.6-2.7 14.8-5.4 16.2-5.3 19.9-7.5 23.8-14.3 3.4-5.6 3.7-13.7.8-20-1.9-4.2-32.4-37.2-52.7-57.1-9-8.8-9.6-9.2-13.8-9.2-2.4.1-5.4.6-6.5 1.3-1.8 1-17.1 16.5-46 46.6l-7.2 7.5-3.3-3.5c-7.4-7.7-11.5-9.6-79.6-35.9-9-3.5-16.3-6.7-16.3-7.1 0-.4 12.5-4.8 27.8-9.8 16.3-5.3 29.6-10.2 32.2-11.9 6-3.8 13.3-11.8 16.3-17.8 7-13.9 5.4-33.5-3.8-45.3-7.2-9.1-103-109.8-110.2-115.7-5.8-4.8-13.2-6.7-19.6-5.3zm34.8 84.4c5.5 1.4 12.2 3.3 14.9 4.2l4.8 1.5-.6 4.6c-1 7.4-11.3 36.9-16.3 46.8-2.5 5.1-7.6 13.4-11.3 18.5-5.4 7.6-7.7 9.9-12 12.1-6.8 3.6-12.8 3.7-19.7.3-5.8-2.9-9.3-6.8-18.2-20.5-8.2-12.5-13.2-23.1-17.5-37.1-5.1-17-6.4-23.8-4.6-24.9.8-.5 7.9-2.5 15.9-4.4 8.1-1.9 15.5-3.8 16.6-4 1.1-.3 10.1-.3 20-.1 15.1.4 19.6.8 28 3zm-76.1 88.4 46.9 23.5 51.8-25.9c28.5-14.3 52.1-25.7 52.4-25.3.8.8-1.8 7-3.6 8.4-3.9 3.1-93.1 50.5-98.6 52.5-3.1 1-4.2.6-19-7.1-23.6-12.4-66.7-35.6-75.5-40.7-7.6-4.4-11.8-8.5-11.8-11.6 0-2.4 4.5-.3 57.4 26.2zM418.5 318c12 1.2 19.5 3.5 19.5 5.9 0 3.7-7.2 22.7-11 29.2-5.4 9.1-9.9 12.9-15.2 12.9-3.7 0-4.7-.6-8.7-4.8-5.3-5.5-12-17.7-15-27.6-3.2-10.5-3.1-11.3 3.2-13.1 12.1-3.5 15-3.8 27.2-2.5zM88.6 354.5c1.4 3.2 2.4 7.5 2.4 10.1 0 6-14 102.3-15.7 107.8-.5 1.8-.6 1.8-1.5.1-1.4-2.4-7.8-23.3-7.8-25.4-.1-3.8 18.5-98.1 19.2-98.1.5 0 2 2.5 3.4 5.5zm239.8 43.3c5.3 26.8 9.6 49.4 9.6 50.1 0 2.1-9.2 28.1-9.9 28.1-.4 0-1.9-7.8-3.4-17.3-1.4-9.4-5.4-34.7-8.7-56l-6-38.8 3.4-7.4c1.9-4.1 3.9-7.5 4.4-7.5.5 0 5.3 21.9 10.6 48.8zm136.5-43.3c-3 2.9-50.1 27.5-52.5 27.5-1.1 0-8.6-3.6-16.9-8.1-8.2-4.4-18.9-10.1-23.7-12.6-8-4.2-10.8-6.7-9.3-8.2.3-.3 11.7 5 25.3 11.8l24.7 12.3 27-13.5c25.2-12.6 31.2-14.8 25.4-9.2zm-86.6 25.1c4.3 1.4 7.7 2.8 7.5 3-.2.2-3.4 1.5-7.1 2.9l-6.6 2.5-1.6-5.2c-.8-2.8-1.5-5.3-1.5-5.5 0-.6 1.7-.2 9.3 2.3zm100.1 62.8 4.5 23.4-2.3 6.9c-2.9 8.4-2.5 9.8-7.6-24.5-3.7-25.3-3.8-26.5-2.2-29.8 1.5-3.2 1.7-3.3 2.4-1.5.4 1.1 2.7 12.6 5.2 25.5z" />
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M35.145 33.65c-1.297-.691-1.612-2.466-.616-3.568l5.17-5.723a1.07 1.07 0 0 1 1.601 0l5.186 5.739c.99 1.097.686 2.866-.611 3.544l-.221.115-2.577 1.224 3.959 1.774a2.23 2.23 0 0 1 1.237 1.47l1.688 6.351c.19.717-.334 1.424-1.055 1.424H32.094c-.721 0-1.245-.707-1.055-1.424l1.688-6.35a2.23 2.23 0 0 1 1.237-1.471l3.96-1.774s-1.594-.697-2.578-1.224zm6.292-5.77c.423.094.953.355 1.294.539a.52.52 0 0 1 .252.59c-.276 1.082-1.146 4.014-2.483 4.014s-2.207-2.932-2.483-4.013a.52.52 0 0 1 .252-.591c.34-.184.87-.445 1.294-.538.716-.158 1.158-.158 1.874 0m4.25 11.48a.58.58 0 0 1 .083-.398l.352-.553 1.13 5.193a.58.58 0 0 1-.044.373l-.617 1.29zm-9.872-7.562.09.188c.094.197.242.361.426.473l3.892 2.355c.171.103.383.103.554 0l3.892-2.355c.184-.112.332-.276.427-.473l.09-.188-4.439 2.32a.53.53 0 0 1-.494 0zm-2.023 12.177a.58.58 0 0 1-.043-.373l1.129-5.192.352.552a.58.58 0 0 1 .083.397l-.904 5.906z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M10.204 24.07c-2.374-1.227-2.95-4.38-1.128-6.335l9.46-10.163a2 2 0 0 1 2.928 0l9.487 10.191c1.813 1.947 1.255 5.09-1.118 6.293l-.404.205-4.715 2.174 7.243 3.149a4 4 0 0 1 2.263 2.612l3.088 11.276A2 2 0 0 1 35.378 46H4.622a2 2 0 0 1-1.929-2.528L5.78 32.196a4 4 0 0 1 2.263-2.612l7.243-3.15s-2.915-1.237-4.715-2.173zm11.51-10.244c.775.165 1.744.628 2.368.955.382.201.57.633.46 1.05-.504 1.92-2.095 7.125-4.542 7.125s-4.038-5.205-4.542-7.125a.916.916 0 0 1 .46-1.05c.624-.327 1.593-.79 2.368-.955 1.31-.28 2.118-.28 3.428 0m7.775 20.382a1 1 0 0 1 .152-.705l.645-.981 2.065 9.22a1 1 0 0 1-.078.661l-1.13 2.293zm-18.06-13.425.163.332a2 2 0 0 0 .781.84l7.12 4.182a1 1 0 0 0 1.013 0l7.12-4.182a2 2 0 0 0 .782-.84l.163-.332-8.119 4.118a1 1 0 0 1-.904 0zm-3.702 21.62a1 1 0 0 1-.078-.66l2.065-9.221.645.981a1 1 0 0 1 .152.705L8.857 44.696z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,14 +16,13 @@ const Poison = ({
|
|||||||
width={props.size || 16}
|
width={props.size || 16}
|
||||||
height={props.size || 16}
|
height={props.size || 16}
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
overflow="visible"
|
viewBox="0 0 52 52"
|
||||||
paintOrder="stroke fill markers"
|
|
||||||
viewBox="0 0 524 524"
|
|
||||||
aria-labelledby={titleId}
|
aria-labelledby={titleId}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path d="M261.6 32.1 228 57.2v60.5l-6.7 1.2c-16.1 2.9-39.8 10.8-54.6 18.2-9.7 4.9-27.4 17-36.5 24.9-24.3 21.3-40.6 49-46.3 79-1.6 8.3-1.6 33.5 0 42 4.3 22.8 16.3 46.9 32.3 64.6 26 28.9 60 47.5 104 56.9l7.8 1.6.2 55.8.3 55.7 34-25.2 34-25.2.5-30.8.5-30.8 10.5-2.2c27-5.6 54.2-17.8 75.5-33.8 77.9-58.4 77.4-158.4-1.2-216.5-22.5-16.7-46.6-27.2-77-33.7l-8.3-1.7V62.3c0-30.8-.4-55.3-.9-55.3s-16 11.3-34.5 25.1zM228 262v89l-2.7-.6c-5.3-1.3-14.8-5.1-23.8-9.7-31-15.5-50.5-41.7-53.4-71.4-2.5-25.1 6.9-48.5 27-67.8 9.4-9 15.7-13.4 27.6-19.4 7.8-3.9 21.4-9 24.1-9.1.9 0 1.2 18.5 1.2 89zm76.1-86.9c33.2 10.8 58.9 34.5 68.6 63.4 2.5 7.4 2.7 9.2 2.7 23.5 0 14.2-.3 16.2-2.8 23.5-6.7 19.6-18.7 35-37.3 47.8-7.1 4.9-25.9 14-33 16l-5.3 1.5v-88.9c0-48.9.2-88.9.3-88.9.2 0 3.3.9 6.8 2.1z" />
|
<path d="M27.109 3.482a.5.5 0 0 0-.3-.396l-4.148-1.75a.502.502 0 0 0-.694.519c.208 1.787.697 6.165 1.094 11.088a17.5 17.5 0 0 1 3.156-.24q.956.012 1.89.126a303 303 0 0 0-.998-9.347M21.556 50.067a.503.503 0 0 0 .72.516l4.556-2.244a.5.5 0 0 0 .274-.384c.119-.97.539-4.48.932-8.787.159-1.735.313-3.6.441-5.48.203-2.966.34-5.97.33-8.563-.008-2.09-.115-4.459-.272-6.849a13 13 0 0 0-2.32-.255c-.946-.02-1.886.063-2.788.247.135 2.398.219 4.757.213 6.857-.008 2.589-.156 5.54-.372 8.502a239 239 0 0 1-.454 5.352 330 330 0 0 1-1.26 11.088" />
|
||||||
|
<path d="M26.217 12.702a17.5 17.5 0 0 0-3.156.24c-6.351 1.072-11.603 5.623-12.035 12.183-.455 6.93 5.072 12.492 11.79 13.854.166-1.734.323-3.545.454-5.352-4.025-1.055-7.345-4.36-6.896-8.502.408-3.76 3.43-6.119 7.055-6.857a12.7 12.7 0 0 1 2.788-.247c.788.017 1.567.103 2.32.255 3.65.742 6.675 3.067 7.089 6.85.464 4.245-2.997 7.581-7.147 8.561-.128 1.88-.282 3.746-.44 5.481 7.254-.93 13.419-6.7 12.934-14.043-.452-6.861-6.119-11.483-12.867-12.296a18 18 0 0 0-1.89-.127" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const ResetGame = ({
|
|||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path
|
<path
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
d="M129.3 50.7C105.7 81.6 95 95.8 82.6 113c-14.3 19.7-16.7 25.2-14.6 33.2 2.5 9.4 9 14.2 21.3 15.8 8.2 1.1 116.4 3.7 117.2 2.8.3-.3-3.3-8.8-8-18.8s-8.8-19.3-9.1-20.5c-.5-2.1-.1-2.3 8.3-3.4 4.9-.6 20.3-1.6 34.2-2.2 65.4-2.7 102.7 6.4 133.2 32.4 31.4 26.8 48.8 75.9 44.9 126.8-6.7 87.6-61.2 133.4-155.3 130.6-45.4-1.3-78.1-13.8-103.7-39.6-23.5-23.8-39-60.5-41.9-99.4-.9-12-2-15.4-6.7-20-5.2-5.2-7.9-5.7-29.3-5.6-24 .1-27.9 1.2-32.1 9.5-1.6 3-2 5.9-2 13.9 0 35.9 12.4 76.8 32.8 108 34.1 52.5 88.1 87.3 152.2 98.2 16.3 2.8 57.2 2.5 73.5-.5 46.1-8.4 84.5-28.3 116.4-60.3 42.5-42.7 65-104 60.3-164.1-4.2-52.8-25-98.7-61.2-134.8-38.8-38.8-87.2-59.7-145-62.6-24.6-1.2-73.1 2.8-99.1 8.2-2.5.5-2.9-.2-10.9-17.1-4.6-9.6-8.7-17.5-9.1-17.5-.3 0-9.1 11.1-19.6 24.7z"
|
d="M129.3 50.7C105.7 81.6 95 95.8 82.6 113c-14.3 19.7-16.7 25.2-14.6 33.2 2.5 9.4 9 14.2 21.3 15.8 8.2 1.1 116.4 3.7 117.2 2.8.3-.3-3.3-8.8-8-18.8s-8.8-19.3-9.1-20.5c-.5-2.1-.1-2.3 8.3-3.4 4.9-.6 20.3-1.6 34.2-2.2 65.4-2.7 102.7 6.4 133.2 32.4 31.4 26.8 48.8 75.9 44.9 126.8-6.7 87.6-61.2 133.4-155.3 130.6-45.4-1.3-78.1-13.8-103.7-39.6-23.5-23.8-39-60.5-41.9-99.4-.9-12-2-15.4-6.7-20-5.2-5.2-7.9-5.7-29.3-5.6-24 .1-27.9 1.2-32.1 9.5-1.6 3-2 5.9-2 13.9 0 35.9 12.4 76.8 32.8 108 34.1 52.5 88.1 87.3 152.2 98.2 16.3 2.8 57.2 2.5 73.5-.5 46.1-8.4 84.5-28.3 116.4-60.3 42.5-42.7 65-104 60.3-164.1-4.2-52.8-25-98.7-61.2-134.8-38.8-38.8-87.2-59.7-145-62.6-24.6-1.2-73.1 2.8-99.1 8.2-2.5.5-2.9-.2-10.9-17.1-4.6-9.6-8.7-17.5-9.1-17.5-.3 0-9.1 11.1-19.6 24.7"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ const Skull = ({
|
|||||||
>
|
>
|
||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<g fill="currentColor">
|
<g fill="currentColor">
|
||||||
<path d="M237.2 37.1c-30.6 2.9-64.8 15-89.4 31.6-12.2 8.2-31.7 23.4-38.3 30-15.2 14.9-24.6 33.8-27.5 55.1-4.5 32.9 10.8 64.5 38.7 79.8 16.3 8.9 22.3 12.7 28.1 18 15 13.5 23.5 31.4 24.9 52.3.8 11.5 3.2 17.9 8.9 23.1 8.6 7.9 4.4 7.5 73.4 7.5 60.6 0 61.6 0 66-2.1 10-4.8 14.7-12.3 16-25.7 1.3-12.5 1.8-15.2 4.2-22.7 3.9-12.1 11.9-24.4 21.2-32.7 5.5-4.9 8.1-6.5 25.3-16 16.4-9.1 26.1-19.1 33.9-34.8 5.6-11.4 7.4-18.5 8.1-30.8 1.5-26.6-9.6-54.2-29.4-72.7-8.8-8.3-27.3-22.7-38.3-29.8-35.8-23.3-82.1-34.3-125.8-30.1zm-46 72c15 3.2 26.2 13.8 28.7 27.3 1.7 9.2-2.6 18.2-14 29.7-14.1 14.1-28.2 21.9-40.9 22.6-6.6.4-7.9.2-11-1.9-7.5-5-10.9-17.2-11-39 0-14.7 1.9-20.6 8.9-27.6 10.1-10 24.7-14.2 39.3-11.1zm145 0c11.6 2.5 22.7 10.8 26.9 20.2 2.2 4.8 2.4 6.4 2.3 20.7-.1 22.2-3.1 32.2-11 37.1-2.9 1.8-4.6 2.1-10.8 1.7-6.2-.3-8.6-1-15.9-4.6-15.2-7.5-30.9-21.8-36.5-33.2-11.3-23.1 15.6-48.1 45-41.9zm-73.5 102.8c4.7 3.4 11.8 13.7 13.3 19.3 2 7.3-2 13.6-11.5 17.9-12.1 5.6-29.5-2.7-29.5-14.1 0-6.8 7.2-19 13.9-23.6 4.8-3.2 8.9-3.1 13.8.5z" />
|
<path d="M237.2 37.1c-30.6 2.9-64.8 15-89.4 31.6-12.2 8.2-31.7 23.4-38.3 30-15.2 14.9-24.6 33.8-27.5 55.1-4.5 32.9 10.8 64.5 38.7 79.8 16.3 8.9 22.3 12.7 28.1 18 15 13.5 23.5 31.4 24.9 52.3.8 11.5 3.2 17.9 8.9 23.1 8.6 7.9 4.4 7.5 73.4 7.5 60.6 0 61.6 0 66-2.1 10-4.8 14.7-12.3 16-25.7 1.3-12.5 1.8-15.2 4.2-22.7 3.9-12.1 11.9-24.4 21.2-32.7 5.5-4.9 8.1-6.5 25.3-16 16.4-9.1 26.1-19.1 33.9-34.8 5.6-11.4 7.4-18.5 8.1-30.8 1.5-26.6-9.6-54.2-29.4-72.7-8.8-8.3-27.3-22.7-38.3-29.8-35.8-23.3-82.1-34.3-125.8-30.1m-46 72c15 3.2 26.2 13.8 28.7 27.3 1.7 9.2-2.6 18.2-14 29.7-14.1 14.1-28.2 21.9-40.9 22.6-6.6.4-7.9.2-11-1.9-7.5-5-10.9-17.2-11-39 0-14.7 1.9-20.6 8.9-27.6 10.1-10 24.7-14.2 39.3-11.1m145 0c11.6 2.5 22.7 10.8 26.9 20.2 2.2 4.8 2.4 6.4 2.3 20.7-.1 22.2-3.1 32.2-11 37.1-2.9 1.8-4.6 2.1-10.8 1.7-6.2-.3-8.6-1-15.9-4.6-15.2-7.5-30.9-21.8-36.5-33.2-11.3-23.1 15.6-48.1 45-41.9m-73.5 102.8c4.7 3.4 11.8 13.7 13.3 19.3 2 7.3-2 13.6-11.5 17.9-12.1 5.6-29.5-2.7-29.5-14.1 0-6.8 7.2-19 13.9-23.6 4.8-3.2 8.9-3.1 13.8.5" />
|
||||||
<path d="M56.8 304.7c-1.4 1-3.6 3.1-4.9 4.6-5.1 6.6-8.2 25.8-4.7 29.3 2.9 2.9 19.8 7.1 67.8 16.9 36.1 7.4 79 17.5 79 18.7 0 .4-.6.8-1.4.8-2.3 0-73.5 23.6-95.6 31.7-20.8 7.6-40.7 16.4-44.8 19.9-1.6 1.3-2.7 4.1-3.8 9.4-2.9 14.3.2 20.8 12.5 25.5 6.3 2.4 7.3 2.5 11.7 1.4 6.2-1.6 18.9-6.3 48.4-18.2 56.7-22.9 114.7-42.6 140.7-47.7l5.8-1.2 24.5 8.5c13.5 4.7 45.2 16 70.5 25.2 78.6 28.6 95.9 34 101.1 31.6 3.2-1.4 5.4-6.7 5.4-12.8 0-9.6-3.3-16.3-10.8-22-4.8-3.6-8.7-4.9-27.7-8.8-7.1-1.5-15.9-3.8-19.5-5.2-3.6-1.3-17.1-6.5-30-11.5-12.9-4.9-30.4-11.4-38.8-14.4-8.4-2.9-15.8-5.7-16.4-6-.6-.4 4.2-2.2 10.8-4.1 11.6-3.3 27-7.7 58.3-16.9 17.6-5.2 48.9-16.6 54.1-19.6 8.8-5.2 14.8-13.3 17.4-23.3 1.3-5.3 1.4-6.2.1-8.2-2-3-5.3-3.7-13.4-2.9-19.7 1.8-89.3 19.4-175.4 44l-29.8 8.6-18.2-5.1c-20.2-5.6-63.5-18.8-91.2-27.9-18.1-5.9-52.3-16.5-62.5-19.4-9.6-2.7-16.1-3-19.2-.9z" />
|
<path d="M56.8 304.7c-1.4 1-3.6 3.1-4.9 4.6-5.1 6.6-8.2 25.8-4.7 29.3 2.9 2.9 19.8 7.1 67.8 16.9 36.1 7.4 79 17.5 79 18.7 0 .4-.6.8-1.4.8-2.3 0-73.5 23.6-95.6 31.7-20.8 7.6-40.7 16.4-44.8 19.9-1.6 1.3-2.7 4.1-3.8 9.4-2.9 14.3.2 20.8 12.5 25.5 6.3 2.4 7.3 2.5 11.7 1.4 6.2-1.6 18.9-6.3 48.4-18.2 56.7-22.9 114.7-42.6 140.7-47.7l5.8-1.2 24.5 8.5c13.5 4.7 45.2 16 70.5 25.2 78.6 28.6 95.9 34 101.1 31.6 3.2-1.4 5.4-6.7 5.4-12.8 0-9.6-3.3-16.3-10.8-22-4.8-3.6-8.7-4.9-27.7-8.8-7.1-1.5-15.9-3.8-19.5-5.2-3.6-1.3-17.1-6.5-30-11.5-12.9-4.9-30.4-11.4-38.8-14.4-8.4-2.9-15.8-5.7-16.4-6-.6-.4 4.2-2.2 10.8-4.1 11.6-3.3 27-7.7 58.3-16.9 17.6-5.2 48.9-16.6 54.1-19.6 8.8-5.2 14.8-13.3 17.4-23.3 1.3-5.3 1.4-6.2.1-8.2-2-3-5.3-3.7-13.4-2.9-19.7 1.8-89.3 19.4-175.4 44l-29.8 8.6-18.2-5.1c-20.2-5.6-63.5-18.8-91.2-27.9-18.1-5.9-52.3-16.5-62.5-19.4-9.6-2.7-16.1-3-19.2-.9" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,23 +23,23 @@ const BuyMeCoffee = ({
|
|||||||
{title ? <title id={titleId}>{title}</title> : null}
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
<path
|
<path
|
||||||
fill="#0D0C22"
|
fill="#0D0C22"
|
||||||
d="m791.109 297.518-.878-.516-2.03-.619a4.833 4.833 0 0 0 2.908 1.135ZM803.896 388.891l-.98.275.98-.275ZM791.484 297.377a1.773 1.773 0 0 1-.366-.087 1.42 1.42 0 0 0 0 .244.738.738 0 0 0 .366-.157Z"
|
d="m791.109 297.518-.878-.516-2.03-.619a4.83 4.83 0 0 0 2.908 1.135M803.896 388.891l-.98.275zM791.484 297.377a1.8 1.8 0 0 1-.366-.087 1.4 1.4 0 0 0 0 .244.74.74 0 0 0 .366-.157"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
fill="#0D0C22"
|
fill="#0D0C22"
|
||||||
d="M791.113 297.529h.131v-.082l-.131.082ZM803.111 388.726l1.48-.843.551-.31.499-.533a8.447 8.447 0 0 0-2.53 1.686ZM793.669 299.515l-1.446-1.377-.98-.533a4.077 4.077 0 0 0 2.426 1.91ZM430.019 1186.18a7.55 7.55 0 0 0-2.943 2.27l.912-.58c.62-.57 1.497-1.24 2.031-1.69ZM641.187 1144.63c0-1.3-.636-1.06-.482 3.58 0-.37.155-.75.224-1.11.086-.83.155-1.64.258-2.47ZM619.284 1186.18a7.546 7.546 0 0 0-2.942 2.27l.912-.58c.619-.57 1.497-1.24 2.03-1.69ZM281.304 1196.06a6.299 6.299 0 0 0-3.097-1.45c.929.45 1.858.9 2.477 1.24l.62.21ZM247.841 1164.01a9.895 9.895 0 0 0-1.222-3.85c.474 1.23.87 2.5 1.187 3.78l.035.07Z"
|
d="M791.113 297.529h.131v-.082zM803.111 388.726l1.48-.843.551-.31.499-.533a8.5 8.5 0 0 0-2.53 1.686M793.669 299.515l-1.446-1.377-.98-.533a4.08 4.08 0 0 0 2.426 1.91M430.019 1186.18a7.55 7.55 0 0 0-2.943 2.27l.912-.58c.62-.57 1.497-1.24 2.031-1.69M641.187 1144.63c0-1.3-.636-1.06-.482 3.58 0-.37.155-.75.224-1.11.086-.83.155-1.64.258-2.47M619.284 1186.18a7.55 7.55 0 0 0-2.942 2.27l.912-.58c.619-.57 1.497-1.24 2.03-1.69M281.304 1196.06a6.3 6.3 0 0 0-3.097-1.45c.929.45 1.858.9 2.477 1.24zM247.841 1164.01a9.9 9.9 0 0 0-1.222-3.85c.474 1.23.87 2.5 1.187 3.78z"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
fill="#FD0"
|
fill="#FD0"
|
||||||
d="M472.623 590.836c-45.941 19.667-98.077 41.966-165.647 41.966a313.577 313.577 0 0 1-83.623-11.528l46.733 479.806a80.166 80.166 0 0 0 25.593 52.38 80.18 80.18 0 0 0 54.313 21.19s66.262 3.44 88.373 3.44c23.796 0 95.151-3.44 95.151-3.44 20.12 0 39.503-7.57 54.303-21.2a80.149 80.149 0 0 0 25.587-52.37l50.053-530.204c-22.368-7.639-44.943-12.715-70.391-12.715-44.014-.017-79.477 15.142-120.445 32.675Z"
|
d="M472.623 590.836c-45.941 19.667-98.077 41.966-165.647 41.966a313.6 313.6 0 0 1-83.623-11.528l46.733 479.806a80.17 80.17 0 0 0 25.593 52.38 80.18 80.18 0 0 0 54.313 21.19s66.262 3.44 88.373 3.44c23.796 0 95.151-3.44 95.151-3.44 20.12 0 39.503-7.57 54.303-21.2a80.15 80.15 0 0 0 25.587-52.37l50.053-530.204c-22.368-7.639-44.943-12.715-70.391-12.715-44.014-.017-79.477 15.142-120.445 32.675"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
fill="#0D0C22"
|
fill="#0D0C22"
|
||||||
d="m78.689 386.132.79.74.517.31a7.95 7.95 0 0 0-1.307-1.05Z"
|
d="m78.689 386.132.79.74.517.31a8 8 0 0 0-1.307-1.05"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
fill="#0D0C22"
|
fill="#0D0C22"
|
||||||
d="m879.567 341.849-7.037-35.497c-6.315-31.849-20.648-61.943-53.34-73.454-10.479-3.683-22.369-5.265-30.404-12.888-8.035-7.622-10.41-19.46-12.268-30.438-3.442-20.149-6.676-40.315-10.204-60.429-3.045-17.293-5.454-36.719-13.386-52.583-10.324-21.302-31.746-33.76-53.048-42.001a305.493 305.493 0 0 0-33.363-10.324C613.297 10.195 557.342 5.033 502.591 2.09a1376.266 1376.266 0 0 0-197.169 3.27c-48.797 4.439-100.193 9.807-146.564 26.687-16.948 6.177-34.413 13.593-47.3 26.687-15.813 16.088-20.975 40.969-9.43 61.031 8.208 14.247 22.111 24.313 36.857 30.972a298.948 298.948 0 0 0 59.844 19.478c57.297 12.664 116.642 17.636 175.178 19.753a1333.99 1333.99 0 0 0 194.433-6.35 1106.995 1106.995 0 0 0 47.817-6.314c18.738-2.874 30.765-27.376 25.242-44.445-6.608-20.406-24.365-28.321-44.444-25.241-2.96.464-5.902.894-8.862 1.324l-2.133.31c-6.803.861-13.605 1.663-20.407 2.409a1083.055 1083.055 0 0 1-42.259 3.717c-31.626 2.202-63.337 3.217-95.031 3.269-31.144 0-62.305-.878-93.38-2.925a1187.382 1187.382 0 0 1-42.431-3.545 1019.94 1019.94 0 0 1-19.219-2.168l-6.092-.774-1.324-.189-6.315-.912c-12.905-1.945-25.81-4.181-38.577-6.883a5.8 5.8 0 0 1 0-11.322h.241c11.064-2.357 22.213-4.37 33.397-6.125 3.729-.585 7.468-1.159 11.219-1.72h.103c7.003-.465 14.041-1.722 21.009-2.547A1336.183 1336.183 0 0 1 469.538 73.1c29.577.86 59.138 2.599 88.578 5.593a979.44 979.44 0 0 1 18.927 2.116c2.409.293 4.835.636 7.262.93l4.886.705a678.058 678.058 0 0 1 42.517 7.725c20.889 4.543 47.714 6.022 57.005 28.907 2.96 7.261 4.302 15.331 5.936 22.953l2.082 9.722c.055.174.095.353.121.533 4.921 22.942 9.848 45.884 14.78 68.826a12.608 12.608 0 0 1-2.006 9.871 12.612 12.612 0 0 1-8.593 5.254h-.138l-3.011.413-2.976.395c-9.43 1.228-18.87 2.375-28.322 3.442a1829.308 1829.308 0 0 1-55.938 5.506 1957.204 1957.204 0 0 1-111.55 6.074c-18.984.504-37.963.74-56.936.705a1975.736 1975.736 0 0 1-225.989-13.146c-8.122-.963-16.243-1.996-24.365-3.045 6.298.809-4.577-.62-6.779-.929a1447.245 1447.245 0 0 1-15.486-2.254c-17.327-2.599-34.55-5.799-51.843-8.604-20.906-3.441-40.9-1.72-59.81 8.604a86.994 86.994 0 0 0-36.012 37.338c-8.156 16.862-10.582 35.221-14.23 53.34C4 342.193-1.678 361.688.473 380.288 5.1 420.431 33.165 453.054 73.53 460.35c37.975 6.882 76.156 12.457 114.44 17.206a2114.804 2114.804 0 0 0 489.988 2.822 25.814 25.814 0 0 1 21.003 7.339 25.806 25.806 0 0 1 7.491 20.948l-3.82 37.132-23.091 225.077a178840.62 178840.62 0 0 1-31.126 302.866c-2.203 21.84-2.512 44.36-6.659 65.94-6.539 33.93-29.509 54.77-63.027 62.39a439.172 439.172 0 0 1-93.569 10.94c-34.912.19-69.806-1.36-104.718-1.17-37.27.21-82.918-3.23-111.687-30.97-25.277-24.36-28.77-62.51-32.211-95.5-4.588-43.67-9.136-87.331-13.645-130.989l-25.293-242.766-16.363-157.077c-.276-2.598-.551-5.162-.809-7.778-1.962-18.737-15.228-37.079-36.134-36.133-17.894.791-38.232 16.002-36.133 36.133l12.13 116.454 25.087 240.89a378681.11 378681.11 0 0 1 21.388 205.306c1.377 13.11 2.667 26.26 4.112 39.37 7.864 71.65 62.58 110.26 130.339 121.13 39.575 6.37 80.113 7.68 120.273 8.33 51.482.83 103.48 2.81 154.118-6.52 75.038-13.77 131.337-63.87 139.372-141.59 2.295-22.44 4.589-44.88 6.883-67.33 7.628-74.241 15.245-148.487 22.85-222.739l24.881-242.61 11.408-111.188a25.812 25.812 0 0 1 20.785-22.696c21.456-4.181 41.967-11.322 57.229-27.651 24.295-25.998 29.13-59.895 20.544-94.067ZM72.43 365.835c.327-.155-.275 2.649-.533 3.957-.052-1.979.051-3.734.533-3.957Zm2.082 16.105c.172-.121.688.568 1.222 1.394-.809-.758-1.325-1.325-1.24-1.394h.018Zm2.048 2.701c.74 1.256 1.135 2.048 0 0Zm4.112 3.338h.103c0 .121.19.241.258.362a2.666 2.666 0 0 0-.378-.362h.017Zm720.124-4.99c-7.708 7.33-19.323 10.737-30.8 12.441-128.704 19.099-259.283 28.769-389.399 24.502-93.121-3.183-185.261-13.525-277.453-26.55-9.034-1.273-18.824-2.925-25.036-9.584-11.7-12.561-5.953-37.854-2.908-53.03 2.788-13.903 8.122-32.434 24.657-34.413 25.81-3.028 55.783 7.863 81.318 11.735a1539.798 1539.798 0 0 0 92.57 11.27c132.18 12.045 266.58 10.169 398.175-7.45a1661.346 1661.346 0 0 0 71.699-11.236c21.216-3.803 44.737-10.943 57.556 11.029 8.792 14.97 9.962 34.998 8.603 51.912a28.942 28.942 0 0 1-8.999 19.374h.017Z"
|
d="m879.567 341.849-7.037-35.497c-6.315-31.849-20.648-61.943-53.34-73.454-10.479-3.683-22.369-5.265-30.404-12.888-8.035-7.622-10.41-19.46-12.268-30.438-3.442-20.149-6.676-40.315-10.204-60.429-3.045-17.293-5.454-36.719-13.386-52.583-10.324-21.302-31.746-33.76-53.048-42.001a306 306 0 0 0-33.363-10.324C613.297 10.195 557.342 5.033 502.591 2.09a1376 1376 0 0 0-197.169 3.27c-48.797 4.439-100.193 9.807-146.564 26.687-16.948 6.177-34.413 13.593-47.3 26.687-15.813 16.088-20.975 40.969-9.43 61.031 8.208 14.247 22.111 24.313 36.857 30.972a299 299 0 0 0 59.844 19.478c57.297 12.664 116.642 17.636 175.178 19.753a1334 1334 0 0 0 194.433-6.35 1107 1107 0 0 0 47.817-6.314c18.738-2.874 30.765-27.376 25.242-44.445-6.608-20.406-24.365-28.321-44.444-25.241-2.96.464-5.902.894-8.862 1.324l-2.133.31q-10.204 1.29-20.407 2.409a1083 1083 0 0 1-42.259 3.717c-31.626 2.202-63.337 3.217-95.031 3.269-31.144 0-62.305-.878-93.38-2.925a1187 1187 0 0 1-42.431-3.545 1020 1020 0 0 1-19.219-2.168l-6.092-.774-1.324-.189-6.315-.912c-12.905-1.945-25.81-4.181-38.577-6.883a5.8 5.8 0 0 1 0-11.322h.241c11.064-2.357 22.213-4.37 33.397-6.125q5.593-.878 11.219-1.72h.103c7.003-.465 14.041-1.722 21.009-2.547A1336 1336 0 0 1 469.538 73.1c29.577.86 59.138 2.599 88.578 5.593a979 979 0 0 1 18.927 2.116c2.409.293 4.835.636 7.262.93l4.886.705a678 678 0 0 1 42.517 7.725c20.889 4.543 47.714 6.022 57.005 28.907 2.96 7.261 4.302 15.331 5.936 22.953l2.082 9.722q.082.262.121.533 7.382 34.413 14.78 68.826a12.6 12.6 0 0 1-2.006 9.871 12.61 12.61 0 0 1-8.593 5.254h-.138l-3.011.413-2.976.395q-14.144 1.842-28.322 3.442a1829 1829 0 0 1-55.938 5.506 1957 1957 0 0 1-111.55 6.074q-28.476.757-56.936.705a1976 1976 0 0 1-225.989-13.146c-8.122-.963-16.243-1.996-24.365-3.045 6.298.809-4.577-.62-6.779-.929a1447 1447 0 0 1-15.486-2.254c-17.327-2.599-34.55-5.799-51.843-8.604-20.906-3.441-40.9-1.72-59.81 8.604a87 87 0 0 0-36.012 37.338c-8.156 16.862-10.582 35.221-14.23 53.34C4 342.193-1.678 361.688.473 380.288 5.1 420.431 33.165 453.054 73.53 460.35c37.975 6.882 76.156 12.457 114.44 17.206a2114.8 2114.8 0 0 0 489.988 2.822 25.81 25.81 0 0 1 21.003 7.339 25.8 25.8 0 0 1 7.491 20.948l-3.82 37.132-23.091 225.077a178841 178841 0 0 1-31.126 302.866c-2.203 21.84-2.512 44.36-6.659 65.94-6.539 33.93-29.509 54.77-63.027 62.39a439 439 0 0 1-93.569 10.94c-34.912.19-69.806-1.36-104.718-1.17-37.27.21-82.918-3.23-111.687-30.97-25.277-24.36-28.77-62.51-32.211-95.5q-6.882-65.504-13.645-130.989l-25.293-242.766-16.363-157.077c-.276-2.598-.551-5.162-.809-7.778-1.962-18.737-15.228-37.079-36.134-36.133-17.894.791-38.232 16.002-36.133 36.133l12.13 116.454 25.087 240.89a378681 378681 0 0 1 21.388 205.306c1.377 13.11 2.667 26.26 4.112 39.37 7.864 71.65 62.58 110.26 130.339 121.13 39.575 6.37 80.113 7.68 120.273 8.33 51.482.83 103.48 2.81 154.118-6.52 75.038-13.77 131.337-63.87 139.372-141.59 2.295-22.44 4.589-44.88 6.883-67.33q11.442-111.361 22.85-222.739l24.881-242.61 11.408-111.188a25.812 25.812 0 0 1 20.785-22.696c21.456-4.181 41.967-11.322 57.229-27.651 24.295-25.998 29.13-59.895 20.544-94.067M72.43 365.835c.327-.155-.275 2.649-.533 3.957-.052-1.979.051-3.734.533-3.957m2.082 16.105c.172-.121.688.568 1.222 1.394-.809-.758-1.325-1.325-1.24-1.394zm2.048 2.701c.74 1.256 1.135 2.048 0 0m4.112 3.338h.103c0 .121.19.241.258.362a2.7 2.7 0 0 0-.378-.362zm720.124-4.99c-7.708 7.33-19.323 10.737-30.8 12.441-128.704 19.099-259.283 28.769-389.399 24.502-93.121-3.183-185.261-13.525-277.453-26.55-9.034-1.273-18.824-2.925-25.036-9.584-11.7-12.561-5.953-37.854-2.908-53.03 2.788-13.903 8.122-32.434 24.657-34.413 25.81-3.028 55.783 7.863 81.318 11.735a1540 1540 0 0 0 92.57 11.27c132.18 12.045 266.58 10.169 398.175-7.45a1661 1661 0 0 0 71.699-11.236c21.216-3.803 44.737-10.943 57.556 11.029 8.792 14.97 9.962 34.998 8.603 51.912a28.94 28.94 0 0 1-8.999 19.374z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -31,14 +31,14 @@ const KoFi = ({
|
|||||||
<use x={97} y={83} href="#a" />
|
<use x={97} y={83} href="#a" />
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
d="m521.5 52.4 410 .2 12.5 2.1c56.6 9.6 99.9 28 143.5 61.2 11.7 8.9 33.5 30.2 42.5 41.6 31 39.2 50.6 86.9 59.2 143.8 3 19.5 3.2 65 .5 83.2-7.3 48.4-24.3 89.7-52.3 127.3-10.5 13.9-35.9 39.8-49.4 50.2-52.3 40.3-116.3 63.2-188.8 67.6-10.4.6-11.3.8-11.6 2.8-.3 1.1-1.1 7.5-2 14.1-7.1 54-32.5 98.7-70.7 124.1-16.3 10.9-38.6 20.3-58.8 25-9.9 2.3-102.7 3.2-354.1 3.6-230.9.3-217.6.6-239.5-4.9-41.1-10.2-73.8-34.9-91.3-69-10.6-20.6-14.9-39-16.2-68.3-1.8-38.7-3.2-261.9-2.7-402 .5-138.6.6-141.7 2.6-149.2 7.2-27.3 27.3-47.5 52.6-52.8 2.2-.4 188.5-.7 414-.6zm-4.6 57.6H117.3l-2 2.2c-4.9 5.4-4.7-1.7-5.1 148.8-.4 142.7 1.1 364 2.8 395.5.7 13.5 1.4 18.8 3.5 26 8.7 29.8 29 48 63.2 56.7l8.8 2.3 216-.3c118.8-.2 243.9-.7 278-1.1l62-.8 8.5-2.8c40.5-13.2 64.2-41.6 73.5-87.9 2.5-12.7 4.5-32.6 4.5-45.1 0-13.1 5-22.4 15.1-28l5.4-3 28-.6c16.4-.4 33-1.4 40-2.3 64.9-8.9 116.3-33.4 156-74.7 32.8-34.1 52.5-77.3 57.6-126.8 1.5-14.3.6-46.8-1.6-61.1-8.5-55.1-29.1-98-62.6-130.6-11.8-11.4-17.7-16-33.9-26.7-33.1-21.6-66.1-33.5-106-38.2-10.8-1.3-67.3-1.5-412.1-1.5z"
|
d="m521.5 52.4 410 .2 12.5 2.1c56.6 9.6 99.9 28 143.5 61.2 11.7 8.9 33.5 30.2 42.5 41.6 31 39.2 50.6 86.9 59.2 143.8 3 19.5 3.2 65 .5 83.2-7.3 48.4-24.3 89.7-52.3 127.3-10.5 13.9-35.9 39.8-49.4 50.2-52.3 40.3-116.3 63.2-188.8 67.6-10.4.6-11.3.8-11.6 2.8-.3 1.1-1.1 7.5-2 14.1-7.1 54-32.5 98.7-70.7 124.1-16.3 10.9-38.6 20.3-58.8 25-9.9 2.3-102.7 3.2-354.1 3.6-230.9.3-217.6.6-239.5-4.9-41.1-10.2-73.8-34.9-91.3-69-10.6-20.6-14.9-39-16.2-68.3-1.8-38.7-3.2-261.9-2.7-402 .5-138.6.6-141.7 2.6-149.2 7.2-27.3 27.3-47.5 52.6-52.8 2.2-.4 188.5-.7 414-.6m-4.6 57.6H117.3l-2 2.2c-4.9 5.4-4.7-1.7-5.1 148.8-.4 142.7 1.1 364 2.8 395.5.7 13.5 1.4 18.8 3.5 26 8.7 29.8 29 48 63.2 56.7l8.8 2.3 216-.3c118.8-.2 243.9-.7 278-1.1l62-.8 8.5-2.8c40.5-13.2 64.2-41.6 73.5-87.9 2.5-12.7 4.5-32.6 4.5-45.1 0-13.1 5-22.4 15.1-28l5.4-3 28-.6c16.4-.4 33-1.4 40-2.3 64.9-8.9 116.3-33.4 156-74.7 32.8-34.1 52.5-77.3 57.6-126.8 1.5-14.3.6-46.8-1.6-61.1-8.5-55.1-29.1-98-62.6-130.6-11.8-11.4-17.7-16-33.9-26.7-33.1-21.6-66.1-33.5-106-38.2-10.8-1.3-67.3-1.5-412.1-1.5"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
d="M939.5 185.4c33.5 6.1 61.7 21.9 83.2 46.5 17.6 20.1 27.6 40.3 33.4 67 4.2 19.7 3.7 56.3-1.3 79.1-10.6 49.7-36.5 83.1-81.3 105-15.9 7.8-27.3 11.6-38.6 13-14.1 1.8-71.1 2.5-78.1 1.1-11-2.3-19.3-10.4-22.3-21.7-2.3-8.8-2.3-261 0-269.8 2.8-10.8 10-18.1 20.8-21.1 7.6-2.2 71.2-1.5 84.2.9zM903.8 241H891v199h9.3c5 0 14.3-.5 20.6-1 9.5-.9 12.8-1.7 20.6-4.9 41.1-16.9 59.5-47.6 59.5-99.2 0-23.9-3.7-38.9-13.3-53.7-14.5-22.4-36.2-36-62.7-39.2-4.6-.5-14.2-1-21.2-1z"
|
d="M939.5 185.4c33.5 6.1 61.7 21.9 83.2 46.5 17.6 20.1 27.6 40.3 33.4 67 4.2 19.7 3.7 56.3-1.3 79.1-10.6 49.7-36.5 83.1-81.3 105-15.9 7.8-27.3 11.6-38.6 13-14.1 1.8-71.1 2.5-78.1 1.1-11-2.3-19.3-10.4-22.3-21.7-2.3-8.8-2.3-261 0-269.8 2.8-10.8 10-18.1 20.8-21.1 7.6-2.2 71.2-1.5 84.2.9M903.8 241H891v199h9.3c5 0 14.3-.5 20.6-1 9.5-.9 12.8-1.7 20.6-4.9 41.1-16.9 59.5-47.6 59.5-99.2 0-23.9-3.7-38.9-13.3-53.7-14.5-22.4-36.2-36-62.7-39.2-4.6-.5-14.2-1-21.2-1"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M561.8 245.6c-28.7 5.2-60.3 22.1-83.5 44.7l-6.2 6-9.8-9.4c-20.6-19.7-46.1-33.2-74.3-39.4-11.6-2.5-36.4-3.1-49-1.1-41.8 6.6-73 31-84.8 66.1-6 17.6-7.4 42.4-3.8 63 4.9 27.5 17.3 56.3 32.4 75.3C302 475 359.3 531.2 433 598c37.5 34 35.1 32.4 42 29.3 7.5-3.4 96.9-88.3 144.9-137.7 38.7-39.7 49.3-52.6 59-71.7 11.8-23.2 16.7-44.3 15.8-68.7-.7-22.4-6.3-40.4-17.7-57.1-16.1-23.8-44.3-41.1-75.7-46.6-10.5-1.8-29.2-1.8-39.5.1z"
|
d="M561.8 245.6c-28.7 5.2-60.3 22.1-83.5 44.7l-6.2 6-9.8-9.4c-20.6-19.7-46.1-33.2-74.3-39.4-11.6-2.5-36.4-3.1-49-1.1-41.8 6.6-73 31-84.8 66.1-6 17.6-7.4 42.4-3.8 63 4.9 27.5 17.3 56.3 32.4 75.3C302 475 359.3 531.2 433 598c37.5 34 35.1 32.4 42 29.3 7.5-3.4 96.9-88.3 144.9-137.7 38.7-39.7 49.3-52.6 59-71.7 11.8-23.2 16.7-44.3 15.8-68.7-.7-22.4-6.3-40.4-17.7-57.1-16.1-23.8-44.3-41.1-75.7-46.6-10.5-1.8-29.2-1.8-39.5.1"
|
||||||
style={{
|
style={{
|
||||||
fill: '#ff504f',
|
fill: '#ff504f',
|
||||||
}}
|
}}
|
||||||
|
|||||||
51
src/Icons/generated/Trinket.tsx
Normal file
51
src/Icons/generated/Trinket.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { SVGProps } from 'react';
|
||||||
|
interface SVGRProps {
|
||||||
|
title?: string;
|
||||||
|
titleId?: string;
|
||||||
|
size?: string;
|
||||||
|
}
|
||||||
|
const Trinket = ({
|
||||||
|
title,
|
||||||
|
titleId,
|
||||||
|
...props
|
||||||
|
}: SVGProps<SVGSVGElement> & SVGRProps) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width={props.size || 16}
|
||||||
|
height={props.size || 16}
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 364 472"
|
||||||
|
aria-labelledby={titleId}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{title ? <title id={titleId}>{title}</title> : null}
|
||||||
|
<path
|
||||||
|
fill="#78A083"
|
||||||
|
d="M40.536 302.159c-6.366-1.843-13.102 1.822-14.118 8.371-1.599 10.309-1.49 20.912.345 31.493 2.538 14.638 8.327 28.947 17.037 42.11s18.895 21.898 32.45 31.582 32.017 14.639 48.355 19.37 34.674 9.982 51.306 9.04c16.633-.942 29.437-2.571 43.833-9.043s26.935-15.473 36.901-26.49c7.205-7.964 12.964-16.868 17.121-26.436 2.641-6.078-1.095-12.775-7.461-14.619L153.42 334.848zM323.254 168.504c6.37 1.832 13.099-1.846 14.102-8.397 1.579-10.312 1.449-20.915-.405-31.493-2.566-14.633-8.382-28.93-17.117-42.077s-18.937-21.862-32.511-31.52-32.044-14.578-48.391-19.278-34.693-9.916-51.324-8.942-29.432 2.626-43.815 9.126-26.905 15.525-36.851 26.56c-7.19 7.978-12.93 16.893-17.07 26.468-2.629 6.084 1.12 12.774 7.489 14.605l112.946 32.474z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="url(#a)"
|
||||||
|
d="M75.577 127.413c2.38-5.473 3.57-8.209 5.537-9.963a12 12 0 0 1 6.168-2.906c2.604-.399 5.472.425 11.207 2.074l216.593 62.274c5.73 1.648 8.595 2.471 10.588 4.191a12 12 0 0 1 3.683 5.732c.736 2.528.295 5.476-.588 11.372l-10.644 71.096c-.14.937-.211 1.405-.317 1.865q-.141.614-.347 1.209c-.154.446-.343.881-.721 1.749l-28.861 66.269c-2.382 5.469-3.573 8.204-5.538 9.956a12 12 0 0 1-6.168 2.904c-2.603.398-5.469-.426-11.202-2.074L48.371 290.886c-5.732-1.648-8.597-2.472-10.59-4.191a12 12 0 0 1-3.684-5.735c-.735-2.527-.293-5.476.592-11.374l10.654-71.027c.14-.934.21-1.401.316-1.86q.142-.612.346-1.206c.153-.445.342-.878.718-1.744z"
|
||||||
|
/>
|
||||||
|
<defs>
|
||||||
|
<radialGradient
|
||||||
|
id="a"
|
||||||
|
cx={0}
|
||||||
|
cy={0}
|
||||||
|
r={1}
|
||||||
|
gradientTransform="matrix(-25.05324 87.13566 -130.70377 -37.57994 181.959 234.953)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stopColor="#FFF4BE" />
|
||||||
|
<stop offset={1} stopColor="#FFC374" />
|
||||||
|
</radialGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
Trinket.propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
};
|
||||||
|
export default Trinket;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user