Better scaling on small devices

This commit is contained in:
Viktor Rådberg
2024-03-23 16:05:29 +01:00
parent 355f4bd4cd
commit 3a568fc3ab
10 changed files with 186 additions and 132 deletions

View File

@@ -1,7 +1,7 @@
{ {
"name": "life-trinket", "name": "life-trinket",
"private": true, "private": true,
"version": "0.7.0", "version": "0.8.0",
"type": "commonjs", "type": "commonjs",
"engines": { "engines": {
"node": ">=18", "node": ">=18",

View File

@@ -21,9 +21,9 @@ const Container = twc.div<RotationDivProps>((props) => [
]); ]);
export const ExtraCountersGrid = twc.div<RotationDivProps>((props) => [ export const ExtraCountersGrid = twc.div<RotationDivProps>((props) => [
'flex absolute flex-row flex-grow pointer-events-none', 'flex absolute flex-row flex-grow pointer-events-none overflow-x-scroll overflow-y-hidden',
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
? 'flex-col-reverse h-full w-auto bottom-auto' ? 'flex-col-reverse h-full w-auto bottom-auto right-0'
: 'w-full bottom-0', : 'w-full bottom-0',
]); ]);

View File

@@ -233,6 +233,8 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
? player.settings.rotation - 180 ? player.settings.rotation - 180
: player.settings.rotation; : player.settings.rotation;
const amountOfPlayers = opponents.length + 1;
return ( return (
<LifeCounterContentWrapper style={{ background: player.color }}> <LifeCounterContentWrapper style={{ background: player.color }}>
<LifeCounterWrapper <LifeCounterWrapper
@@ -240,45 +242,49 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
style={{ rotate: `${calcRotation}deg` }} style={{ rotate: `${calcRotation}deg` }}
{...handlers} {...handlers}
> >
{!playing && settings.showStartingPlayer && player.isStartingPlayer && ( {amountOfPlayers > 1 &&
<div !playing &&
className="z-20 flex absolute w-full h-full justify-center items-center select-none cursor-pointer webkit-user-select-none" settings.showStartingPlayer &&
style={{ player.isStartingPlayer && (
rotate: `${calcRotation}deg`, <div
backgroundImage: className="z-20 flex absolute w-full h-full justify-center items-center select-none cursor-pointer webkit-user-select-none"
stopPlayerRandomization ||
!settings.useRandomStartingPlayerInterval
? `radial-gradient(circle at center, ${player.color}, ${baseColors.primary.main})`
: 'none',
}}
onClick={() => {
clearTimeout(playingTimerRef.current);
setPlaying(true);
}}
>
<DynamicText
style={{ style={{
rotate: `${calcTextRotation}deg`, rotate: `${calcRotation}deg`,
backgroundImage:
stopPlayerRandomization ||
!settings.useRandomStartingPlayerInterval
? `radial-gradient(circle at center, ${player.color}, ${baseColors.primary.main})`
: 'none',
}}
onClick={() => {
clearTimeout(playingTimerRef.current);
setPlaying(true);
}} }}
> >
<div className="flex flex-col justify-center items-center"> <DynamicText
<Paragraph>👑</Paragraph> style={{
{(stopPlayerRandomization || rotate: `${calcTextRotation}deg`,
!settings.useRandomStartingPlayerInterval) && ( }}
<> >
<Paragraph>You start!</Paragraph> <div className="flex flex-col justify-center items-center">
<Paragraph className="text-xl">(Press to hide)</Paragraph> <Paragraph>👑</Paragraph>
</> {(stopPlayerRandomization ||
)} !settings.useRandomStartingPlayerInterval) && (
</div> <>
</DynamicText> <Paragraph>You start!</Paragraph>
</div> <Paragraph className="text-xl">(Press to hide)</Paragraph>
)} </>
)}
</div>
</DynamicText>
</div>
)}
{player.hasLost && ( {player.hasLost && (
<PlayerLostWrapper $rotation={player.settings.rotation} /> <PlayerLostWrapper $rotation={player.settings.rotation} />
)} )}
{settings.showStartingPlayer && {amountOfPlayers > 1 &&
settings.showStartingPlayer &&
settings.useRandomStartingPlayerInterval && settings.useRandomStartingPlayerInterval &&
!stopPlayerRandomization && !stopPlayerRandomization &&
!playing && ( !playing && (

View File

@@ -1,4 +1,4 @@
import { Button, Checkbox } from '@mui/material'; import { Checkbox } from '@mui/material';
import { useRef } from 'react'; import { useRef } from 'react';
import { twc } from 'react-twc'; import { twc } from 'react-twc';
import { theme } from '../../Data/theme'; import { theme } from '../../Data/theme';
@@ -19,8 +19,6 @@ import {
import { Player, Rotation } from '../../Types/Player'; import { Player, Rotation } from '../../Types/Player';
import { RotationDivProps } from '../Buttons/CommanderDamage'; import { RotationDivProps } from '../Buttons/CommanderDamage';
const CheckboxContainer = twc.div``;
const PlayerMenuWrapper = twc.div` const PlayerMenuWrapper = twc.div`
flex flex
flex-col flex-col
@@ -51,7 +49,6 @@ const TogglesSection = twc.div`
flex-row flex-row
flex-wrap flex-wrap
relative relative
gap-2
h-full h-full
justify-evenly justify-evenly
items-center items-center
@@ -60,11 +57,11 @@ const TogglesSection = twc.div`
const ButtonsSections = twc.div` const ButtonsSections = twc.div`
flex flex
max-w-full max-w-full
gap-4 justify-evenly
justify-between
p-[3%]
items-center items-center
flex-wrap flex-wrap
mt-0
px-2
`; `;
const ColorPickerButton = twc.div` const ColorPickerButton = twc.div`
@@ -79,7 +76,7 @@ const ColorPickerButton = twc.div`
`; `;
const SettingsContainer = twc.div<RotationDivProps>((props) => [ const SettingsContainer = twc.div<RotationDivProps>((props) => [
'flex flex-wrap h-full w-full', 'flex flex-wrap h-full w-full overflow-y-scroll',
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
? 'flex-col' ? 'flex-col'
: 'flex-row', : 'flex-row',
@@ -98,6 +95,7 @@ const PlayerMenu = ({
}: PlayerMenuProps) => { }: PlayerMenuProps) => {
const settingsContainerRef = useRef<HTMLDivElement | null>(null); const settingsContainerRef = useRef<HTMLDivElement | null>(null);
const resetGameDialogRef = useRef<HTMLDialogElement | null>(null); const resetGameDialogRef = useRef<HTMLDialogElement | null>(null);
const endGameDialogRef = useRef<HTMLDialogElement | null>(null);
const { isSide } = useSafeRotate({ const { isSide } = useSafeRotate({
rotation: player.settings.rotation, rotation: player.settings.rotation,
@@ -112,6 +110,7 @@ const PlayerMenu = ({
setPlaying, setPlaying,
setStopPlayerRandomization, setStopPlayerRandomization,
} = useGlobalSettings(); } = useGlobalSettings();
const { updatePlayer, resetCurrentGame } = usePlayers(); const { updatePlayer, resetCurrentGame } = usePlayers();
const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => {
@@ -173,14 +172,13 @@ const PlayerMenu = ({
}} }}
ref={settingsContainerRef} ref={settingsContainerRef}
> >
{settings.showPlayerMenuCog && ( <button
<button onClick={() => setShowPlayerMenu(false)}
onClick={() => setShowPlayerMenu(false)} className="flex absolute top-0 right-2 z-10 bg-transparent items-center justify-center rounded-full border-solid border-primary-main border-2 p-[0.2rem]"
className="flex absolute top-0 right-2 z-10 w-8 h-8 bg-transparent items-center justify-center rounded-full border-solid border-primary-main border-2" >
> <Cross size={buttonFontSize} className="text-primary-main " />
<Cross size="16px" className="text-primary-main " /> </button>
</button>
)}
<BetterRowContainer> <BetterRowContainer>
<TogglesSection> <TogglesSection>
<ColorPickerButton aria-label="Color picker"> <ColorPickerButton aria-label="Color picker">
@@ -192,7 +190,7 @@ const PlayerMenu = ({
/> />
</ColorPickerButton> </ColorPickerButton>
{player.settings.useCommanderDamage && ( {player.settings.useCommanderDamage && (
<CheckboxContainer> <div>
<Checkbox <Checkbox
name="usePartner" name="usePartner"
checked={player.settings.usePartner} checked={player.settings.usePartner}
@@ -217,9 +215,9 @@ const PlayerMenu = ({
aria-checked={player.settings.usePartner} aria-checked={player.settings.usePartner}
aria-label="Partner" aria-label="Partner"
/> />
</CheckboxContainer> </div>
)} )}
<CheckboxContainer> <div>
<Checkbox <Checkbox
name="usePoison" name="usePoison"
checked={player.settings.usePoison} checked={player.settings.usePoison}
@@ -244,8 +242,8 @@ const PlayerMenu = ({
aria-checked={player.settings.usePoison} aria-checked={player.settings.usePoison}
aria-label="Poison" aria-label="Poison"
/> />
</CheckboxContainer> </div>
<CheckboxContainer> <div>
<Checkbox <Checkbox
name="useEnergy" name="useEnergy"
checked={player.settings.useEnergy} checked={player.settings.useEnergy}
@@ -270,8 +268,8 @@ const PlayerMenu = ({
aria-checked={player.settings.useEnergy} aria-checked={player.settings.useEnergy}
aria-label="Energy" aria-label="Energy"
/> />
</CheckboxContainer> </div>
<CheckboxContainer> <div>
<Checkbox <Checkbox
name="useExperience" name="useExperience"
checked={player.settings.useExperience} checked={player.settings.useExperience}
@@ -296,21 +294,22 @@ const PlayerMenu = ({
aria-checked={player.settings.useExperience} aria-checked={player.settings.useExperience}
aria-label="Experience" aria-label="Experience"
/> />
</CheckboxContainer> </div>
</TogglesSection> </TogglesSection>
<ButtonsSections className="mt-4"> <ButtonsSections>
<Button <button
variant="text" className="text-primary-main cursor-pointer webkit-user-select-none"
style={{ onClick={() => endGameDialogRef.current?.show()}
cursor: 'pointer',
userSelect: 'none',
}}
onClick={handleGoToStart}
aria-label="Back to start" aria-label="Back to start"
> >
<Exit size={iconSize} style={{ rotate: '180deg' }} /> <Exit size={iconSize} style={{ rotate: '180deg' }} />
</Button> </button>
<CheckboxContainer> <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"
>
<Checkbox <Checkbox
name="fullscreen" name="fullscreen"
checked={document.fullscreenElement ? true : false} checked={document.fullscreenElement ? true : false}
@@ -325,65 +324,113 @@ const PlayerMenu = ({
role="checkbox" role="checkbox"
aria-checked={document.fullscreenElement ? true : false} aria-checked={document.fullscreenElement ? true : false}
aria-label="Fullscreen" aria-label="Fullscreen"
style={{ padding: '4px' }}
/> />
</CheckboxContainer> </div>
<Button <button
variant={wakeLock.active ? 'contained' : 'outlined'} data-wake-lock-active={settings.keepAwake}
style={{ style={{
cursor: 'pointer',
userSelect: 'none',
fontSize: buttonFontSize, fontSize: buttonFontSize,
padding: '0 4px 0 4px',
}} }}
onClick={wakeLock.toggleWakeLock} className="text-primary-main px-1 webkit-user-select-none cursor-pointer
data-[wake-lock-active=false]:bg-secondary-dark rounded-lg border border-transparent
data-[wake-lock-active=false]:border-primary-main
"
onClick={() => {
wakeLock.toggleWakeLock();
}}
role="checkbox" role="checkbox"
aria-checked={wakeLock.active} aria-checked={settings.keepAwake}
aria-label="Keep awake" aria-label="Keep awake"
> >
Keep Awake Keep Awake
</Button> </button>
<Button <button
style={{ style={{
cursor: 'pointer', cursor: 'pointer',
userSelect: 'none', userSelect: 'none',
fontSize: buttonFontSize, fontSize: buttonFontSize,
padding: '4px', padding: '2px',
}} }}
className="text-primary-main"
onClick={() => resetGameDialogRef.current?.show()} onClick={() => resetGameDialogRef.current?.show()}
role="checkbox" role="checkbox"
aria-checked={wakeLock.active}
aria-label="Reset Game" aria-label="Reset Game"
> >
<ResetGame size={iconSize} /> <ResetGame size={iconSize} />
</Button> </button>
</ButtonsSections> </ButtonsSections>
</BetterRowContainer> </BetterRowContainer>
<dialog <dialog
ref={resetGameDialogRef} ref={resetGameDialogRef}
className="z-[999] size-full bg-background-settings" className="z-[999] size-full bg-background-settings overflow-y-scroll"
onClick={() => resetGameDialogRef.current?.close()} onClick={() => resetGameDialogRef.current?.close()}
> >
<div className="flex size-full items-center justify-center"> <div className="flex size-full items-center justify-center">
<div className="flex flex-col justify-center p-4 gap-2 bg-background-default rounded-2xl border-none"> <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">Reset Game?</h1> <h1
<div className="flex justify-evenly gap-4"> className="text-center text-text-primary"
<Button style={{ fontSize: extraCountersSize }}
variant="contained" >
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()} onClick={() => resetGameDialogRef.current?.close()}
> >
No No
</Button> </button>
<Button <button
variant="contained" className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
onClick={() => { onClick={() => {
handleResetGame(); handleResetGame();
resetGameDialogRef.current?.close(); resetGameDialogRef.current?.close();
}} }}
style={{ fontSize: iconSize }}
> >
Yes Yes
</Button> </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 }}
>
End 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={() => 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> </div>
</div> </div>

View File

@@ -42,6 +42,7 @@ export const Players = (players: PlayerType[], gridClasses: string) => {
useEffect(() => { useEffect(() => {
if ( if (
players.length > 1 &&
settings.showStartingPlayer && settings.showStartingPlayer &&
settings.useRandomStartingPlayerInterval && settings.useRandomStartingPlayerInterval &&
!stopPlayerRandomization && !stopPlayerRandomization &&
@@ -101,14 +102,17 @@ export const Players = (players: PlayerType[], gridClasses: string) => {
stopPlayerRandomization, stopPlayerRandomization,
]); ]);
const gradientColors = players.map((player) => player.color).join(', ');
return ( return (
<PlayersWrapper> <PlayersWrapper>
{settings.showStartingPlayer && {players.length > 1 &&
settings.showStartingPlayer &&
settings.useRandomStartingPlayerInterval && settings.useRandomStartingPlayerInterval &&
!stopPlayerRandomization && !stopPlayerRandomization &&
!playing && ( !playing && (
<div <div
className="absolute flex justify-center items-center bg-black bg-opacity-40 h-screen w-screen portrait:h-[100vw] portrait:w-[100vh] z-50 cursor-pointer text-5xl" className="absolute flex justify-center items-center h-screen w-screen portrait:h-[100vw] portrait:w-[100vh] z-50 cursor-pointer text-5xl"
onClick={() => { onClick={() => {
if (randomIntervalRef.current) { if (randomIntervalRef.current) {
clearInterval(randomIntervalRef.current); clearInterval(randomIntervalRef.current);
@@ -117,8 +121,16 @@ export const Players = (players: PlayerType[], gridClasses: string) => {
setStopPlayerRandomization(true); setStopPlayerRandomization(true);
}} }}
> >
<div className="bg-primary-main px-8 py-2 rounded-2xl opacity-70 text-[5vmax]"> <div className="absolute flex top-[30%] justify-center items-center px-8 py-4">
PRESS TO SELECT PLAYER <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 SELECT PLAYER
</p>
</div> </div>
</div> </div>
)} )}

View File

@@ -3,10 +3,12 @@ import React from 'react';
import { theme } from '../../../Data/theme'; 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,
@@ -303,20 +305,20 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
} }
label="" label=""
/> />
{/* <FormControlLabel <FormControlLabel
value={GridTemplateAreas.FivePlayersSide} value={Orientation.Portrait}
control={ control={
<Radio <Radio
icon={ icon={
<FivePlayersSide <FivePlayersSide
height={iconHeight} height={iconHeight}
width={iconWidth} width={iconWidth}
fill={theme.palette.secondary.main} fill={theme.palette.secondary.main}
/> />
} }
checkedIcon={ checkedIcon={
<FivePlayersSide <FivePlayersSide
height={iconHeight} height={iconHeight}
width={iconWidth} width={iconWidth}
fill={theme.palette.primary.main} fill={theme.palette.primary.main}
/> />
@@ -325,7 +327,7 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
/> />
} }
label="" label=""
/> */} />
</> </>
); );
@@ -356,20 +358,20 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
} }
label="" label=""
/> />
{/* <FormControlLabel <FormControlLabel
value={GridTemplateAreas.SixPlayersSide} value={Orientation.Portrait}
control={ control={
<Radio <Radio
icon={ icon={
<SixPlayersSide <SixPlayersSide
height={iconHeight} height={iconHeight}
width={iconWidth} width={iconWidth}
fill={theme.palette.secondary.main} fill={theme.palette.secondary.main}
/> />
} }
checkedIcon={ checkedIcon={
<SixPlayersSide <SixPlayersSide
height={iconHeight} height={iconHeight}
width={iconWidth} width={iconWidth}
fill={theme.palette.primary.main} fill={theme.palette.primary.main}
/> />
@@ -378,7 +380,7 @@ export const LayoutOptions: React.FC<LayoutOptionsProps> = ({
/> />
} }
label="" label=""
/> */} />
</> </>
); );

View File

@@ -262,18 +262,6 @@ const Start = () => {
</ToggleButtonsWrapper> </ToggleButtonsWrapper>
<FormLabel>Layout</FormLabel> <FormLabel>Layout</FormLabel>
{/* <LayoutOptions
numberOfPlayers={playerOptions.numberOfPlayers}
gridAreas={playerOptions.gridAreas}
onChange={(gridAreas) =>
setPlayerOptions({
...playerOptions,
gridAreas,
//TODO fix the layout selection
orientation: Orientation.Portrait,
})
}
/> */}
<LayoutOptions <LayoutOptions
numberOfPlayers={playerOptions.numberOfPlayers} numberOfPlayers={playerOptions.numberOfPlayers}
selectedOrientation={playerOptions.orientation} selectedOrientation={playerOptions.orientation}

View File

@@ -127,15 +127,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 +163,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;
} }

View File

@@ -23,7 +23,6 @@ export default function useOrientation(
const onChange = () => { const onChange = () => {
if (mounted) { if (mounted) {
const { orientation } = screen; const { orientation } = screen;
console.log(orientation);
if (orientation) { if (orientation) {
const { angle, type } = orientation; const { angle, type } = orientation;

View File

@@ -80,8 +80,8 @@ export default {
], ],
sixPlayers: ['player0 player1 player2', 'player3 player4 player5'], sixPlayers: ['player0 player1 player2', 'player3 player4 player5'],
sixPlayersSide: [ sixPlayersSide: [
'player0 player1 player1 player1 player1 player2 player2 player2 player2 player3', 'player0 player1 player1 player1 player1 player1 player1 player2 player2 player2 player2 player2 player2 player3',
'player0 player4 player4 player4 player4 player5 player5 player5 player5 player3', 'player0 player4 player4 player4 player4 player4 player4 player5 player5 player5 player5 player5 player5 player3',
], ],
}, },
colors: baseColors, colors: baseColors,