forked from external-repos/LifeTrinket
Compare commits
4 Commits
develop
...
swipable-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc1d5fe01d | ||
|
|
41e73d2c0c | ||
|
|
724dcf086c | ||
|
|
51f9c4d20e |
@@ -22,6 +22,7 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-screen-wake-lock": "^3.0.2",
|
||||
"react-swipeable": "^7.0.1",
|
||||
"react-twc": "^1.3.0",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
|
||||
@@ -66,10 +66,6 @@ export const CommanderDamage = ({
|
||||
const [timeoutFinished, setTimeoutFinished] = useState(false);
|
||||
const [hasPressedDown, setHasPressedDown] = useState(false);
|
||||
|
||||
const isSide =
|
||||
player.settings.rotation === Rotation.Side ||
|
||||
player.settings.rotation === Rotation.SideFlipped;
|
||||
|
||||
const handleCommanderDamageChange = (
|
||||
index: number,
|
||||
increment: number,
|
||||
@@ -132,9 +128,9 @@ export const CommanderDamage = ({
|
||||
};
|
||||
|
||||
const opponentIndex = opponent.index;
|
||||
const fontSize = isSide ? '4vmax' : '7vmin';
|
||||
const fontSize = player.isSide ? '4vmax' : '7vmin';
|
||||
const fontWeight = 'bold';
|
||||
const strokeWidth = isSide ? '0.4vmax' : '0.7vmin';
|
||||
const strokeWidth = player.isSide ? '0.4vmax' : '0.7vmin';
|
||||
|
||||
return (
|
||||
<CommanderDamageContainer
|
||||
|
||||
@@ -47,6 +47,7 @@ type ExtraCounterProps = {
|
||||
type: CounterType;
|
||||
setCounterTotal: (updatedCounterTotal: number, type: CounterType) => void;
|
||||
rotation: number;
|
||||
isSide: boolean;
|
||||
playerIndex: number;
|
||||
};
|
||||
|
||||
@@ -56,15 +57,13 @@ const ExtraCounter = ({
|
||||
setCounterTotal,
|
||||
type,
|
||||
rotation,
|
||||
isSide,
|
||||
playerIndex,
|
||||
}: ExtraCounterProps) => {
|
||||
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||
const [timeoutFinished, setTimeoutFinished] = useState(false);
|
||||
const [hasPressedDown, setHasPressedDown] = useState(false);
|
||||
|
||||
const isSide =
|
||||
rotation === Rotation.Side || rotation === Rotation.SideFlipped;
|
||||
|
||||
const handleCountChange = (increment: number) => {
|
||||
if (!counterTotal) {
|
||||
setCounterTotal(increment, type);
|
||||
|
||||
@@ -4,13 +4,11 @@ import { Rotation } from '../../Types/Player';
|
||||
import { RotationDivProps } from './CommanderDamage';
|
||||
|
||||
const LoseButton = twc.div<RotationDivProps>((props) => [
|
||||
'absolute flex-grow border-none outline-none cursor-pointer bg-interface-loseButton-background rounded-lg select-none z-[1] webkit-user-select-none',
|
||||
'absolute flex-grow border-none outline-none cursor-pointer bg-interface-loseButton-background rounded-lg select-none z-[1] webkit-user-select-none py-2 px-4 ',
|
||||
|
||||
props.$rotation === Rotation.SideFlipped
|
||||
? `right-auto top-[15%] left-[27%]`
|
||||
: props.$rotation === Rotation.Side
|
||||
? `right-auto top-[15%] left-[27%]`
|
||||
: 'right-[15%] top-1/4',
|
||||
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||
? `left-[19%]`
|
||||
: 'top-[21%]',
|
||||
]);
|
||||
|
||||
type LoseButtonProps = {
|
||||
@@ -24,6 +22,8 @@ export const LoseGameButton = ({ rotation, onClick }: LoseButtonProps) => {
|
||||
? rotation
|
||||
: rotation === Rotation.Side
|
||||
? rotation - 180
|
||||
: rotation === Rotation.Flipped
|
||||
? rotation - 180
|
||||
: rotation;
|
||||
|
||||
return (
|
||||
@@ -33,7 +33,7 @@ export const LoseGameButton = ({ rotation, onClick }: LoseButtonProps) => {
|
||||
aria-label={`Lose Game`}
|
||||
style={{ rotate: `${calcRotation}deg` }}
|
||||
>
|
||||
<Skull size="5vmin" color="black" opacity={0.5} />
|
||||
<Skull size="8vmin" color="black" opacity={0.5} />
|
||||
</LoseButton>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -100,6 +100,7 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
|
||||
(counter) => counter.type === 'commanderTax'
|
||||
)?.value
|
||||
}
|
||||
isSide={player.isSide}
|
||||
setCounterTotal={handleCounterChange}
|
||||
playerIndex={player.index}
|
||||
/>
|
||||
@@ -114,6 +115,7 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
|
||||
(counter) => counter.type === 'partnerTax'
|
||||
)?.value
|
||||
}
|
||||
isSide={player.isSide}
|
||||
setCounterTotal={handleCounterChange}
|
||||
playerIndex={player.index}
|
||||
/>
|
||||
@@ -127,6 +129,7 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
|
||||
player.extraCounters?.find((counter) => counter.type === 'poison')
|
||||
?.value
|
||||
}
|
||||
isSide={player.isSide}
|
||||
setCounterTotal={handleCounterChange}
|
||||
playerIndex={player.index}
|
||||
/>
|
||||
@@ -140,6 +143,7 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
|
||||
player.extraCounters?.find((counter) => counter.type === 'energy')
|
||||
?.value
|
||||
}
|
||||
isSide={player.isSide}
|
||||
setCounterTotal={handleCounterChange}
|
||||
playerIndex={player.index}
|
||||
/>
|
||||
@@ -154,6 +158,7 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
|
||||
(counter) => counter.type === 'experience'
|
||||
)?.value
|
||||
}
|
||||
isSide={player.isSide}
|
||||
setCounterTotal={handleCounterChange}
|
||||
playerIndex={player.index}
|
||||
/>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import LifeCounterButton from '../Buttons/LifeCounterButton';
|
||||
import { OutlinedText } from '../Misc/OutlinedText';
|
||||
|
||||
const LifeCountainer = twc.div<RotationDivProps>((props) => [
|
||||
const LifeContainer = twc.div<RotationDivProps>((props) => [
|
||||
'flex flex-grow relative w-full h-full justify-between items-center',
|
||||
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||
? 'flex-col-reverse'
|
||||
@@ -49,7 +49,6 @@ type HealthProps = {
|
||||
|
||||
const Health = ({
|
||||
player,
|
||||
rotation,
|
||||
handleLifeChange,
|
||||
differenceKey,
|
||||
recentDifference,
|
||||
@@ -99,12 +98,13 @@ const Health = ({
|
||||
}, [textContainerRef]);
|
||||
|
||||
const calculateFontSize = (container: HTMLDivElement) => {
|
||||
const isSide =
|
||||
rotation === Rotation.SideFlipped || rotation === Rotation.Side;
|
||||
const widthRatio = player.isSide
|
||||
? container.clientHeight
|
||||
: container.clientWidth;
|
||||
|
||||
const widthRatio = isSide ? container.clientHeight : container.clientWidth;
|
||||
|
||||
const heightRatio = isSide ? container.clientWidth : container.clientHeight;
|
||||
const heightRatio = player.isSide
|
||||
? container.clientWidth
|
||||
: container.clientHeight;
|
||||
|
||||
const minRatio = Math.min(widthRatio, heightRatio);
|
||||
|
||||
@@ -116,7 +116,7 @@ const Health = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<LifeCountainer $rotation={player.settings.rotation}>
|
||||
<LifeContainer $rotation={player.settings.rotation}>
|
||||
<LifeCounterButton
|
||||
lifeTotal={player.lifeTotal}
|
||||
setLifeTotal={handleLifeChange}
|
||||
@@ -154,7 +154,7 @@ const Health = ({
|
||||
operation="add"
|
||||
increment={1}
|
||||
/>
|
||||
</LifeCountainer>
|
||||
</LifeContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSwipeable } from 'react-swipeable';
|
||||
import { twc } from 'react-twc';
|
||||
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
||||
import { usePlayers } from '../../Hooks/usePlayers';
|
||||
import { Player, Rotation } from '../../Types/Player';
|
||||
import { RotationDivProps } from '../Buttons/CommanderDamage';
|
||||
import { LoseGameButton } from '../Buttons/LoseButton';
|
||||
import SettingsButton from '../Buttons/SettingsButton';
|
||||
import CommanderDamageBar from '../Counters/CommanderDamageBar';
|
||||
import ExtraCountersBar from '../Counters/ExtraCountersBar';
|
||||
import PlayerMenu from '../Player/PlayerMenu';
|
||||
@@ -24,7 +24,7 @@ const LifeCounterWrapper = twc.div<RotationDivProps>((props) => [
|
||||
const StartingPlayerNoticeWrapper = twc.div`z-[1] flex absolute w-full h-full justify-center items-center pointer-events-none select-none webkit-user-select-none bg-primary-main`;
|
||||
|
||||
const PlayerLostWrapper = twc.div<RotationDivProps>((props) => [
|
||||
'z-[1] flex absolute w-full h-full justify-center items-center pointer-events-none select-none webkit-user-select-none bg-lifeCounter-lostWrapper',
|
||||
'z-[1] flex absolute w-full h-full justify-center items-center pointer-events-none select-none webkit-user-select-none bg-lifeCounter-lostWrapper opacity-75',
|
||||
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
|
||||
? `rotate-[${props.$rotation - 90}deg]`
|
||||
: '',
|
||||
@@ -71,14 +71,51 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
|
||||
const [showPlayerMenu, setShowPlayerMenu] = useState(false);
|
||||
const [recentDifference, setRecentDifference] = useState(0);
|
||||
const [differenceKey, setDifferenceKey] = useState(Date.now());
|
||||
const [isLandscape, setIsLandscape] = useState(false);
|
||||
|
||||
const calcRot = player.isSide
|
||||
? player.settings.rotation - 180
|
||||
: player.settings.rotation;
|
||||
|
||||
const rotationAngle = isLandscape ? calcRot : calcRot + 90;
|
||||
|
||||
const handlers = useSwipeable({
|
||||
trackMouse: true,
|
||||
onSwipedDown: () => {
|
||||
console.log(`User DOWN Swiped on player ${player.index}`);
|
||||
setShowPlayerMenu(true);
|
||||
},
|
||||
onSwipedUp: () => {
|
||||
console.log(`User UP Swiped on player ${player.index}`);
|
||||
setShowPlayerMenu(false);
|
||||
},
|
||||
|
||||
swipeDuration: 500,
|
||||
onSwiping: (eventData) => console.log(eventData),
|
||||
rotationAngle,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setRecentDifference(0);
|
||||
}, 3_000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [recentDifference]);
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
if (document.body.clientWidth > document.body.clientHeight)
|
||||
setIsLandscape(true);
|
||||
else setIsLandscape(false);
|
||||
return;
|
||||
});
|
||||
|
||||
resizeObserver.observe(document.body);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
// Cleanup: disconnect the ResizeObserver when the component unmounts.
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [recentDifference, document.body.clientHeight, document.body.clientWidth]);
|
||||
|
||||
useEffect(() => {
|
||||
if (player.showStartingPlayer) {
|
||||
@@ -124,6 +161,7 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
|
||||
<LifeCounterWrapper
|
||||
$rotation={player.settings.rotation}
|
||||
style={{ rotate: `${calcRotation}deg` }}
|
||||
{...handlers}
|
||||
>
|
||||
{settings.showStartingPlayer &&
|
||||
player.isStartingPlayer &&
|
||||
@@ -150,12 +188,6 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
|
||||
key={player.index}
|
||||
handleLifeChange={handleLifeChange}
|
||||
/>
|
||||
<SettingsButton
|
||||
onClick={() => {
|
||||
setShowPlayerMenu(!showPlayerMenu);
|
||||
}}
|
||||
rotation={player.settings.rotation}
|
||||
/>
|
||||
{playerCanLose(player) && (
|
||||
<LoseGameButton
|
||||
rotation={player.settings.rotation}
|
||||
@@ -170,9 +202,12 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
|
||||
handleLifeChange={handleLifeChange}
|
||||
/>
|
||||
<ExtraCountersBar player={player} />
|
||||
{showPlayerMenu && (
|
||||
<PlayerMenu player={player} setShowPlayerMenu={setShowPlayerMenu} />
|
||||
)}
|
||||
|
||||
<PlayerMenu
|
||||
isShown={showPlayerMenu}
|
||||
player={player}
|
||||
setShowPlayerMenu={setShowPlayerMenu}
|
||||
/>
|
||||
</LifeCounterWrapper>
|
||||
</LifeCounterContentWrapper>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,6 @@ import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
||||
import { usePlayers } from '../../Hooks/usePlayers';
|
||||
import { useSafeRotate } from '../../Hooks/useSafeRotate';
|
||||
import {
|
||||
Cross,
|
||||
Energy,
|
||||
Exit,
|
||||
Experience,
|
||||
@@ -17,10 +16,7 @@ import {
|
||||
ResetGame,
|
||||
} from '../../Icons/generated';
|
||||
import { Player, Rotation } from '../../Types/Player';
|
||||
import {
|
||||
RotationButtonProps,
|
||||
RotationDivProps,
|
||||
} from '../Buttons/CommanderDamage';
|
||||
import { RotationDivProps } from '../Buttons/CommanderDamage';
|
||||
|
||||
const CheckboxContainer = twc.div``;
|
||||
|
||||
@@ -35,6 +31,7 @@ const PlayerMenuWrapper = twc.div`
|
||||
justify-center
|
||||
z-[2]
|
||||
webkit-user-select-none
|
||||
transition-all
|
||||
`;
|
||||
|
||||
const BetterRowContainer = twc.div`
|
||||
@@ -85,21 +82,17 @@ const SettingsContainer = twc.div<RotationDivProps>((props) => [
|
||||
: 'flex-row',
|
||||
]);
|
||||
|
||||
const CloseButton = twc.button<RotationButtonProps>((props) => [
|
||||
'absolute border-none outline-none cursor-pointer bg-transparent z-[99]',
|
||||
props.$rotation === Rotation.Side
|
||||
? `top-[5%] right-auto left-[5%]`
|
||||
: props.$rotation === Rotation.SideFlipped
|
||||
? 'top-auto left-auto bottom-[5%] right-[5%]'
|
||||
: 'top-[15%] right-[5%]',
|
||||
]);
|
||||
|
||||
type PlayerMenuProps = {
|
||||
player: Player;
|
||||
setShowPlayerMenu: (showPlayerMenu: boolean) => void;
|
||||
isShown: boolean;
|
||||
};
|
||||
|
||||
const PlayerMenu = ({ player, setShowPlayerMenu }: PlayerMenuProps) => {
|
||||
const PlayerMenu = ({
|
||||
player,
|
||||
setShowPlayerMenu,
|
||||
isShown,
|
||||
}: PlayerMenuProps) => {
|
||||
const settingsContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const dialogRef = useRef<HTMLDialogElement | null>(null);
|
||||
|
||||
@@ -108,9 +101,6 @@ const PlayerMenu = ({ player, setShowPlayerMenu }: PlayerMenuProps) => {
|
||||
containerRef: settingsContainerRef,
|
||||
});
|
||||
|
||||
const handleOnClick = () => {
|
||||
setShowPlayerMenu(false);
|
||||
};
|
||||
const { fullscreen, wakeLock, goToStart } = useGlobalSettings();
|
||||
const { updatePlayer, resetCurrentGame } = usePlayers();
|
||||
|
||||
@@ -142,7 +132,6 @@ const PlayerMenu = ({ player, setShowPlayerMenu }: PlayerMenuProps) => {
|
||||
const buttonFontSize = isSide ? '1.5vmax' : '3vmin';
|
||||
const iconSize = isSide ? '6vmin' : '3vmax';
|
||||
const extraCountersSize = isSide ? '8vmin' : '4vmax';
|
||||
const closeButtonSize = isSide ? '6vmin' : '3vmax';
|
||||
|
||||
const calcRotation =
|
||||
player.settings.rotation === Rotation.Side
|
||||
@@ -156,33 +145,10 @@ const PlayerMenu = ({ player, setShowPlayerMenu }: PlayerMenuProps) => {
|
||||
//TODO: Fix hacky solution to rotation for SideFlipped
|
||||
style={{
|
||||
rotate:
|
||||
player.settings.rotation === Rotation.SideFlipped ? '180deg' : '',
|
||||
player.settings.rotation === Rotation.SideFlipped ? `180deg` : '',
|
||||
translate: isShown ? '' : player.isSide ? `-100%` : `0 -100%`,
|
||||
}}
|
||||
>
|
||||
<CloseButton
|
||||
$rotation={player.settings.rotation}
|
||||
style={{
|
||||
rotate:
|
||||
player.settings.rotation === Rotation.Side ||
|
||||
player.settings.rotation === Rotation.SideFlipped
|
||||
? `${player.settings.rotation - 180}deg`
|
||||
: '',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={handleOnClick}
|
||||
style={{
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
height: closeButtonSize,
|
||||
width: closeButtonSize,
|
||||
}}
|
||||
>
|
||||
<Cross size={closeButtonSize} />
|
||||
</Button>
|
||||
</CloseButton>
|
||||
|
||||
<SettingsContainer
|
||||
$rotation={player.settings.rotation}
|
||||
style={{
|
||||
@@ -373,7 +339,7 @@ const PlayerMenu = ({ player, setShowPlayerMenu }: PlayerMenuProps) => {
|
||||
</BetterRowContainer>
|
||||
<dialog
|
||||
ref={dialogRef}
|
||||
className="z-[9999] min-h-2/4 bg-background-default text-text-primary rounded-2xl border-none absolute top-[10%]"
|
||||
className="z-[9999] min-h-2/4 bg-background-default text-text-primary rounded-2xl border-none absolute bottom-[20%]"
|
||||
>
|
||||
<div className="h-full flex flex-col p-4 gap-2">
|
||||
<h1 className="text-center">Reset Game?</h1>
|
||||
|
||||
@@ -229,6 +229,7 @@ export const createInitialPlayers = ({
|
||||
extraCounters: [],
|
||||
commanderDamage,
|
||||
hasLost: false,
|
||||
isSide: rotation === Rotation.Side || rotation === Rotation.SideFlipped,
|
||||
};
|
||||
|
||||
players.push(player);
|
||||
|
||||
53
src/Hooks/useOrientation.ts
Normal file
53
src/Hooks/useOrientation.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export interface OrientationState {
|
||||
angle: number;
|
||||
type: string;
|
||||
}
|
||||
|
||||
const defaultState: OrientationState = {
|
||||
angle: 0,
|
||||
type: 'landscape-primary',
|
||||
};
|
||||
|
||||
export default function useOrientation(
|
||||
initialState: OrientationState = defaultState
|
||||
) {
|
||||
const [state, setState] = useState(initialState);
|
||||
const [isLandscape, setIsLandscape] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const screen = window.screen;
|
||||
let mounted = true;
|
||||
|
||||
const onChange = () => {
|
||||
if (mounted) {
|
||||
const { orientation } = screen;
|
||||
console.log(orientation);
|
||||
|
||||
if (orientation) {
|
||||
const { angle, type } = orientation;
|
||||
setState({ angle, type });
|
||||
if (type.includes('landscape')) {
|
||||
setIsLandscape(true);
|
||||
} else if (type.includes('portrait')) {
|
||||
setIsLandscape(false);
|
||||
}
|
||||
} else if (window.orientation !== undefined) {
|
||||
setState({
|
||||
angle:
|
||||
typeof window.orientation === 'number' ? window.orientation : 0,
|
||||
type: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
onChange();
|
||||
|
||||
return () => {
|
||||
mounted = false;
|
||||
};
|
||||
}, [isLandscape]);
|
||||
|
||||
return { state, isLandscape };
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export type Player = {
|
||||
isStartingPlayer: boolean;
|
||||
showStartingPlayer: boolean;
|
||||
hasLost: boolean;
|
||||
isSide: boolean;
|
||||
};
|
||||
|
||||
export type PlayerSettings = {
|
||||
|
||||
@@ -67,7 +67,7 @@ export default {
|
||||
},
|
||||
lifeCounter: {
|
||||
text: 'rgba(0, 0, 0, 0.4)',
|
||||
lostWrapper: '#00000070',
|
||||
lostWrapper: '#000000',
|
||||
},
|
||||
interface: {
|
||||
loseButton: {
|
||||
|
||||
@@ -7125,6 +7125,11 @@ react-screen-wake-lock@^3.0.2:
|
||||
resolved "https://registry.yarnpkg.com/react-screen-wake-lock/-/react-screen-wake-lock-3.0.2.tgz#ce185ebfdb74a82c89d532726738f60466f00438"
|
||||
integrity sha512-f88vcfMG1AWYRSIWQ5Qx5YVboH6TSL0F4ZlFLERZp6aKiZRGVRAAJ9wedJdO5jqTMcCDZ4OXJ8PjcSkDmvGSBg==
|
||||
|
||||
react-swipeable@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-7.0.1.tgz#cd299f5986c5e4a7ee979839658c228f660e1e0c"
|
||||
integrity sha512-RKB17JdQzvECfnVj9yDZsiYn3vH0eyva/ZbrCZXZR0qp66PBRhtg4F9yJcJTWYT5Adadi+x4NoG53BxKHwIYLQ==
|
||||
|
||||
react-transition-group@^4.4.5:
|
||||
version "4.4.5"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
|
||||
|
||||
Reference in New Issue
Block a user