Compare commits

...

4 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
20 changed files with 242 additions and 242 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "life-trinket",
"private": true,
"version": "0.9.96",
"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

@@ -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

@@ -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();
@@ -396,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 &&
return (
<MainWrapper>
{players.length > 1 &&
!preStartCompleted &&
settings.preStartMode !== PreStartMode.None &&
!playing &&
settings.showStartingPlayer
) {
return (
<MainWrapper>
<PreStart gridLayout={gridLayout} />
</MainWrapper>
);
}
settings.showStartingPlayer && <PreStart />}
return (
<MainWrapper>
<Players gridLayout={gridLayout} />
</MainWrapper>
);

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 = {