Compare commits

...

9 Commits

Author SHA1 Message Date
Vikeo
890a095eb4 add name tag 2024-08-01 12:31:35 +02:00
Vikeo
a96b6bc340 fix roulette card size not being the same a actual play card size 2024-06-17 14:51:30 +02:00
Vikeo
a1f8745509 bump 2024-06-09 21:40:23 +02:00
Vikeo
1dc984e7b3 fix side extra counter text rotation 2024-06-09 21:28:13 +02:00
Vikeo
df3c30da31 fix 2024-05-27 11:01:43 +02:00
Vikeo
5fff18e079 check if ios 2024-05-27 11:01:25 +02:00
Vikeo
7203f0170a hotfix 2024-05-27 10:59:09 +02:00
Vikeo
63fbceafe2 fix null state 2024-05-27 10:58:42 +02:00
Vikeo
2ec9998a2a show and hide things based on ios, add button for installing PWA 2024-05-27 10:47:59 +02:00
26 changed files with 411 additions and 311 deletions

View File

@@ -1,12 +1,12 @@
index.html,1716462527631,3daeb4b4b2f195883f1e266f94c16156ee3c60a29c3eb8c44a8dcfdbb1fa0a03
manifest.webmanifest,1716462527631,10e89b44378da695cb672bf7d801a4ade909383751f1665416f561bbe1434e5d
manifest.json,1716462527513,91ce94afb71f33a477f5d8d48c3f98bd7de422279c74f17b6500eec72003ac1a
logo192.png,1716462527512,14ac21c3975e11951c1eb7793eec18e1cc3274bfe7cf7858636d547a9a4efc1c
registerSW.js,1716462527631,8db45a5ae8765ce12ec241d6c5bd5d30eb81dd9163b2685c5e1b867a0e487018
robots.txt,1716462527513,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2
sw.js,1716462528602,b681e1b343578a0de67032920144d05430677997580429b7e2b749f6afff69ed
workbox-3e911b1d.js,1716462528602,666146b896084273226c83dca0b93f99accb195688330d6aa5c8c570bd48a4ac
assets/index-D9CdzROR.css,1716462527631,610c77754d47a35446b00c0a50488070c943f3a05e6d57658faefd943bc3fc46
favicon.ico,1716462527511,c3d2b7ac7f6263cca7ee26f91725eb32e7539bf0564f3b31a1bfc23cc88e9739
logo512.png,1716462527512,a9ebde1252bb76a5b474130ef07a7ed744448fde84221f715f3fec849eccbcd2
assets/index-DgCoW5us.js,1716462527631,06a6d92ff20d7f9e1f5e0c4d3ad8f931d7d0636f109b5e2dbbe28abd3707bb50
index.html,1716800495173,a60a0d61bc25aa1eb4446d628875d96224860fbd767a11682a6c1db79b9403e2
manifest.webmanifest,1716800495173,10e89b44378da695cb672bf7d801a4ade909383751f1665416f561bbe1434e5d
manifest.json,1716800495051,91ce94afb71f33a477f5d8d48c3f98bd7de422279c74f17b6500eec72003ac1a
registerSW.js,1716800495173,8db45a5ae8765ce12ec241d6c5bd5d30eb81dd9163b2685c5e1b867a0e487018
robots.txt,1716800495051,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2
sw.js,1716800496019,609c6e27a6431ece2081013efab535a218046977d97b8600a6f58ba7589373c1
logo192.png,1716800495050,14ac21c3975e11951c1eb7793eec18e1cc3274bfe7cf7858636d547a9a4efc1c
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

View File

@@ -1,7 +1,7 @@
{
"name": "life-trinket",
"private": true,
"version": "0.9.95",
"version": "0.9.99",
"type": "commonjs",
"engines": {
"node": ">=20",

147
pnpm-lock.yaml generated
View File

@@ -41,7 +41,7 @@ importers:
version: 11.11.4(@types/react@18.3.1)(react@18.2.0)
'@emotion/styled':
specifier: ^11.11.5
version: 11.11.5(@emotion/react@11.11.4(@types/react@18.3.1)(react@18.2.0))(@types/react@18.3.1)(react@18.2.0)
version: 11.11.5(@emotion/react@11.11.4)(@types/react@18.3.1)(react@18.2.0)
'@savvywombat/tailwindcss-grid-areas':
specifier: ^4.0.0
version: 4.0.0(tailwindcss@3.4.3)
@@ -59,13 +59,13 @@ importers:
version: 7.5.8
'@typescript-eslint/eslint-plugin':
specifier: ^7.8.0
version: 7.8.0(@typescript-eslint/parser@7.8.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
version: 7.8.0(@typescript-eslint/parser@7.8.0)(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/parser':
specifier: ^7.8.0
version: 7.8.0(eslint@8.57.0)(typescript@5.4.5)
'@vitejs/plugin-react-swc':
specifier: ^3.6.0
version: 3.6.0(vite@5.2.10(@types/node@20.12.2)(terser@5.30.0))
version: 3.6.0(vite@5.2.10)
autoprefixer:
specifier: ^10.4.19
version: 10.4.19(postcss@8.4.38)
@@ -80,7 +80,7 @@ importers:
version: 0.4.6(eslint@8.57.0)
firebase-tools:
specifier: ^13.7.5
version: 13.7.5(encoding@0.1.13)
version: 13.7.5
install:
specifier: ^0.13.0
version: 0.13.0
@@ -101,10 +101,10 @@ importers:
version: 5.4.5
vite:
specifier: ^5.2.10
version: 5.2.10(@types/node@20.12.2)(terser@5.30.0)
version: 5.2.10
vite-plugin-pwa:
specifier: ^0.20.0
version: 0.20.0(vite@5.2.10(@types/node@20.12.2)(terser@5.30.0))(workbox-build@7.1.0)(workbox-window@7.1.0)
version: 0.20.0(vite@5.2.10)(workbox-build@7.1.0)(workbox-window@7.1.0)
packages:
@@ -5740,10 +5740,9 @@ snapshots:
'@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
'@emotion/utils': 1.2.1
'@emotion/weak-memoize': 0.3.1
'@types/react': 18.3.1
hoist-non-react-statics: 3.3.2
react: 18.2.0
optionalDependencies:
'@types/react': 18.3.1
'@emotion/serialize@1.1.4':
dependencies:
@@ -5755,7 +5754,7 @@ snapshots:
'@emotion/sheet@1.2.2': {}
'@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.1)(react@18.2.0))(@types/react@18.3.1)(react@18.2.0)':
'@emotion/styled@11.11.5(@emotion/react@11.11.4)(@types/react@18.3.1)(react@18.2.0)':
dependencies:
'@babel/runtime': 7.24.1
'@emotion/babel-plugin': 11.11.0
@@ -5764,9 +5763,8 @@ snapshots:
'@emotion/serialize': 1.1.4
'@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
'@emotion/utils': 1.2.1
react: 18.2.0
optionalDependencies:
'@types/react': 18.3.1
react: 18.2.0
'@emotion/unitless@0.8.1': {}
@@ -6186,7 +6184,7 @@ snapshots:
'@google-cloud/promisify@2.0.4': {}
'@google-cloud/pubsub@3.7.5(encoding@0.1.13)':
'@google-cloud/pubsub@3.7.5':
dependencies:
'@google-cloud/paginator': 4.0.1
'@google-cloud/precise-date': 3.0.1
@@ -6198,8 +6196,8 @@ snapshots:
'@types/long': 4.0.2
arrify: 2.0.1
extend: 3.0.2
google-auth-library: 8.9.0(encoding@0.1.13)
google-gax: 3.6.1(encoding@0.1.13)
google-auth-library: 8.9.0
google-gax: 3.6.1
heap-js: 2.5.0
is-stream-ended: 0.1.4
lodash.snakecase: 4.1.1
@@ -6347,17 +6345,15 @@ snapshots:
'@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.1)(react@18.2.0)':
dependencies:
'@babel/runtime': 7.24.1
react: 18.2.0
optionalDependencies:
'@types/react': 18.3.1
react: 18.2.0
'@radix-ui/react-slot@1.0.2(@types/react@18.3.1)(react@18.2.0)':
dependencies:
'@babel/runtime': 7.24.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.2.0)
react: 18.2.0
optionalDependencies:
'@types/react': 18.3.1
react: 18.2.0
'@rollup/plugin-babel@5.3.1(@babel/core@7.24.5)(rollup@2.79.1)':
dependencies:
@@ -6374,7 +6370,6 @@ snapshots:
is-builtin-module: 3.2.1
is-module: 1.0.0
resolve: 1.22.8
optionalDependencies:
rollup: 2.79.1
'@rollup/plugin-replace@2.4.2(rollup@2.79.1)':
@@ -6385,11 +6380,10 @@ snapshots:
'@rollup/plugin-terser@0.4.4(rollup@2.79.1)':
dependencies:
rollup: 2.79.1
serialize-javascript: 6.0.2
smob: 1.5.0
terser: 5.30.0
optionalDependencies:
rollup: 2.79.1
'@rollup/pluginutils@3.1.0(rollup@2.79.1)':
dependencies:
@@ -6403,7 +6397,6 @@ snapshots:
'@types/estree': 1.0.5
estree-walker: 2.0.2
picomatch: 2.3.1
optionalDependencies:
rollup: 2.79.1
'@rollup/rollup-android-arm-eabi@4.13.2':
@@ -6509,9 +6502,9 @@ snapshots:
'@svgr/cli@8.1.0(typescript@5.4.5)':
dependencies:
'@svgr/core': 8.1.0(typescript@5.4.5)
'@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.4.5))
'@svgr/plugin-prettier': 8.1.0(@svgr/core@8.1.0(typescript@5.4.5))
'@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.4.5))(typescript@5.4.5)
'@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0)
'@svgr/plugin-prettier': 8.1.0(@svgr/core@8.1.0)
'@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0)(typescript@5.4.5)
camelcase: 6.3.0
chalk: 4.1.2
commander: 9.5.0
@@ -6538,7 +6531,7 @@ snapshots:
'@babel/types': 7.24.0
entities: 4.5.0
'@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.4.5))':
'@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0)':
dependencies:
'@babel/core': 7.24.3
'@svgr/babel-preset': 8.1.0(@babel/core@7.24.3)
@@ -6548,13 +6541,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@svgr/plugin-prettier@8.1.0(@svgr/core@8.1.0(typescript@5.4.5))':
'@svgr/plugin-prettier@8.1.0(@svgr/core@8.1.0)':
dependencies:
'@svgr/core': 8.1.0(typescript@5.4.5)
deepmerge: 4.3.1
prettier: 2.8.8
'@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.4.5))(typescript@5.4.5)':
'@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0)(typescript@5.4.5)':
dependencies:
'@svgr/core': 8.1.0(typescript@5.4.5)
cosmiconfig: 8.3.6(typescript@5.4.5)
@@ -6677,7 +6670,7 @@ snapshots:
'@types/trusted-types@2.0.7': {}
'@typescript-eslint/eslint-plugin@7.8.0(@typescript-eslint/parser@7.8.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)':
'@typescript-eslint/eslint-plugin@7.8.0(@typescript-eslint/parser@7.8.0)(eslint@8.57.0)(typescript@5.4.5)':
dependencies:
'@eslint-community/regexpp': 4.10.0
'@typescript-eslint/parser': 7.8.0(eslint@8.57.0)(typescript@5.4.5)
@@ -6692,7 +6685,6 @@ snapshots:
natural-compare: 1.4.0
semver: 7.6.2
ts-api-utils: 1.3.0(typescript@5.4.5)
optionalDependencies:
typescript: 5.4.5
transitivePeerDependencies:
- supports-color
@@ -6705,7 +6697,6 @@ snapshots:
'@typescript-eslint/visitor-keys': 7.8.0
debug: 4.3.4
eslint: 8.57.0
optionalDependencies:
typescript: 5.4.5
transitivePeerDependencies:
- supports-color
@@ -6722,7 +6713,6 @@ snapshots:
debug: 4.3.4
eslint: 8.57.0
ts-api-utils: 1.3.0(typescript@5.4.5)
optionalDependencies:
typescript: 5.4.5
transitivePeerDependencies:
- supports-color
@@ -6739,7 +6729,6 @@ snapshots:
minimatch: 9.0.4
semver: 7.6.2
ts-api-utils: 1.3.0(typescript@5.4.5)
optionalDependencies:
typescript: 5.4.5
transitivePeerDependencies:
- supports-color
@@ -6765,10 +6754,10 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
'@vitejs/plugin-react-swc@3.6.0(vite@5.2.10(@types/node@20.12.2)(terser@5.30.0))':
'@vitejs/plugin-react-swc@3.6.0(vite@5.2.10)':
dependencies:
'@swc/core': 1.4.11
vite: 5.2.10(@types/node@20.12.2)(terser@5.30.0)
vite: 5.2.10
transitivePeerDependencies:
- '@swc/helpers'
@@ -6809,7 +6798,7 @@ snapshots:
optional: true
ajv-formats@2.1.1(ajv@8.12.0):
optionalDependencies:
dependencies:
ajv: 8.12.0
ajv@6.12.6:
@@ -7321,7 +7310,6 @@ snapshots:
js-yaml: 4.1.0
parse-json: 5.2.0
path-type: 4.0.0
optionalDependencies:
typescript: 5.4.5
crc-32@1.2.2: {}
@@ -7921,9 +7909,9 @@ snapshots:
locate-path: 6.0.0
path-exists: 4.0.0
firebase-tools@13.7.5(encoding@0.1.13):
firebase-tools@13.7.5:
dependencies:
'@google-cloud/pubsub': 3.7.5(encoding@0.1.13)
'@google-cloud/pubsub': 3.7.5
abort-controller: 3.0.0
ajv: 6.12.6
archiver: 5.3.2
@@ -7948,7 +7936,7 @@ snapshots:
fs-extra: 10.1.0
fuzzy: 0.1.3
glob: 7.2.3
google-auth-library: 7.14.1(encoding@0.1.13)
google-auth-library: 7.14.1
inquirer: 8.2.6
inquirer-autocomplete-prompt: 2.0.1(inquirer@8.2.6)
jsonwebtoken: 9.0.2
@@ -7960,7 +7948,7 @@ snapshots:
mime: 2.6.0
minimatch: 3.1.2
morgan: 1.10.0
node-fetch: 2.7.0(encoding@0.1.13)
node-fetch: 2.7.0
open: 6.4.0
ora: 5.4.1
p-limit: 3.1.0
@@ -7973,13 +7961,13 @@ snapshots:
stream-chain: 2.2.5
stream-json: 1.8.0
strip-ansi: 6.0.1
superstatic: 9.0.3(encoding@0.1.13)
superstatic: 9.0.3
tar: 6.2.1
tcp-port-used: 1.0.2
tmp: 0.2.3
triple-beam: 1.4.1
universal-analytics: 0.5.3
update-notifier-cjs: 5.1.6(encoding@0.1.13)
update-notifier-cjs: 5.1.6
uuid: 8.3.2
winston: 3.13.0
winston-transport: 4.7.0
@@ -8103,38 +8091,38 @@ snapshots:
ga-4-react@0.1.281: {}
gaxios@4.3.3(encoding@0.1.13):
gaxios@4.3.3:
dependencies:
abort-controller: 3.0.0
extend: 3.0.2
https-proxy-agent: 5.0.1
is-stream: 2.0.1
node-fetch: 2.7.0(encoding@0.1.13)
node-fetch: 2.7.0
transitivePeerDependencies:
- encoding
- supports-color
gaxios@5.1.3(encoding@0.1.13):
gaxios@5.1.3:
dependencies:
extend: 3.0.2
https-proxy-agent: 5.0.1
is-stream: 2.0.1
node-fetch: 2.7.0(encoding@0.1.13)
node-fetch: 2.7.0
transitivePeerDependencies:
- encoding
- supports-color
gcp-metadata@4.3.1(encoding@0.1.13):
gcp-metadata@4.3.1:
dependencies:
gaxios: 4.3.3(encoding@0.1.13)
gaxios: 4.3.3
json-bigint: 1.0.0
transitivePeerDependencies:
- encoding
- supports-color
gcp-metadata@5.3.0(encoding@0.1.13):
gcp-metadata@5.3.0:
dependencies:
gaxios: 5.1.3(encoding@0.1.13)
gaxios: 5.1.3
json-bigint: 1.0.0
transitivePeerDependencies:
- encoding
@@ -8233,37 +8221,37 @@ snapshots:
merge2: 1.4.1
slash: 3.0.0
google-auth-library@7.14.1(encoding@0.1.13):
google-auth-library@7.14.1:
dependencies:
arrify: 2.0.1
base64-js: 1.5.1
ecdsa-sig-formatter: 1.0.11
fast-text-encoding: 1.0.6
gaxios: 4.3.3(encoding@0.1.13)
gcp-metadata: 4.3.1(encoding@0.1.13)
gtoken: 5.3.2(encoding@0.1.13)
gaxios: 4.3.3
gcp-metadata: 4.3.1
gtoken: 5.3.2
jws: 4.0.0
lru-cache: 6.0.0
transitivePeerDependencies:
- encoding
- supports-color
google-auth-library@8.9.0(encoding@0.1.13):
google-auth-library@8.9.0:
dependencies:
arrify: 2.0.1
base64-js: 1.5.1
ecdsa-sig-formatter: 1.0.11
fast-text-encoding: 1.0.6
gaxios: 5.1.3(encoding@0.1.13)
gcp-metadata: 5.3.0(encoding@0.1.13)
gtoken: 6.1.2(encoding@0.1.13)
gaxios: 5.1.3
gcp-metadata: 5.3.0
gtoken: 6.1.2
jws: 4.0.0
lru-cache: 6.0.0
transitivePeerDependencies:
- encoding
- supports-color
google-gax@3.6.1(encoding@0.1.13):
google-gax@3.6.1:
dependencies:
'@grpc/grpc-js': 1.8.21
'@grpc/proto-loader': 0.7.12
@@ -8272,9 +8260,9 @@ snapshots:
abort-controller: 3.0.0
duplexify: 4.1.3
fast-text-encoding: 1.0.6
google-auth-library: 8.9.0(encoding@0.1.13)
google-auth-library: 8.9.0
is-stream-ended: 0.1.4
node-fetch: 2.7.0(encoding@0.1.13)
node-fetch: 2.7.0
object-hash: 3.0.0
proto3-json-serializer: 1.1.1
protobufjs: 7.2.4
@@ -8302,18 +8290,18 @@ snapshots:
graphemer@1.4.0: {}
gtoken@5.3.2(encoding@0.1.13):
gtoken@5.3.2:
dependencies:
gaxios: 4.3.3(encoding@0.1.13)
gaxios: 4.3.3
google-p12-pem: 3.1.4
jws: 4.0.0
transitivePeerDependencies:
- encoding
- supports-color
gtoken@6.1.2(encoding@0.1.13):
gtoken@6.1.2:
dependencies:
gaxios: 5.1.3(encoding@0.1.13)
gaxios: 5.1.3
google-p12-pem: 4.0.1
jws: 4.0.0
transitivePeerDependencies:
@@ -8607,9 +8595,9 @@ snapshots:
isexe@3.1.1:
optional: true
isomorphic-fetch@3.0.0(encoding@0.1.13):
isomorphic-fetch@3.0.0:
dependencies:
node-fetch: 2.7.0(encoding@0.1.13)
node-fetch: 2.7.0
whatwg-fetch: 3.6.20
transitivePeerDependencies:
- encoding
@@ -9055,11 +9043,9 @@ snapshots:
dependencies:
lodash: 4.17.21
node-fetch@2.7.0(encoding@0.1.13):
node-fetch@2.7.0:
dependencies:
whatwg-url: 5.0.0
optionalDependencies:
encoding: 0.1.13
node-forge@1.3.1: {}
@@ -9273,9 +9259,8 @@ snapshots:
postcss-load-config@4.0.2(postcss@8.4.38):
dependencies:
lilconfig: 3.1.1
yaml: 2.4.1
optionalDependencies:
postcss: 8.4.38
yaml: 2.4.1
postcss-nested@6.0.1(postcss@8.4.38):
dependencies:
@@ -9897,7 +9882,7 @@ snapshots:
pirates: 4.0.6
ts-interface-checker: 0.1.13
superstatic@9.0.3(encoding@0.1.13):
superstatic@9.0.3:
dependencies:
basic-auth-connect: 1.0.0
commander: 10.0.1
@@ -9916,7 +9901,7 @@ snapshots:
on-headers: 1.0.2
path-to-regexp: 1.8.0
router: 1.3.8
update-notifier-cjs: 5.1.6(encoding@0.1.13)
update-notifier-cjs: 5.1.6
optionalDependencies:
re2: 1.20.10
transitivePeerDependencies:
@@ -10185,7 +10170,7 @@ snapshots:
escalade: 3.1.2
picocolors: 1.0.0
update-notifier-cjs@5.1.6(encoding@0.1.13):
update-notifier-cjs@5.1.6:
dependencies:
boxen: 5.1.2
chalk: 4.1.2
@@ -10196,7 +10181,7 @@ snapshots:
is-installed-globally: 0.4.0
is-npm: 5.0.0
is-yarn-global: 0.3.0
isomorphic-fetch: 3.0.0(encoding@0.1.13)
isomorphic-fetch: 3.0.0
pupa: 2.1.1
registry-auth-token: 5.0.2
registry-url: 5.1.0
@@ -10222,26 +10207,24 @@ snapshots:
vary@1.1.2: {}
vite-plugin-pwa@0.20.0(vite@5.2.10(@types/node@20.12.2)(terser@5.30.0))(workbox-build@7.1.0)(workbox-window@7.1.0):
vite-plugin-pwa@0.20.0(vite@5.2.10)(workbox-build@7.1.0)(workbox-window@7.1.0):
dependencies:
debug: 4.3.4
fast-glob: 3.3.2
pretty-bytes: 6.1.1
vite: 5.2.10(@types/node@20.12.2)(terser@5.30.0)
vite: 5.2.10
workbox-build: 7.1.0
workbox-window: 7.1.0
transitivePeerDependencies:
- supports-color
vite@5.2.10(@types/node@20.12.2)(terser@5.30.0):
vite@5.2.10:
dependencies:
esbuild: 0.20.2
postcss: 8.4.38
rollup: 4.13.2
optionalDependencies:
'@types/node': 20.12.2
fsevents: 2.3.3
terser: 5.30.0
wcwidth@1.0.1:
dependencies:

View File

@@ -9,10 +9,6 @@ export type RotationDivProps = TwcComponentProps<'div'> & {
$rotation?: number;
};
export type RotationSpanProps = TwcComponentProps<'span'> & {
$rotation?: number;
};
export type RotationButtonProps = TwcComponentProps<'button'> & {
$rotation?: number;
};

View File

@@ -3,7 +3,7 @@ import { twc } from 'react-twc';
import { decrementTimeoutMs } from '../../Data/constants';
import { CounterType, Rotation } from '../../Types/Player';
import { OutlinedText } from '../Misc/OutlinedText';
import { MAX_TAP_MOVE_DISTANCE, RotationDivProps } from './CommanderDamage';
import { MAX_TAP_MOVE_DISTANCE } from './CommanderDamage';
const ExtraCounterContainer = twc.div`
flex
@@ -28,17 +28,14 @@ const ExtraCounterButton = twc.button`
webkit-user-select-none
`;
const IconContainer = twc.div<RotationDivProps>((props) => [
'w-auto opacity-50',
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
? 'rotate-[-90deg]'
: '',
]);
const IconContainer = twc.div`
w-auto opacity-50 data-[rotation-is-side=true]:-rotate-90`;
const TextContainer = twc.div`
absolute
top-1/2
left-1/2
data-[rotation-is-side=true]:-rotate-90
`;
type ExtraCounterProps = {
@@ -115,6 +112,9 @@ const ExtraCounter = ({
const fontWeight = 'bold';
const strokeWidth = isSide ? '0.4vmax' : '0.7vmin';
const rotationIsSide =
rotation === Rotation.SideFlipped || rotation === Rotation.Side;
return (
<ExtraCounterContainer>
<ExtraCounterButton
@@ -126,8 +126,10 @@ const ExtraCounter = ({
}}
aria-label={`Player ${playerIndex} extra counter: ${type}`}
>
<IconContainer $rotation={rotation}>{Icon}</IconContainer>
<TextContainer>
<IconContainer data-rotation-is-side={rotationIsSide}>
{Icon}
</IconContainer>
<TextContainer data-rotation-is-side={rotationIsSide}>
<OutlinedText
fontSize={fontSize}
fontWeight={fontWeight}

View File

@@ -1,9 +1,8 @@
import { useEffect, useRef, useState } from 'react';
import { useRef, useState } from 'react';
import { TwcComponentProps, twc } from 'react-twc';
import { lifeLongPressMultiplier } from '../../Data/constants';
import { Player, Rotation } from '../../Types/Player';
import { MAX_TAP_MOVE_DISTANCE } from './CommanderDamage';
import { checkContrast } from '../../Utils/checkContrast';
type RotationButtonProps = TwcComponentProps<'div'> & {
$align?: string;
@@ -58,18 +57,6 @@ const LifeCounterButton = ({
const [hasPressedDown, setHasPressedDown] = useState(false);
const downPositionRef = useRef({ x: 0, y: 0 });
const [iconColor, setIconColor] = useState<'dark' | 'light'>('dark');
useEffect(() => {
const contrast = checkContrast(player.color, '#00000080');
if (contrast === 'Fail') {
setIconColor('light');
} else {
setIconColor('dark');
}
}, [player.color]);
const handleLifeChange = (increment: number) => {
setLifeTotal(player.lifeTotal + increment);
};
@@ -132,7 +119,7 @@ const LifeCounterButton = ({
<TextContainer
$rotation={player.settings.rotation}
$align={operation === 'add' ? 'right' : 'left'}
data-contrast={iconColor}
data-contrast={player.iconTheme}
className="data-[contrast=dark]:text-icons-dark
data-[contrast=light]:text-icons-light"
>

View File

@@ -10,8 +10,6 @@ import {
import { CounterType, Player, Rotation } from '../../Types/Player';
import { RotationDivProps } from '../Buttons/CommanderDamage';
import ExtraCounter from '../Buttons/ExtraCounter';
import { useEffect, useState } from 'react';
import { checkContrast } from '../../Utils/checkContrast';
const Container = twc.div<RotationDivProps>((props) => [
'flex',
@@ -33,17 +31,6 @@ type ExtraCountersBarProps = {
const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
const { updatePlayer } = usePlayers();
const [iconColor, setIconColor] = useState<'dark' | 'light'>('dark');
useEffect(() => {
const contrast = checkContrast(player.color, '#00000080');
if (contrast === 'Fail') {
setIconColor('light');
} else {
setIconColor('dark');
}
}, [player.color]);
const handleCounterChange = (
updatedCounterTotal: number,
@@ -109,7 +96,7 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
Icon={
<CommanderTax
size={iconSize}
data-contrast={iconColor}
data-contrast={player.iconTheme}
strokeWidth={0}
stroke="transparent"
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
@@ -132,7 +119,7 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
Icon={
<PartnerTax
size={iconSize}
data-contrast={iconColor}
data-contrast={player.iconTheme}
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
/>
}
@@ -153,7 +140,7 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
Icon={
<Poison
size={iconSize}
data-contrast={iconColor}
data-contrast={player.iconTheme}
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
/>
}
@@ -173,7 +160,7 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
Icon={
<Energy
size={iconSize}
data-contrast={iconColor}
data-contrast={player.iconTheme}
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
/>
}
@@ -193,7 +180,7 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
Icon={
<Experience
size={iconSize}
data-contrast={iconColor}
data-contrast={player.iconTheme}
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
/>
}

View File

@@ -2,6 +2,7 @@ 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';
@@ -201,25 +202,25 @@ export const SettingsDialog = ({
is enabled.
</Description>
</SettingContainer>
<SettingContainer>
<ToggleContainer>
<label>
Fullscreen on start <span className="text-xs">(Android only)</span>
</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>
{(!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>
@@ -255,21 +256,45 @@ export const SettingsDialog = ({
</div>
{!isPWA && (
<>
<Separator height="1px" />
<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 className="mt-1">
If you do, this 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>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" />

View File

@@ -1,10 +1,7 @@
import { useEffect, useRef, useState } from 'react';
import { twc } from 'react-twc';
import { Player, Rotation } from '../../Types/Player';
import {
RotationDivProps,
RotationSpanProps,
} from '../Buttons/CommanderDamage';
import { RotationDivProps } from '../Buttons/CommanderDamage';
import LifeCounterButton from '../Buttons/LifeCounterButton';
import { OutlinedText } from '../Misc/OutlinedText';
@@ -32,12 +29,12 @@ const TextWrapper = twc.div`
z-[-1]
`;
const RecentDifference = twc.div<RotationSpanProps>((props) => [
'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',
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
? 'top-1/3 translate-x-1/4 translate-y-1/2 rotate-[270deg]'
: 'top-1/4 left-[50%] -translate-x-1/2',
]);
const RecentDifference = twc.div`
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
top-1/4 left-[50%] -translate-x-1/2
data-[isSide=true]:top-1/3 data-[isSide=true]:translate-x-1/4 data-[isSide=true]:translate-y-1/2 data-[isSide=true]:rotate-[270deg] data-[isSide=true]:left-auto
`;
type HealthProps = {
player: Player;
@@ -101,6 +98,10 @@ const Health = ({
return minRatio * scaleFactor * 1;
};
const isSide =
player.settings.rotation === Rotation.SideFlipped ||
player.settings.rotation === Rotation.Side;
return (
<LifeContainer $rotation={player.settings.rotation}>
<LifeCounterButton
@@ -109,6 +110,31 @@ const Health = ({
operation="subtract"
increment={-1}
/>
{player.name && isSide ? (
<div className="size-full relative flex items-center justify-start">
<div className="fixed flex justify-center -rotate-90 left-[5.4vmax] ">
<div
data-contrast={player.iconTheme}
className="absolute text-[4vmin] opacity-50 font-bold
data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
>
{player.name}
</div>
</div>
</div>
) : (
<div className="w-full h-full relative flex items-start justify-center">
<div
data-contrast={player.iconTheme}
className="absolute text-[4vmin] -top-[1.1vmin] opacity-50 font-bold
data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
>
{player.name}
</div>
</div>
)}
<TextWrapper>
<LifeCounterTextContainer
$rotation={player.settings.rotation}
@@ -122,10 +148,7 @@ const Health = ({
{player.lifeTotal}
</OutlinedText>
{recentDifference !== 0 && (
<RecentDifference
key={differenceKey}
$rotation={player.settings.rotation}
>
<RecentDifference data-isSide={isSide} key={differenceKey}>
{recentDifference > 0 ? '+' : ''}
{recentDifference}
</RecentDifference>

View File

@@ -6,7 +6,6 @@ import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
import { usePlayers } from '../../Hooks/usePlayers';
import { Cog } from '../../Icons/generated';
import { Player, Rotation } from '../../Types/Player';
import { checkContrast } from '../../Utils/checkContrast';
import {
RotationButtonProps,
RotationDivProps,
@@ -28,22 +27,14 @@ const SettingsButtonTwc = twc.button<RotationButtonProps>((props) => [
type SettingsButtonProps = {
onClick: () => void;
rotation: Rotation;
color: string;
iconTheme: 'light' | 'dark';
};
const SettingsButton = ({ onClick, rotation, color }: SettingsButtonProps) => {
const [iconColor, setIconColor] = useState<'dark' | 'light'>('dark');
useEffect(() => {
const contrast = checkContrast(color, '#00000080');
if (contrast === 'Fail') {
setIconColor('light');
} else {
setIconColor('dark');
}
}, [color]);
const SettingsButton = ({
onClick,
rotation,
iconTheme,
}: SettingsButtonProps) => {
return (
<SettingsButtonTwc
onClick={onClick}
@@ -52,7 +43,7 @@ const SettingsButton = ({ onClick, rotation, color }: SettingsButtonProps) => {
>
<Cog
size="5vmin"
data-contrast={iconColor}
data-contrast={iconTheme}
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
/>
</SettingsButtonTwc>
@@ -215,11 +206,9 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
!playing &&
settings.showStartingPlayer &&
player.isStartingPlayer && <StartingPlayerCard player={player} />}
{player.hasLost && (
<PlayerLostWrapper $rotation={player.settings.rotation} />
)}
<CommanderDamageBar
opponents={opponents}
player={player}
@@ -233,7 +222,7 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
setShowPlayerMenu(!showPlayerMenu);
}}
rotation={player.settings.rotation}
color={player.color}
iconTheme={player.iconTheme}
/>
)}
{playerCanLose(player) && (
@@ -250,7 +239,6 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
handleLifeChange={handleLifeChange}
/>
<ExtraCountersBar player={player} />
<PlayerMenu
isShown={showPlayerMenu}
player={player}

View File

@@ -0,0 +1,45 @@
import { useEffect, useRef, useState } from 'react';
import { BeforeInstallPromptEvent } from '../../global';
import { useAnalytics } from '../../Hooks/useAnalytics';
export const InstallPWAButton = () => {
const supportsPWARef = useRef<boolean>(false);
const [promptInstall, setPromptInstall] =
useState<BeforeInstallPromptEvent | null>(null);
const analytics = useAnalytics();
const handler = (e: BeforeInstallPromptEvent) => {
e.preventDefault();
supportsPWARef.current = true;
setPromptInstall(e);
};
useEffect(() => {
window.addEventListener('beforeinstallprompt', handler);
return () => window.removeEventListener('transitionend', handler);
}, []);
if (!supportsPWARef.current) {
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>
);
};

View File

@@ -11,6 +11,7 @@ import {
Experience,
FullscreenOff,
FullscreenOn,
NameTag,
PartnerTax,
Poison,
ResetGame,
@@ -19,6 +20,7 @@ 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
@@ -120,8 +122,16 @@ const PlayerMenu = ({
const { updatePlayer, resetCurrentGame, players } = usePlayers();
const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const updatedPlayer = { ...player, color: event.target.value };
updatePlayer(updatedPlayer);
const iconTheme =
checkContrast(event.target.value, '#00000080') === 'Fail'
? 'light'
: 'dark';
updatePlayer({
...player,
color: event.target.value,
iconTheme,
});
};
const handleSettingsChange = (event: React.ChangeEvent<HTMLInputElement>) => {
@@ -152,6 +162,18 @@ const PlayerMenu = ({
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();
@@ -347,30 +369,35 @@ const PlayerMenu = ({
>
<Exit size={iconSize} style={{ rotate: '180deg' }} />
</button>
<div
data-fullscreen={document.fullscreenElement ? true : false}
className="flex
{(!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>
>
<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}
@@ -391,6 +418,21 @@ const PlayerMenu = ({
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',

View File

@@ -1,7 +1,10 @@
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) => {
@@ -28,6 +31,8 @@ const PlayersWrapper = twc.div`w-full h-full bg-black`;
export const Players = ({ gridLayout }: { gridLayout: GridLayout }) => {
const { players } = usePlayers();
const { playing, settings, preStartCompleted } = useGlobalSettings();
return (
<PlayersWrapper>
<div className={`grid w-full h-full gap-1 box-border ${gridLayout} `}>
@@ -36,7 +41,7 @@ export const Players = ({ gridLayout }: { gridLayout: GridLayout }) => {
return (
<div
key={player.index}
className={`flex justify-center items-center align-middle ${gridArea}`}
className={`relative flex justify-center items-center align-middle ${gridArea}`}
>
<LifeCounter
player={player}
@@ -44,6 +49,15 @@ export const Players = ({ gridLayout }: { gridLayout: GridLayout }) => {
(opponent) => opponent.index !== player.index
)}
/>
{settings.preStartMode === PreStartMode.RandomKing &&
!preStartCompleted &&
!playing &&
settings.showStartingPlayer && (
<div className="absolute size-full z-20">
<RoulettePlayerCard player={player} />
</div>
)}
</div>
);
})}

View File

@@ -1,49 +0,0 @@
import { usePlayers } from '../../../../Hooks/usePlayers';
import { Player } from '../../../../Types/Player';
import { GridLayout } from '../../../Views/Play';
import { RoulettePlayerCard } from './RoulettePlayerCard';
const getGridArea = (player: Player) => {
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 RandomKingPlayers = ({
gridLayout,
}: {
gridLayout: GridLayout;
}) => {
const { players } = usePlayers();
return (
<div className="w-full h-full bg-black">
<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={`flex justify-center items-center align-middle ${gridArea}`}
>
<RoulettePlayerCard player={player} />
</div>
);
})}
</div>
</div>
);
};

View File

@@ -2,7 +2,7 @@ import { useEffect, useRef } from 'react';
import { useGlobalSettings } from '../../../../Hooks/useGlobalSettings';
import { usePlayers } from '../../../../Hooks/usePlayers';
export const RandomKingSelectWrapper = () => {
export const RandomKingRandomizer = () => {
const { setRandomizingPlayer } = useGlobalSettings();
const randomIntervalRef = useRef<NodeJS.Timeout | null>(null);
@@ -87,7 +87,7 @@ export const RandomKingSelectWrapper = () => {
backgroundImage: `linear-gradient(60deg, ${gradientColors})`,
}}
/>
<p className="relative z-10 text-[5vmax]">PRESS TO SELECT PLAYER</p>
<p className="relative z-10 text-[5vmax]">PRESS TO START</p>
</div>
</div>
);

View File

@@ -36,7 +36,7 @@ export const RoulettePlayerCard = ({ player }: { player: Player }) => {
: player.settings.rotation;
return (
<div className="relative flex flex-grow flex-col items-center w-full h-full overflow-hidden">
<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 }}

View File

@@ -1,12 +1,11 @@
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
import { PreStartMode } from '../../Types/Settings';
import { GridLayout } from '../Views/Play';
import { FingerGame } from './Games/FingerGame';
import { RandomKingPlayers } from './Games/RandomKing/RandomKingPlayers';
import { RandomKingSelectWrapper } from './Games/RandomKing/RandomKingSelectWrapper';
import { RandomKingRandomizer } from './Games/RandomKing/RandomKingSelectWrapper';
import { Trivia } from './Games/Trivia';
export const PreStart = ({ gridLayout }: { gridLayout: GridLayout }) => {
export const PreStart = () => {
const { settings, randomizingPlayer, goToStart } = useGlobalSettings();
if (settings.preStartMode === PreStartMode.RandomKing) {
@@ -14,12 +13,7 @@ export const PreStart = ({ gridLayout }: { gridLayout: GridLayout }) => {
return null;
}
return (
<>
<RandomKingSelectWrapper />
<RandomKingPlayers gridLayout={gridLayout} />
</>
);
return <RandomKingRandomizer />;
}
if (settings.preStartMode === PreStartMode.FingerGame) {

View File

@@ -94,22 +94,14 @@ export const Play = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (
players.length > 1 &&
!preStartCompleted &&
settings.preStartMode !== PreStartMode.None &&
!playing &&
settings.showStartingPlayer
) {
return (
<MainWrapper>
<PreStart gridLayout={gridLayout} />
</MainWrapper>
);
}
return (
<MainWrapper>
{players.length > 1 &&
!preStartCompleted &&
settings.preStartMode !== PreStartMode.None &&
!playing &&
settings.showStartingPlayer && <PreStart />}
<Players gridLayout={gridLayout} />
</MainWrapper>
);

View File

@@ -387,7 +387,7 @@ const Start = () => {
}}
/>
</div>
{!isPWA && (
{!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{' '}

View File

@@ -1,5 +1,6 @@
import { Player, Rotation } from '../Types/Player';
import { InitialGameSettings, Orientation } from '../Types/Settings';
import { checkContrast } from '../Utils/checkContrast';
export const presetColors = [
'#D08182', // Muted Pink
@@ -214,6 +215,8 @@ export const createInitialPlayers = ({
lifeTotal: startingLifeTotal,
index: i,
color,
iconTheme:
checkContrast(color, '#00000080') === 'Fail' ? 'light' : 'dark',
settings: {
useCommanderDamage,
usePartner: false,
@@ -227,6 +230,7 @@ export const createInitialPlayers = ({
hasLost: false,
isStartingPlayer: false,
isSide: rotation === Rotation.Side || rotation === Rotation.SideFlipped,
name: '',
};
players.push(player);

View 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;

View File

@@ -10,6 +10,7 @@ export { default as FullscreenOn } from './FullscreenOn';
export { default as Info } from './Info';
export { default as LittleGuy } from './LittleGuy';
export { default as Logo } from './Logo';
export { default as NameTag } from './NameTag';
export { default as PartnerTax } from './PartnerTax';
export { default as Poison } from './Poison';
export { default as ResetGame } from './ResetGame';

View File

@@ -0,0 +1,3 @@
<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.2284 2H48C49.1046 2 50 2.89543 50 4V18.7716C50 19.302 49.7893 19.8107 49.4142 20.1858L21.7213 47.8787C20.5497 49.0503 18.6502 49.0502 17.4787 47.8787L4.12132 34.5213C2.94974 33.3497 2.94974 31.4503 4.12132 30.2787L31.8142 2.58579C32.1893 2.21071 32.698 2 33.2284 2ZM47.3333 8C47.3333 9.84095 45.8409 11.3333 44 11.3333C42.159 11.3333 40.6667 9.84095 40.6667 8C40.6667 6.15905 42.159 4.66667 44 4.66667C45.8409 4.66667 47.3333 6.15905 47.3333 8ZM30.7334 14.6437L36.9045 20.8148L40.6771 17.0422L40.0142 16.3793L36.9889 19.4046L34.8916 17.3074L37.6759 14.5232L37.013 13.8602L34.2287 16.6445L32.1436 14.5593L35.1207 11.5822L34.4577 10.9193L30.7334 14.6437ZM23.1845 22.1926L24.0764 21.3007L31.2961 24.326L31.3684 24.2537L28.3431 17.0339L29.235 16.142L35.4062 22.3131L34.7071 23.0122L30.0185 18.3236L29.9582 18.3839L32.7183 25.0009L32.0434 25.6759L25.4263 22.9158L25.366 22.976L30.0546 27.6646L29.3556 28.3637L23.1845 22.1926ZM23.8745 33.8448L23.0911 34.6282L19.1859 26.1912L19.9573 25.4198L28.3944 29.3249L27.6109 30.1084L25.2428 28.9811L22.7472 31.4767L23.8745 33.8448ZM24.3446 28.5535L22.3196 30.5785L20.5238 26.8059L20.572 26.7577L24.3446 28.5535ZM22.1261 35.5932L15.9549 29.4221L15.2197 30.1574L20.0771 35.0147L20.0168 35.075L11.7846 33.5924L11.0614 34.3156L17.2326 40.4867L17.9798 39.7395L13.1346 34.8942L13.1948 34.8339L21.4029 36.3164L22.1261 35.5932Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -2,12 +2,14 @@ export type Player = {
lifeTotal: number;
index: number;
color: string;
iconTheme: 'light' | 'dark';
settings: PlayerSettings;
commanderDamage: CommanderDamage[];
extraCounters: ExtraCounter[];
isStartingPlayer: boolean;
hasLost: boolean;
isSide: boolean;
name: string;
};
export type PlayerSettings = {

21
src/global.d.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
export {};
export interface BeforeInstallPromptEvent extends Event {
readonly platforms: string[];
readonly userChoice: Promise<{
outcome: 'accepted' | 'dismissed';
platform: string;
}>;
prompt(): Promise<void>;
}
declare global {
interface Window {
isIOS: boolean;
isIPad: boolean;
}
interface WindowEventMap {
beforeinstallprompt: BeforeInstallPromptEvent;
transitionend: BeforeInstallPromptEvent;
}
}

View File

@@ -3,6 +3,10 @@ import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
window.isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
window.isIPad = /iPad/.test(navigator.userAgent);
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);