refactor(details): migrate game details, thumbnails and menus to v3

- GameDetails/context menus: chakra context-menu dep replaced with
  MenuRoot shims, hover lift kept, FixedSelect play-status control,
  Link/Text/Separator prop renames
- Features/Summary/SystemRequirements: Jodit + modal flows moved onto
  Dialog shims, v3 Button/Tag/Link tokens, carousel slide kept as-is
- Preserves legacy dark card/menu surfaces via gray.950 -> v2 gray.700
  mapping in the theme
This commit is contained in:
2026-09-06 10:30:39 -04:00
parent 45dd783964
commit 0919295843
11 changed files with 195 additions and 250 deletions
+20 -19
View File
@@ -1,6 +1,6 @@
import React, { useEffect } from 'react'
import agent from '../api/agent'
import { useParams } from 'react-router'
import { useParams } from 'react-router-dom'
import {
Box,
Button,
@@ -12,10 +12,9 @@ import {
Spacer,
Stack,
Text,
useColorModeValue,
useDisclosure,
useMediaQuery,
} from '@chakra-ui/react'
import {useColorModeValue} from './ui/color-mode'
import {useDisclosure, useMediaQuery} from '@chakra-ui/react'
import ImageSlider from './detailsComponents/ImageSlider'
import Summary from './detailsComponents/Summary'
import Features from './detailsComponents/Features'
@@ -26,7 +25,7 @@ import { loadingState } from '../stateManagement/loadingState'
import { ImmutableObject, useHookstate } from '@hookstate/core'
import LoadingModal from './LoadingModal'
import SteamAndDeckLogo from './helperComponents/SteamAndDeckLogo'
import { ExternalLinkIcon } from '@chakra-ui/icons'
import { LuExternalLink } from 'react-icons/lu'
import { Data } from '../models/game'
import SummaryModal from './detailsComponents/SummaryModal'
import SystemRequirementsModal from './detailsComponents/SystemRequirementsModal'
@@ -38,14 +37,14 @@ const GameDetails = () => {
const loaded = loadedState.get()
const { id } = useParams<{ id: string }>()
const bg = useColorModeValue('white', 'gray.700')
const { isOpen: isSummaryOpen, onOpen: onSummaryOpen, onClose: onSummaryClose } = useDisclosure()
const { isOpen: isSystemOpen, onOpen: onSystemOpen, onClose: onSystemClose } = useDisclosure()
const [isSmallerThan768] = useMediaQuery('(max-width: 768px)')
const { open: isSummaryOpen, onOpen: onSummaryOpen, onClose: onSummaryClose } = useDisclosure()
const { open: isSystemOpen, onOpen: onSystemOpen, onClose: onSystemClose } = useDisclosure()
const [isSmallerThan768] = useMediaQuery(['(max-width: 768px)'])
useEffect(() => {
const getGame = async () => {
loadedState.set(false)
return await agent.Games.details(id)
return await agent.Games.details(id!)
}
getGame().then((result) => {
gameState.set(result)
@@ -69,7 +68,7 @@ const GameDetails = () => {
<SystemRequirementsModal isOpen={isSystemOpen} onClose={onSystemClose} game={game} />
: ''}
<Container maxW="container.xl" mt="5">
<Container maxW="1280px" mt="5">
<Flex direction={{ base: 'column', md: 'row' }}>
<Box p={2} alignSelf={{
base: 'center',
@@ -78,25 +77,27 @@ const GameDetails = () => {
<Heading as="h1">{game.title}</Heading>
</Box>
<Spacer />
<Stack spacing={[1, 5]} direction={'row'} alignSelf={{
<Stack gap={[1, 5]} direction={'row'} alignSelf={{
base: 'center',
md: 'end'
}} pb="3">
{game.accessedBy[0].store.includes('Steam') && game.steamId.length > 0 &&
(<ChakraLink href={`steam://run/${game.steamId}`}>
<Button variant={'outline'} colorScheme="blue">
<Button variant={'outline'} colorPalette="blue">
<Flex>
<SteamAndDeckLogo size={'1.5em'} />
<Text mt={'1'} ml={'2'}>
Open with Steam <ExternalLinkIcon pb={'1'} />
Open with Steam <LuExternalLink style={{display: 'inline'}} />
</Text>
</Flex>
</Button>
</ChakraLink>)}
<Button as={Link} to={`/games/${game._id}/edit`} colorScheme="blue">
<Text mt={'1'}>
Edit
</Text>
<Button asChild colorPalette="blue">
<Link to={`/games/${game._id}/edit`}>
<Text mt={'1'}>
Edit
</Text>
</Link>
</Button>
</Stack>
</Flex>
@@ -122,7 +123,7 @@ const GameDetails = () => {
<Box bg={bg} p="5" mt="3" rounded="md">
{isSmallerThan768 ?
<Flex width="100%">
<Button colorScheme='blue' size='lg' width='100%' onMouseDown={onSummaryOpen}>
<Button colorPalette='blue' size='lg' width='100%' onMouseDown={onSummaryOpen}>
About This Game
</Button>
<Spacer />
@@ -138,7 +139,7 @@ const GameDetails = () => {
<Box bg={bg} mt="3" p="5" rounded="md">
{isSmallerThan768 ?
<Flex width="100%">
<Button colorScheme='blue' size='lg' width='100%' onMouseDown={onSystemOpen}>
<Button colorPalette='blue' size='lg' width='100%' onMouseDown={onSystemOpen}>
System Requirements
</Button>
<Spacer />
@@ -1,7 +1,7 @@
import React from 'react'
import { Link } from 'react-router-dom'
import { Data } from '../../models/game'
import { Link as ChakraLink, Flex, Heading, Image, LinkBox, LinkOverlay, Spacer, Text } from '@chakra-ui/react'
import { Link as ChakraLink, Flex, Heading, Image, Spacer, Text, Box } from '@chakra-ui/react'
import GameRating from "../detailsComponents/GameRating";
import SteamAndDeckLogo from "../helperComponents/SteamAndDeckLogo";
@@ -16,20 +16,21 @@ export default function GameThumbnail({ onToggle, game, playState }: Props) {
const gameLink = game.accessedBy[0].store.includes('Steam') && game.steamId.length > 0 ? game.steamId : game._id
return (
<>
<LinkBox
as={Link}
to={`/games/${gameLink}`}
onMouseOver={onToggle}
onMouseOut={onToggle}
<Box
asChild
>
<LinkOverlay>
<Link
to={`/games/${gameLink}`}
onMouseOver={onToggle}
onMouseOut={onToggle}
>
<Image borderTopRadius="md" src={game.frontImage} alt={game.title} />
</LinkOverlay>
<Heading m={2} size="md" my="2">
{game.steamId !== '' ? `${game.steamId}: ${game.title}` : `${game.title}`}
</Heading>
<Text ml={2}>Play Status: {playState}</Text>
</LinkBox>
<Heading m={2} size="md" my="2">
{game.steamId !== '' ? `${game.steamId}: ${game.title}` : `${game.title}`}
</Heading>
<Text ml={2}>Play Status: {playState}</Text>
</Link>
</Box>
<Flex m={2}>
<GameRating game={game} size={'1em'} />
<Spacer />
@@ -1,9 +1,8 @@
import React, { useState } from 'react';
import { Data } from "../../models/game";
import { ContextMenu } from 'chakra-ui-contextmenu';
import { MenuItem, MenuList } from '@chakra-ui/menu';
import { Menu, Portal } from '@chakra-ui/react';
import GameThumbnail from "./GameThumbnail";
import { Box, MenuGroup, useDisclosure } from "@chakra-ui/react";
import { Box, useDisclosure } from "@chakra-ui/react";
import GameThumbnailSlide from "./GameThumbnailSlide";
import { playStatusValues } from "../../componentUtils/SelectValues";
import agent from "../../api/agent";
@@ -14,7 +13,7 @@ interface Props {
}
const GameThumbnailContextMenu = ({ game }: Props) => {
const { isOpen, onToggle } = useDisclosure()
const { open: isOpen, onToggle } = useDisclosure()
const [playState, setPlayState] = useState(game.accessedBy[0].playStatus)
const gameLink = game.steamId.length > 1 ? game.steamId : game._id
@@ -34,20 +33,10 @@ const GameThumbnailContextMenu = ({ game }: Props) => {
let mql = window.matchMedia('(max-width: 768px)');
return (
<ContextMenu<HTMLDivElement> key={game._id} renderMenu={() => (
<MenuList>
<MenuItem><Link to={`/games/${gameLink}`} target="_blank" rel="noopener noreferrer">Open in New Tab</Link></MenuItem>
<MenuGroup title={'Change PlayStatus'}>
{playStatusValues.map(value => <MenuItem
onClick={() => rightClick(value.label, value.value)}
>{value.label}</MenuItem>)}
</MenuGroup>
</MenuList>
)}>
{ref => <>
<Menu.Root key={game._id}>
<Menu.ContextTrigger asChild>
<Box
ref={ref}
border={'1px'}
borderWidth={'1px'} borderStyle={'solid'}
borderColor={'gray.500'}
maxW={'sm'}
rounded={'md'}
@@ -58,10 +47,28 @@ const GameThumbnailContextMenu = ({ game }: Props) => {
>
<GameThumbnail onToggle={onToggle} game={game} playState={playState} />
</Box>
{(!mql.matches) && <GameThumbnailSlide isOpen={isOpen} game={game} />}
</>
}
</ContextMenu>
</Menu.ContextTrigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="open-new-tab" asChild>
<Link to={`/games/${gameLink}`} target="_blank" rel="noopener noreferrer">Open in New Tab</Link>
</Menu.Item>
<Menu.ItemGroup>
<Menu.ItemGroupLabel>Change PlayStatus</Menu.ItemGroupLabel>
{playStatusValues.map((value, idx) => (
<Menu.Item
key={idx}
value={value.value}
onSelect={() => rightClick(value.label, value.value)}
>{value.label}</Menu.Item>
))}
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Portal>
{(!mql.matches) && <GameThumbnailSlide isOpen={isOpen} game={game} />}
</Menu.Root>
);
};
@@ -1,5 +1,5 @@
import React from 'react';
import {Box, Container, Heading, HStack, Slide} from "@chakra-ui/react";
import {Box, Container, Heading, HStack, Presence} from "@chakra-ui/react";
import {Data} from "../../models/game";
import ImageSlider from "../detailsComponents/ImageSlider";
@@ -10,17 +10,25 @@ interface Props {
const GameThumbnailSlide = ({isOpen, game}: Props) => {
return (
<Slide direction="bottom" in={isOpen} unmountOnExit={true} style={{zIndex: 10}}>
<Presence
present={isOpen}
position="fixed"
bottom="0"
insetX="0"
animationName={{ _open: "slide-from-bottom-full", _closed: "slide-to-bottom-full" }}
animationDuration="moderate"
style={{zIndex: 10}}
>
<Box
p="40px"
color="white"
mt="4"
bgGradient={'linear(to-br, #314755, #26a0da)'}
bgImage={'linear-gradient(to bottom right, #314755, #26a0da)'}
borderTopRadius="md"
shadow="md"
>
<Container maxW="container.xl">
<HStack spacing='2rem'>
<Container maxW="1280px">
<HStack gap='2rem'>
<ImageSlider game={game} width={'250px'}/>
<Box>
<Heading>{game.title}</Heading>
@@ -29,7 +37,7 @@ const GameThumbnailSlide = ({isOpen, game}: Props) => {
</HStack>
</Container>
</Box>
</Slide>
</Presence>
);
};
+39 -113
View File
@@ -1,5 +1,5 @@
import React, { useState as reactUseState } from 'react'
import { Badge, Box, Divider, Flex, Heading, Link, Text } from '@chakra-ui/react'
import { Badge, Box, Separator, Flex, Heading, Link, Text } from '@chakra-ui/react'
import { Data } from '../../models/game'
import { gameDashboardState } from '../../stateManagement/gameState'
import { ImmutableObject, useHookstate } from '@hookstate/core'
@@ -36,48 +36,40 @@ export default function Features({ game }: Props) {
return history('/games')
}
const badge = (label: string) => (
<Box>
<Badge colorPalette="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
{label}
</Text>
</Badge>
</Box>
)
return (
<Box mb="3">
<Heading as="h3" size="lg">Features and Information</Heading>
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Rating:
</Text>
</Badge>
</Box>
{badge('Rating:')}
<Box mt="1.5" ml="5" mr="2">
<GameRating game={game} size={'1.5em'} />
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
{game.accessedBy[0].store.includes('Steam') && game.steamId.length > 0 &&
(<>
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Steam ID:
</Text>
</Badge>
</Box>
{badge('Steam ID:')}
<Box mt="1.5" ml="5" mr="2">
<Link pt="1" href={`steam://run/${game.steamId}`}>{game.steamId}</Link>
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
</>)}
{game.series.length > 1 &&
(<>
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Series:
</Text>
</Badge>
</Box>
{badge('Series:')}
<Box mt="1.5" ml="5" mr="2">
<Link onMouseDown={() => {
gamesState.filters.series.set(game.series)
@@ -85,16 +77,10 @@ export default function Features({ game }: Props) {
}} pt="1">{game.series}</Link>
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
</>)}
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Play Status:
</Text>
</Badge>
</Box>
{badge('Play Status:')}
<Box mt="1.5" ml="5" mr="2">
<Box display={'flex'}>
{editMode ?
@@ -120,15 +106,9 @@ export default function Features({ game }: Props) {
</Box>
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Genre:
</Text>
</Badge>
</Box>
{badge('Genre:')}
<Box mt={'2'} ml={'4'}>
{game.genre.map((genre, index) => [
index > 0 && ", ",
@@ -141,15 +121,9 @@ export default function Features({ game }: Props) {
])}
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Operating Systems:
</Text>
</Badge>
</Box>
{badge('Operating Systems:')}
<Box mt="1.5" ml="5" mr="2">
<Text pt="1">
{Object.keys(game.os).filter(os => {
@@ -175,15 +149,9 @@ export default function Features({ game }: Props) {
</Text>
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Store:
</Text>
</Badge>
</Box>
{badge('Store:')}
<Box mt="1.5" ml="5" mr="2">
{game.accessedBy[0].store.map((store, index) => [
index > 0 && ", ",
@@ -196,15 +164,9 @@ export default function Features({ game }: Props) {
])}
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Developer:
</Text>
</Badge>
</Box>
{badge('Developer:')}
<Box mt="1.5" ml="5" mr="2">
{game.developer.map((developer, index) => [
index > 0 && ", ",
@@ -217,15 +179,9 @@ export default function Features({ game }: Props) {
])}
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Publisher:
</Text>
</Badge>
</Box>
{badge('Publisher:')}
<Box mt="1.5" ml="5" mr="2">
{game.publisher.map((publisher, index) => [
index > 0 && ", ",
@@ -238,15 +194,9 @@ export default function Features({ game }: Props) {
])}
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Controller Support:
</Text>
</Badge>
</Box>
{badge('Controller Support:')}
<Box mt="1.5" ml="5" mr="2">
<Link onMouseDown={() => {
gamesState.filters.controller.set(game.controller)
@@ -254,28 +204,16 @@ export default function Features({ game }: Props) {
}} pt="1">{game.controller}</Link>
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Release Date:
</Text>
</Badge>
</Box>
{badge('Release Date:')}
<Box mt="1.5" ml="5" mr="2">
<Text pt="1">{game.releaseDate}</Text>
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Soundtrack:
</Text>
</Badge>
</Box>
{badge('Soundtrack:')}
<Box mt="1.5" ml="5" mr="2">
<Link onMouseDown={() => {
gamesState.filters.soundtrack.set(game.accessedBy[0].soundtrack)
@@ -283,15 +221,9 @@ export default function Features({ game }: Props) {
}} pt="1">{game.accessedBy[0].soundtrack}</Link>
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Does it work with Intel?:
</Text>
</Badge>
</Box>
{badge('Does it work with Intel?:')}
<Box mt="1.5" ml="5" mr="2">
<Link onMouseDown={() => {
gamesState.filters.intel.set(game.intel)
@@ -299,15 +231,9 @@ export default function Features({ game }: Props) {
}} pt="1">{game.intel}</Link>
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
<Flex direction="column" m="2">
<Box>
<Badge colorScheme="blue" variant="outline" rounded="md" pl="2" pr="2">
<Text pt="1" fontSize="sm">
Does it work with WINE?:
</Text>
</Badge>
</Box>
{badge('Does it work with WINE?:')}
<Box mt="1.5" ml="5" mr="2">
<Link onMouseDown={() => {
gamesState.filters.wine.set(game.wine)
@@ -315,7 +241,7 @@ export default function Features({ game }: Props) {
}} pt="1">{game.wine}</Link>
</Box>
</Flex>
<Divider orientation="horizontal" />
<Separator borderColor="gray.200" orientation="horizontal" />
</Box>
)
}
@@ -2,11 +2,12 @@ import React from 'react';
import { Data } from '../../models/game'
import agent from "../../api/agent";
import {playStatusValues} from "../../componentUtils/SelectValues";
import {Select} from "chakra-react-select";
import {Select} from "../FixedSelect";
import {ImmutableObject} from "@hookstate/core";
interface Props {
game: Data
game: Data | ImmutableObject<Data>
playState: string
setPlayState: any
}
@@ -33,14 +34,14 @@ const GamePlayStatus = ({game, playState, setPlayState}: Props) => {
value: playState,
label: playState,
}}
onChange={(e) => {
onChange={(e: any) => {
if(e === null) return
handleClick(e.value, e.label)
}}
size={'sm'}
options={playStatusValues}
chakraStyles={{
container: (provided) => ({
container: (provided: any) => ({
...provided,
width: "150px"
})
@@ -5,10 +5,11 @@ import agent from "../../api/agent";
import {Flex, Link} from "@chakra-ui/react";
import {IoGameController, IoGameControllerOutline} from "react-icons/io5";
import {BiReset} from "react-icons/bi";
import {ImmutableObject} from "@hookstate/core";
interface Props {
game: Data
game: Data | ImmutableObject<Data>
size: string
}
+3 -3
View File
@@ -1,7 +1,7 @@
import React from 'react'
import { Data } from '../../models/game'
import { Interweave } from 'interweave'
import { Divider, Box, Heading } from '@chakra-ui/react'
import { Separator, Box, Heading } from '@chakra-ui/react'
import { ImmutableObject } from '@hookstate/core'
interface Props {
@@ -15,7 +15,7 @@ export default function Summary({ game }: Props) {
{reviews.length >= 1 && (
<>
<Heading as="h1">Reviews</Heading>
<Divider m="3" />
<Separator borderColor="gray.200" m="3" />
<Interweave
content={reviews}
allowAttributes
@@ -24,7 +24,7 @@ export default function Summary({ game }: Props) {
</>
)}
<Heading mt={reviews.length >= 1 ? '10' : ''} as="h1">About This Game</Heading>
<Divider m="3" />
<Separator borderColor="gray.200" m="3" />
<Interweave
content={summary}
allowAttributes
@@ -1,5 +1,6 @@
import React from 'react'
import { Button, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay } from '@chakra-ui/react'
import { Button, Dialog } from '@chakra-ui/react'
import {DialogBody, DialogCloseTrigger, DialogContent, DialogFooter, DialogRoot} from '../ui/dialog'
import Summary from './Summary'
import { ImmutableObject } from '@hookstate/core'
import { Data } from '../../models/game'
@@ -14,20 +15,19 @@ interface Props {
const SummaryModal = ({ isOpen, onClose, game }: Props) => {
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent maxW={'90%'}>
<ModalCloseButton />
<ModalBody>
<DialogRoot open={isOpen} onOpenChange={(e) => { if (!e.open) onClose() }}>
<DialogContent maxW={'90%'}>
<DialogCloseTrigger />
<DialogBody>
<Summary game={game} />
</ModalBody>
<ModalFooter>
<Button colorScheme='blue' onMouseDown={onClose}>
</DialogBody>
<DialogFooter>
<Button colorPalette='blue' onMouseDown={onClose}>
Close
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</DialogFooter>
</DialogContent>
</DialogRoot>
)
}
@@ -1,5 +1,5 @@
import React from 'react'
import { TabList, Tab, TabPanel, TabPanels, Tabs, Heading, Box } from '@chakra-ui/react'
import { Tabs, Heading, Box } from '@chakra-ui/react'
import { Data } from '../../models/game'
import { Interweave } from 'interweave'
import { ImmutableObject } from '@hookstate/core'
@@ -20,51 +20,51 @@ export default function SystemRequirements({ game }: Props) {
return (
<Box>
<Heading as='h3' size='lg'>System Requirements</Heading>
<Tabs isLazy mt='3'>
<TabList>
<Tab>Windows</Tab>
<Tab isDisabled={macMin}>Mac</Tab>
<Tab isDisabled={linMin}>Linux</Tab>
</TabList>
<TabPanels>
<TabPanel>
<Tabs isLazy>
<TabList>
<Tab>Minimum</Tab>
<Tab isDisabled={winRec}>Recommended</Tab>
</TabList>
<TabPanels>
<TabPanel>{<Interweave content={windows.minimum} />}</TabPanel>
<TabPanel>{<Interweave content={windows.recommended} />}</TabPanel>
</TabPanels>
</Tabs>
</TabPanel>
<TabPanel>
<Tabs isLazy>
<TabList>
<Tab isDisabled={macMin}>Minimum</Tab>
<Tab isDisabled={macRec}>Recommended</Tab>
</TabList>
<TabPanels>
<TabPanel>{<Interweave content={mac.minimum} />}</TabPanel>
<TabPanel>{<Interweave content={mac.recommended} />}</TabPanel>
</TabPanels>
</Tabs>
</TabPanel>
<TabPanel>
<Tabs isLazy>
<TabList>
<Tab isDisabled={linMin}>Minimum</Tab>
<Tab isDisabled={linRec}>Recommended</Tab>
</TabList>
<TabPanels>
<TabPanel>{<Interweave content={linux.minimum} />}</TabPanel>
<TabPanel>{<Interweave content={linux.recommended} />}</TabPanel>
</TabPanels>
</Tabs>
</TabPanel>
</TabPanels>
</Tabs>
<Tabs.Root lazyMount mt='3' defaultValue="windows">
<Tabs.List>
<Tabs.Trigger value="windows">Windows</Tabs.Trigger>
<Tabs.Trigger value="mac" disabled={macMin}>Mac</Tabs.Trigger>
<Tabs.Trigger value="linux" disabled={linMin}>Linux</Tabs.Trigger>
</Tabs.List>
<Tabs.ContentGroup>
<Tabs.Content value="windows">
<Tabs.Root lazyMount defaultValue="win-min">
<Tabs.List>
<Tabs.Trigger value="win-min">Minimum</Tabs.Trigger>
<Tabs.Trigger value="win-rec" disabled={winRec}>Recommended</Tabs.Trigger>
</Tabs.List>
<Tabs.ContentGroup>
<Tabs.Content value="win-min">{<Interweave content={windows.minimum} />}</Tabs.Content>
<Tabs.Content value="win-rec">{<Interweave content={windows.recommended} />}</Tabs.Content>
</Tabs.ContentGroup>
</Tabs.Root>
</Tabs.Content>
<Tabs.Content value="mac">
<Tabs.Root lazyMount defaultValue="mac-min">
<Tabs.List>
<Tabs.Trigger value="mac-min" disabled={macMin}>Minimum</Tabs.Trigger>
<Tabs.Trigger value="mac-rec" disabled={macRec}>Recommended</Tabs.Trigger>
</Tabs.List>
<Tabs.ContentGroup>
<Tabs.Content value="mac-min">{<Interweave content={mac.minimum} />}</Tabs.Content>
<Tabs.Content value="mac-rec">{<Interweave content={mac.recommended} />}</Tabs.Content>
</Tabs.ContentGroup>
</Tabs.Root>
</Tabs.Content>
<Tabs.Content value="linux">
<Tabs.Root lazyMount defaultValue="lin-min">
<Tabs.List>
<Tabs.Trigger value="lin-min" disabled={linMin}>Minimum</Tabs.Trigger>
<Tabs.Trigger value="lin-rec" disabled={linRec}>Recommended</Tabs.Trigger>
</Tabs.List>
<Tabs.ContentGroup>
<Tabs.Content value="lin-min">{<Interweave content={linux.minimum} />}</Tabs.Content>
<Tabs.Content value="lin-rec">{<Interweave content={linux.recommended} />}</Tabs.Content>
</Tabs.ContentGroup>
</Tabs.Root>
</Tabs.Content>
</Tabs.ContentGroup>
</Tabs.Root>
</Box>
)
}
@@ -1,5 +1,6 @@
import React from 'react'
import { Button, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay } from '@chakra-ui/react'
import { Button } from '@chakra-ui/react'
import {DialogBody, DialogCloseTrigger, DialogContent, DialogFooter, DialogRoot} from '../ui/dialog'
import { ImmutableObject } from '@hookstate/core'
import { Data } from '../../models/game'
import SystemRequirements from './SystemRequirementsMenu'
@@ -14,20 +15,19 @@ interface Props {
const SummaryModal = ({ isOpen, onClose, game }: Props) => {
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent maxW={'90%'}>
<ModalCloseButton />
<ModalBody>
<DialogRoot open={isOpen} onOpenChange={(e) => { if (!e.open) onClose() }}>
<DialogContent maxW={'90%'}>
<DialogCloseTrigger />
<DialogBody>
<SystemRequirements game={game} />
</ModalBody>
<ModalFooter>
<Button colorScheme='blue' onMouseDown={onClose}>
</DialogBody>
<DialogFooter>
<Button colorPalette='blue' onMouseDown={onClose}>
Close
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</DialogFooter>
</DialogContent>
</DialogRoot>
)
}