feat: shells

This commit is contained in:
TrèsAbhi committed 2024-05-03 19:37:41 -05:00
1 parent cc1e3e1315
commit 98716b7f71
2 files changed
+409 -417

No files matched your search

@@ -1,251 +0,0 @@
import { Cross1Icon, ShuffleIcon } from '@radix-ui/react-icons';
import { Button, Card, Dialog, Flex, Heading, Tabs } from '@radix-ui/themes';
import { useState } from 'react';
import { ModuleButton } from '../../../../../components/ModuleButtons/ModuleButton';
import { ShellButton } from '../../../../../components/ModuleButtons/ShellButton';
import { SmallTankIcon } from '../../../../../components/SmallTankIcon';
import { resolveNearPenetration } from '../../../../../core/blitz/resolveNearPenetration';
import { pushTankopediaPath } from '../../../../../core/blitzkit/pushTankopediaPath';
import {
SHELL_NAMES,
TIER_ROMAN_NUMERALS,
} from '../../../../../core/blitzkit/tankDefinitions/constants';
import { mutateDuel, useDuel } from '../../../../../stores/duel';
import {
mutateTankopediaTemporary,
useTankopediaPersistent,
} from '../../../../../stores/tankopedia';
import { TankSearch } from '../../components/TankSearch';
interface AntagonistBarProps {
floating?: boolean;
}
export function AntagonistBar({ floating }: AntagonistBarProps) {
const antagonist = useDuel((state) => state.antagonist!);
const mode = useTankopediaPersistent((state) => state.mode);
const [tab, setTab] = useState('search');
const [antagonistSelectorOpen, setAntagonistSelectorVisible] =
useState(false);
if (mode !== 'armor') return null;
return (
<Flex justify="center">
<Card
style={
floating
? {
position: 'absolute',
bottom: 8,
left: '50%',
transform: 'translateX(-50%)',
}
: undefined
}
>
<Flex
align="center"
justify="center"
wrap="wrap"
style={{
gap: 'min(5vw, 32px)',
}}
>
<Flex>
{antagonist.gun.shells.map((shell, index) => {
return (
<ShellButton
selected={antagonist.shell.id === shell.id}
shell={shell.icon}
rowChild
first={index === 0}
key={shell.id}
last={index === antagonist.gun.shells.length - 1}
onClick={() => {
mutateDuel((draft) => {
draft.antagonist!.shell = shell;
mutateTankopediaTemporary((draft) => {
draft.shot = undefined;
});
});
}}
/>
);
})}
</Flex>
<Button
variant="ghost"
onClick={() => {
mutateDuel((draft) => {
[draft.protagonist, draft.antagonist] = [
draft.antagonist,
draft.protagonist,
];
pushTankopediaPath(draft.antagonist!.tank.id);
});
}}
>
<ShuffleIcon /> Swap
</Button>
<Flex align="center" gap="4">
<Dialog.Root
open={antagonistSelectorOpen}
onOpenChange={setAntagonistSelectorVisible}
>
<Dialog.Trigger>
<Button variant="ghost">
<Flex gap="2" align="center">
Versus {antagonist.tank.name}
<SmallTankIcon id={antagonist.tank.id} size={16} />
</Flex>
</Button>
</Dialog.Trigger>
<Dialog.Content>
<Tabs.Root
value={tab}
onValueChange={setTab}
style={{ position: 'relative' }}
>
<Dialog.Close>
<Button
variant="ghost"
style={{ position: 'absolute', right: 0, top: 8 }}
>
<Cross1Icon />
</Button>
</Dialog.Close>
<Flex gap="4" direction="column">
<Tabs.List>
<Tabs.Trigger value="search">Search</Tabs.Trigger>
<Tabs.Trigger value="configure">Configure</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="search">
<Flex
direction="column"
gap="4"
style={{ flex: 1 }}
justify="center"
>
<TankSearch
compact
onSelect={(tank) => {
mutateDuel((draft) => {
draft.antagonist!.tank = tank;
draft.antagonist!.engine = tank.engines.at(-1)!;
draft.antagonist!.track = tank.tracks.at(-1)!;
draft.antagonist!.turret = tank.turrets.at(-1)!;
draft.antagonist!.gun =
draft.antagonist!.turret.guns.at(-1)!;
draft.antagonist!.shell =
draft.antagonist!.gun.shells[0];
});
setAntagonistSelectorVisible(false);
}}
/>
</Flex>
</Tabs.Content>
<Tabs.Content value="configure">
<Flex direction="column" gap="4">
<Flex direction="column" gap="2" style={{ flex: 1 }}>
<Heading size="4">
{antagonist.tank.name} modules
</Heading>
<Flex gap="2" wrap="wrap">
<Flex>
{antagonist.tank.turrets.map((turret, index) => (
<ModuleButton
rowChild
first={index === 0}
last={
index === antagonist.tank.turrets.length - 1
}
key={turret.id}
onClick={() => {
mutateDuel((draft) => {
draft.antagonist!.turret = turret;
draft.antagonist!.gun =
turret.guns.at(-1)!;
draft.antagonist!.shell =
draft.antagonist!.gun.shells[0];
});
mutateTankopediaTemporary((draft) => {
draft.shot = undefined;
});
}}
selected={antagonist.turret.id === turret.id}
discriminator={
TIER_ROMAN_NUMERALS[turret.tier]
}
module="turret"
/>
))}
</Flex>
<Flex>
{antagonist.turret.guns.map((gun, index) => (
<ModuleButton
key={gun.id}
rowChild
first={index === 0}
last={
index === antagonist.turret.guns.length - 1
}
onClick={() => {
mutateDuel((draft) => {
draft.antagonist!.gun = gun;
draft.antagonist!.shell = gun.shells[0];
});
}}
selected={antagonist.gun.id === gun.id}
discriminator={TIER_ROMAN_NUMERALS[gun.tier]}
module="gun"
/>
))}
</Flex>
</Flex>
</Flex>
<Flex direction="column" style={{ flex: 1 }}>
<Heading size="4">Properties</Heading>
<ul>
<li>
Turret: <b>{antagonist.turret.name}</b>
</li>
<li>
Gun: <b>{antagonist.gun.name}</b>
</li>
<li>Shells:</li>
<ul>
{antagonist.gun.shells.map((shell) => (
<li key={shell.id}>
{SHELL_NAMES[shell.type]}:{' '}
<b>
{resolveNearPenetration(shell.penetration)}
mm
</b>
, {shell.damage.armor}HP
</li>
))}
</ul>
</ul>
</Flex>
</Flex>
</Tabs.Content>
</Flex>
</Tabs.Root>
</Dialog.Content>
</Dialog.Root>
</Flex>
</Flex>
</Card>
</Flex>
);
}
@@ -1,12 +1,32 @@
import { import {
Cross1Icon,
EnterFullScreenIcon, EnterFullScreenIcon,
ExitFullScreenIcon, ExitFullScreenIcon,
EyeOpenIcon, EyeOpenIcon,
GearIcon, GearIcon,
} from '@radix-ui/react-icons'; } from '@radix-ui/react-icons';
import { DropdownMenu, Flex, IconButton, Switch } from '@radix-ui/themes'; import {
Button,
Dialog,
DropdownMenu,
Flex,
Heading,
IconButton,
Switch,
Tabs,
Text,
} from '@radix-ui/themes';
import { useState } from 'react';
import { ModuleButton } from '../../../../../../../components/ModuleButtons/ModuleButton';
import { SmallTankIcon } from '../../../../../../../components/SmallTankIcon';
import { resolveNearPenetration } from '../../../../../../../core/blitz/resolveNearPenetration';
import { asset } from '../../../../../../../core/blitzkit/asset'; import { asset } from '../../../../../../../core/blitzkit/asset';
import { Pose, poseEvent } from '../../../../../../../core/blitzkit/pose'; import { Pose, poseEvent } from '../../../../../../../core/blitzkit/pose';
import {
SHELL_NAMES,
TIER_ROMAN_NUMERALS,
} from '../../../../../../../core/blitzkit/tankDefinitions/constants';
import { useEquipment } from '../../../../../../../hooks/useEquipment';
import { useFullScreen } from '../../../../../../../hooks/useFullScreen'; import { useFullScreen } from '../../../../../../../hooks/useFullScreen';
import { useApp } from '../../../../../../../stores/app'; import { useApp } from '../../../../../../../stores/app';
import { mutateDuel, useDuel } from '../../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../../stores/duel';
@@ -14,6 +34,7 @@ import mutateTankopediaPersistent, {
mutateTankopediaTemporary, mutateTankopediaTemporary,
useTankopediaPersistent, useTankopediaPersistent,
} from '../../../../../../../stores/tankopedia'; } from '../../../../../../../stores/tankopedia';
import { TankSearch } from '../../../../components/TankSearch';
import { ENVIRONMENTS } from '../../Lighting'; import { ENVIRONMENTS } from '../../Lighting';
export function Options() { export function Options() {
@@ -41,226 +62,448 @@ export function Options() {
(state) => state.model.visual.environment, (state) => state.model.visual.environment,
); );
const developerMode = useApp((state) => state.developerMode); const developerMode = useApp((state) => state.developerMode);
const gun = useDuel((state) => state.antagonist!.gun); const antagonistGun = useDuel((state) => state.antagonist!.gun);
const shell = useDuel((state) => state.antagonist!.shell); const antagonistShell = useDuel((state) => state.antagonist!.shell);
const [antagonistSelectorOpen, setAntagonistSelectorOpen] = useState(false);
const antagonistTank = useDuel((state) => state.antagonist!.tank);
const antagonistTurret = useDuel((state) => state.antagonist!.turret);
const hasCalibratedShells = useEquipment(103, true);
const [tab, setTab] = useState('search');
const hasEnhancedArmor = useEquipment(110);
return ( return (
<> <>
{mode === 'armor' && ( {mode === 'armor' && (
<Flex <Flex
gap="2"
direction="column"
style={{ style={{
position: 'absolute', position: 'absolute',
right: 16, right: 16,
top: '50%', top: '50%',
transform: 'translateY(-50%)', transform: 'translateY(-50%)',
}} }}
direction="column"
> >
{gun.shells.map((thisShell, shellIndex) => ( <Flex direction="column">
{antagonistGun.shells.map((thisShell, shellIndex) => (
<IconButton
color={thisShell.id === antagonistShell.id ? undefined : 'gray'}
variant="soft"
key={thisShell.id}
style={{
borderTopLeftRadius: shellIndex === 0 ? undefined : 0,
borderTopRightRadius: shellIndex === 0 ? undefined : 0,
borderBottomRightRadius:
shellIndex === antagonistGun.shells.length - 1
? undefined
: 0,
borderBottomLeftRadius:
shellIndex === antagonistGun.shells.length - 1
? undefined
: 0,
marginTop: shellIndex === 0 ? 0 : -1,
}}
onClick={() => {
mutateDuel((draft) => {
draft.antagonist!.shell = thisShell;
});
}}
>
<img
width={16}
height={16}
src={asset(`icons/shells/${thisShell.icon}.webp`)}
/>
</IconButton>
))}
</Flex>
<Flex direction="column" style={{ pointerEvents: 'auto' }}>
<IconButton <IconButton
color={thisShell.id === shell.id ? undefined : 'gray'} color={hasCalibratedShells ? undefined : 'gray'}
variant="soft" variant="soft"
key={thisShell.id}
style={{ style={{
borderTopLeftRadius: shellIndex === 0 ? undefined : 0, borderBottomRightRadius: 0,
borderTopRightRadius: shellIndex === 0 ? undefined : 0, borderBottomLeftRadius: 0,
borderBottomRightRadius:
shellIndex === gun.shells.length - 1 ? undefined : 0,
borderBottomLeftRadius:
shellIndex === gun.shells.length - 1 ? undefined : 0,
marginTop: shellIndex === 0 ? 0 : -1,
}} }}
onClick={() => { onClick={() => {
mutateDuel((draft) => { mutateDuel((draft) => {
draft.antagonist!.shell = thisShell; draft.antagonist!.equipmentMatrix[0][0] = hasCalibratedShells
? 0
: 1;
}); });
}} }}
> >
<img <img
width={16} width={16}
height={16} height={16}
src={asset(`icons/shells/${thisShell.icon}.webp`)} src={asset('icons/equipment/103.webp')}
/> />
</IconButton> </IconButton>
))} <IconButton
color={hasEnhancedArmor ? undefined : 'gray'}
variant="soft"
style={{
borderTopRightRadius: 0,
borderTopLeftRadius: 0,
marginTop: -1,
}}
onClick={() => {
mutateDuel((draft) => {
draft.protagonist!.equipmentMatrix[1][1] = hasEnhancedArmor
? 0
: -1;
});
}}
>
<img
width={16}
height={16}
src={asset('icons/equipment/110.webp')}
/>
</IconButton>
</Flex>
</Flex> </Flex>
)} )}
<Flex <Flex
gap="4"
align="center"
style={{ style={{
position: 'absolute', position: 'absolute',
bottom: 16, bottom: 16,
left: '50%', left: '50%',
transform: 'translateX(-50%)', transform: 'translateX(-50%)',
}} }}
direction="column"
align="center"
gap="2"
> >
<Flex {mode === 'armor' && (
style={{ <Flex align="center" gap="2">
cursor: 'default', <Text color="gray" size="2">
userSelect: 'none', Shooter:
}} </Text>
align="center" <Dialog.Root
gap="2" open={antagonistSelectorOpen}
onClick={() => { onOpenChange={setAntagonistSelectorOpen}
mutateTankopediaPersistent((draft) => { >
draft.mode = draft.mode === 'armor' ? 'model' : 'armor'; <Dialog.Trigger>
}); <Button variant="ghost">
}} <Flex gap="2" align="center">
mr="2" {antagonistTank.name}
> <SmallTankIcon id={antagonistTank.id} size={16} />
<Switch size="1" checked={mode === 'armor'} /> </Flex>
Armor </Button>
</Flex> </Dialog.Trigger>
<DropdownMenu.Root> <Dialog.Content>
<DropdownMenu.Trigger> <Tabs.Root
<IconButton variant="ghost" color="gray"> value={tab}
<EyeOpenIcon /> onValueChange={setTab}
</IconButton> style={{ position: 'relative' }}
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item onClick={() => poseEvent.emit(Pose.HullDown)}>
Hull down
</DropdownMenu.Item>
<DropdownMenu.Item onClick={() => poseEvent.emit(Pose.FaceHug)}>
Face hug
</DropdownMenu.Item>
<DropdownMenu.Item onClick={() => poseEvent.emit(Pose.Default)}>
Default
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<IconButton variant="ghost" color="gray">
<GearIcon />
</IconButton>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
{mode === 'armor' && (
<>
<DropdownMenu.Label>Armor</DropdownMenu.Label>
<DropdownMenu.CheckboxItem
checked={showSpacedArmor}
onCheckedChange={(checked) => {
mutateTankopediaPersistent((draft) => {
draft.model.visual.showSpacedArmor = checked;
});
mutateTankopediaTemporary((draft) => {
draft.shot = undefined;
});
}}
> >
Spaced armor <Dialog.Close>
</DropdownMenu.CheckboxItem> <Button
variant="ghost"
style={{ position: 'absolute', right: 0, top: 8 }}
>
<Cross1Icon />
</Button>
</Dialog.Close>
<DropdownMenu.CheckboxItem <Flex gap="4" direction="column">
checked={greenPenetration} <Tabs.List>
onCheckedChange={(checked) => { <Tabs.Trigger value="search">Search</Tabs.Trigger>
mutateTankopediaPersistent((draft) => { <Tabs.Trigger value="configure">Configure</Tabs.Trigger>
draft.model.visual.greenPenetration = checked; </Tabs.List>
});
}} <Tabs.Content value="search">
> <Flex
Green penetration direction="column"
</DropdownMenu.CheckboxItem> gap="4"
style={{ flex: 1 }}
justify="center"
>
<TankSearch
compact
onSelect={(tank) => {
mutateDuel((draft) => {
draft.antagonist!.tank = tank;
draft.antagonist!.engine = tank.engines.at(-1)!;
draft.antagonist!.track = tank.tracks.at(-1)!;
draft.antagonist!.turret = tank.turrets.at(-1)!;
draft.antagonist!.gun =
draft.antagonist!.turret.guns.at(-1)!;
draft.antagonist!.shell =
draft.antagonist!.gun.shells[0];
});
setAntagonistSelectorOpen(false);
}}
/>
</Flex>
</Tabs.Content>
<Tabs.Content value="configure">
<Flex direction="column" gap="4">
<Flex direction="column" gap="2" style={{ flex: 1 }}>
<Heading size="4">
{antagonistTank.name} modules
</Heading>
<Flex gap="2" wrap="wrap">
<Flex>
{antagonistTank.turrets.map((turret, index) => (
<ModuleButton
rowChild
first={index === 0}
last={
index === antagonistTank.turrets.length - 1
}
key={turret.id}
onClick={() => {
mutateDuel((draft) => {
draft.antagonist!.turret = turret;
draft.antagonist!.gun =
turret.guns.at(-1)!;
draft.antagonist!.shell =
draft.antagonist!.gun.shells[0];
});
mutateTankopediaTemporary((draft) => {
draft.shot = undefined;
});
}}
selected={antagonistTurret.id === turret.id}
discriminator={
TIER_ROMAN_NUMERALS[turret.tier]
}
module="turret"
/>
))}
</Flex>
<Flex>
{antagonistTurret.guns.map((gun, index) => (
<ModuleButton
key={gun.id}
rowChild
first={index === 0}
last={
index === antagonistTurret.guns.length - 1
}
onClick={() => {
mutateDuel((draft) => {
draft.antagonist!.gun = gun;
draft.antagonist!.shell = gun.shells[0];
});
}}
selected={antagonistGun.id === gun.id}
discriminator={TIER_ROMAN_NUMERALS[gun.tier]}
module="gun"
/>
))}
</Flex>
</Flex>
</Flex>
<Flex direction="column" style={{ flex: 1 }}>
<Heading size="4">Properties</Heading>
<ul>
<li>
Turret: <b>{antagonistTurret.name}</b>
</li>
<li>
Gun: <b>{antagonistGun.name}</b>
</li>
<li>Shells:</li>
<ul>
{antagonistGun.shells.map((shell) => (
<li key={shell.id}>
{SHELL_NAMES[shell.type]}:{' '}
<b>
{resolveNearPenetration(shell.penetration)}
mm
</b>
, {shell.damage.armor}HP
</li>
))}
</ul>
</ul>
</Flex>
</Flex>
</Tabs.Content>
</Flex>
</Tabs.Root>
</Dialog.Content>
</Dialog.Root>
</Flex>
)}
<Flex gap="4" align="center">
<Flex
style={{
cursor: 'default',
userSelect: 'none',
}}
align="center"
gap="2"
onClick={() => {
mutateTankopediaPersistent((draft) => {
draft.mode = draft.mode === 'armor' ? 'model' : 'armor';
});
}}
mr="2"
>
<Switch size="1" checked={mode === 'armor'} />
Armor
</Flex>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<IconButton variant="ghost" color="gray">
<EyeOpenIcon />
</IconButton>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item onClick={() => poseEvent.emit(Pose.HullDown)}>
Hull down
</DropdownMenu.Item>
<DropdownMenu.Item onClick={() => poseEvent.emit(Pose.FaceHug)}>
Face hug
</DropdownMenu.Item>
<DropdownMenu.Item onClick={() => poseEvent.emit(Pose.Default)}>
Default
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<IconButton variant="ghost" color="gray">
<GearIcon />
</IconButton>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
{mode === 'armor' && (
<>
<DropdownMenu.Label>Armor</DropdownMenu.Label>
{developerMode && (
<DropdownMenu.CheckboxItem <DropdownMenu.CheckboxItem
checked={wireframe} checked={showSpacedArmor}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
mutateTankopediaPersistent((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.visual.wireframe = checked; draft.model.visual.showSpacedArmor = checked;
});
mutateTankopediaTemporary((draft) => {
draft.shot = undefined;
}); });
}} }}
> >
Wireframe Spaced armor
</DropdownMenu.CheckboxItem> </DropdownMenu.CheckboxItem>
)}
<DropdownMenu.CheckboxItem <DropdownMenu.CheckboxItem
checked={opaque} checked={greenPenetration}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
mutateTankopediaPersistent((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.visual.opaque = checked; draft.model.visual.greenPenetration = checked;
}); });
}} }}
> >
Opaque Green penetration
</DropdownMenu.CheckboxItem> </DropdownMenu.CheckboxItem>
</>
)}
<DropdownMenu.Label>Environment</DropdownMenu.Label> {developerMode && (
<DropdownMenu.CheckboxItem
<DropdownMenu.CheckboxItem checked={wireframe}
checked={showGrid} onCheckedChange={(checked) => {
onCheckedChange={(checked) => {
mutateTankopediaPersistent((draft) => {
draft.model.visual.showGrid = checked;
});
}}
>
Show grid
</DropdownMenu.CheckboxItem>
<DropdownMenu.CheckboxItem
checked={showEnvironment}
onCheckedChange={(checked) => {
mutateTankopediaPersistent((draft) => {
draft.model.visual.showEnvironment = checked;
});
}}
>
{/* TODO: consider removing this? */}
View environment
</DropdownMenu.CheckboxItem>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>Lighting</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.RadioGroup value={environment}>
{ENVIRONMENTS.map((environment) => (
<DropdownMenu.RadioItem
key={environment}
value={environment}
onClick={() => {
mutateTankopediaPersistent((draft) => { mutateTankopediaPersistent((draft) => {
draft.model.visual.environment = environment; draft.model.visual.wireframe = checked;
}); });
}} }}
> >
{environment[0].toUpperCase() + environment.slice(1)} Wireframe
</DropdownMenu.RadioItem> </DropdownMenu.CheckboxItem>
))} )}
</DropdownMenu.RadioGroup>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
</DropdownMenu.Content>
</DropdownMenu.Root>
{fullScreenAvailable && ( <DropdownMenu.CheckboxItem
<IconButton checked={opaque}
color="gray" onCheckedChange={(checked) => {
variant="ghost" mutateTankopediaPersistent((draft) => {
onClick={() => { draft.model.visual.opaque = checked;
if (isFullScreen) { });
document.exitFullscreen(); }}
} else document.body.requestFullscreen(); >
}} Opaque
> </DropdownMenu.CheckboxItem>
{isFullScreen ? <ExitFullScreenIcon /> : <EnterFullScreenIcon />} </>
</IconButton> )}
)}
<DropdownMenu.Label>Environment</DropdownMenu.Label>
<DropdownMenu.CheckboxItem
checked={showGrid}
onCheckedChange={(checked) => {
mutateTankopediaPersistent((draft) => {
draft.model.visual.showGrid = checked;
});
}}
>
Show grid
</DropdownMenu.CheckboxItem>
<DropdownMenu.CheckboxItem
checked={showEnvironment}
onCheckedChange={(checked) => {
mutateTankopediaPersistent((draft) => {
draft.model.visual.showEnvironment = checked;
});
}}
>
{/* TODO: consider removing this? */}
View environment
</DropdownMenu.CheckboxItem>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>Lighting</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.RadioGroup value={environment}>
{ENVIRONMENTS.map((environment) => (
<DropdownMenu.RadioItem
key={environment}
value={environment}
onClick={() => {
mutateTankopediaPersistent((draft) => {
draft.model.visual.environment = environment;
});
}}
>
{environment[0].toUpperCase() + environment.slice(1)}
</DropdownMenu.RadioItem>
))}
</DropdownMenu.RadioGroup>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
</DropdownMenu.Content>
</DropdownMenu.Root>
{fullScreenAvailable && (
<IconButton
color="gray"
variant="ghost"
onClick={() => {
if (isFullScreen) {
document.exitFullscreen();
} else document.body.requestFullscreen();
}}
>
{isFullScreen ? <ExitFullScreenIcon /> : <EnterFullScreenIcon />}
</IconButton>
)}
</Flex>
</Flex> </Flex>
</> </>
); );