Skip to content

Commit

Permalink
[UX] Improve gamepage (#3613)
Browse files Browse the repository at this point in the history
* WIP: Better UX for new gamepage

Signed-off-by: Kajot-dev <[email protected]>

* WIP: Update formatting

Signed-off-by: Kajot-dev <[email protected]>

* Sticky topbar + UI polish

* HLTB styles improvement

* Fix formatting

* Implement install button

* Fix aria-control attribute

* Remove unnecessary code

* Fix HLTB breaking when hours were triple digits

* Fix keyboard focus issues

* Formatting

* Remove opts wrapper

* Hardcode 'mainBtn' class

* Do not show focus outline when using a mouse

* Scaling and scrolling

* Fix shadow on dots menu

* Merge HLTBInline and HLTB

* Remove unnecessary outline on root element

* Add nowrap to hte HLTB hour count

* Scale bg-image correctly

* Add support for the RTL

* Style the progressbar

* Make nav buttons respect current theme

* use MUI Progress for consistency

* Better placed/scaled icons

* Icon scaling

* Formatting

* Make new gamepage work on light themes

* Bad icon order when game is uninstalled

* Reduce background opacity

---------

Signed-off-by: Kajot-dev <[email protected]>
  • Loading branch information
Kajot-dev authored Jun 2, 2024
1 parent 95a003c commit 2789f0e
Show file tree
Hide file tree
Showing 14 changed files with 452 additions and 277 deletions.
2 changes: 2 additions & 0 deletions public/locales/en/gamepage.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,11 @@
},
"changelogFor": "Changelog for {{gameTitle}}",
"downloadSize": "Download Size",
"extra_info": "Extra info",
"firstPlayed": "First Played",
"getting-download-size": "Getting download size",
"getting-install-size": "Getting install size",
"install_info": "Install info",
"installSize": "Install Size",
"language": "Language",
"lastPlayed": "Last Played",
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ body {
}
}

/* Remove outline on the root element since it's not clickable anyway and it's partially hidden away by sidebar */

#root:focus-visible {
outline: none;
}

/* disable links when the help is open, so users cannot navigate
to another page but still use the elements of the current page */
#root:has(.HelpContent.open) {
Expand Down
45 changes: 26 additions & 19 deletions src/frontend/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import SettingsModal from './screens/Settings/components/SettingsModal'
import ExternalLinkDialog from './components/UI/ExternalLinkDialog'
import WindowControls from './components/UI/WindowControls'
import classNames from 'classnames'
import { ThemeProvider, createTheme } from '@mui/material/styles'

function Root() {
const {
Expand All @@ -29,6 +30,10 @@ function Root() {
const hasNativeOverlayControls = navigator['windowControlsOverlay']?.visible
const showOverlayControls = isFrameless && !hasNativeOverlayControls

const theme = createTheme({
direction: isRTL ? 'rtl' : 'ltr'
})

return (
<div
id="app"
Expand All @@ -41,25 +46,27 @@ function Root() {
// disable dragging for all elements by default
onDragStart={(e) => e.preventDefault()}
>
<OfflineMessage />
<Sidebar />
<main className="content">
<DialogHandler />
{isSettingsModalOpen.gameInfo && (
<SettingsModal
gameInfo={isSettingsModalOpen.gameInfo}
type={isSettingsModalOpen.type}
/>
)}
<ExternalLinkDialog />
<Outlet />
</main>
<div className="controller">
<ControllerHints />
<div className="simple-keyboard"></div>
</div>
{showOverlayControls && <WindowControls />}
{experimentalFeatures.enableHelp && <Help items={help.items} />}
<ThemeProvider theme={theme}>
<OfflineMessage />
<Sidebar />
<main className="content">
<DialogHandler />
{isSettingsModalOpen.gameInfo && (
<SettingsModal
gameInfo={isSettingsModalOpen.gameInfo}
type={isSettingsModalOpen.type}
/>
)}
<ExternalLinkDialog />
<Outlet />
</main>
<div className="controller">
<ControllerHints />
<div className="simple-keyboard"></div>
</div>
{showOverlayControls && <WindowControls />}
{experimentalFeatures.enableHelp && <Help items={help.items} />}
</ThemeProvider>
</div>
)
}
Expand Down
24 changes: 24 additions & 0 deletions src/frontend/components/UI/TabPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { HTMLProps } from 'react'

type TabPanelProps = HTMLProps<HTMLDivElement> & {
children?: React.ReactNode
index: string
value: string
}

function TabPanel(props: Readonly<TabPanelProps>) {
const { children, value, index, ...other } = props

return (
<div
role="tabpanel"
id={`tabpanel-${index}`}
aria-labelledby={`tab-${index}`}
{...other}
>
{value === index && <div>{children}</div>}
</div>
)
}

export default TabPanel
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.howLongToBeat {
display: flex;
justify-content: space-around;
gap: var(--space-sm-fixed);

.circle {
width: 100px;
Expand All @@ -11,10 +13,10 @@
border-radius: 50%; /* change to a circle */
border: 2px solid; /* add a border */
color: var(--text-secondary);
margin-inline-end: 10px;
padding: 0 var(--space-xs);
background-color: transparent;
cursor: pointer;
position: relative;

&.green {
border: 3px solid green;
Expand All @@ -30,14 +32,20 @@

.circle__title {
font-size: 0.8rem;
white-space: nowrap;
text-align: center;
}

.circle__value {
font-size: 1.2rem;
white-space: nowrap;
font-weight: var(--bold);
}
}
.circle {
flex-basis: 33.3%;
aspect-ratio: 1 / 1;
height: auto;
width: fit-content;
min-width: 100px;
justify-content: center;
Expand Down
1 change: 1 addition & 0 deletions src/frontend/components/UI/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export { default as InfoBox } from './InfoBox'
export { default as LanguageSelector } from './LanguageSelector'
export { default as SelectField } from './SelectField'
export { default as SmallInfo } from './SmallInfo'
export { default as TabPanel } from './TabPanel'
export { default as TextInputField } from './TextInputField'
export { default as ToggleSwitch } from './ToggleSwitch'
export { default as UpdateComponent } from './UpdateComponent'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const CompatibilityInfo = ({ gameInfo }: Props) => {
)}
{hasSteamDeckCompat && (
<a className="iconWithText" style={{ cursor: 'default' }}>
<WineBar />
<WineBar tabIndex={-1} />
<b>
{t('info.steamdeck-compatibility-info', 'SteamDeck Compatibility')}:
</b>
Expand Down
5 changes: 3 additions & 2 deletions src/frontend/screens/Game/GamePage/components/GameStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { GameInfo, InstallProgress } from 'common/types'
import { getProgress } from 'frontend/helpers'
import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom'
import { LinearProgress } from '@mui/material'

interface Props {
gameInfo: GameInfo
Expand Down Expand Up @@ -118,9 +119,9 @@ const GameStatus = ({ gameInfo, progress, handleUpdate, hasUpdate }: Props) => {
return (
<div className="gameStatus">
{(is.installing || is.updating) && (
<progress
<LinearProgress
variant="determinate"
className="installProgress"
max={100}
value={getProgress(progress)}
/>
)}
Expand Down
66 changes: 48 additions & 18 deletions src/frontend/screens/Game/GamePage/components/HLTB.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import React, { useContext } from 'react'
import React, { useContext, useState } from 'react'
import { useTranslation } from 'react-i18next'
import GameContext from '../../GameContext'
import { Speed } from '@mui/icons-material'
import ContextProvider from 'frontend/state/ContextProvider'
import { Speed, ExpandMore } from '@mui/icons-material'
import { Accordion, AccordionSummary, AccordionDetails } from '@mui/material'
import PopoverComponent from 'frontend/components/UI/PopoverComponent'
import HowLongToBeat from 'frontend/components/UI/WikiGameInfo/components/HowLongToBeat'

const HLTB = () => {
const { t } = useTranslation('gamepage')
const { wikiInfo } = useContext(GameContext)
const { experimentalFeatures } = useContext(ContextProvider)

const [isExpanded, setIsExpanded] = useState(false)

function handleExpansionChange() {
setIsExpanded((prevExpanded) => !prevExpanded)
}

if (!wikiInfo) {
return null
Expand All @@ -19,23 +28,44 @@ const HLTB = () => {
return null
}

return (
<PopoverComponent
item={
<div
className="iconWithText"
title={t('info.clickToOpen', 'Click to open')}
>
<Speed />
<b>{t('howLongToBeat', 'How Long To Beat')}</b>
</div>
}
>
<div className="poppedElement">
<HowLongToBeat info={howlongtobeat} />
if (experimentalFeatures.enableNewDesign) {
return (
<div className="hltbWrapper">
<Accordion expanded={isExpanded} onChange={handleExpansionChange}>
<AccordionSummary
expandIcon={<ExpandMore />}
aria-controls="hltb-content"
id="hltb-header"
title={t('info.clickToOpen', 'Click to open')}
>
<Speed />
<b>{t('howLongToBeat', 'How Long To Beat')}</b>
</AccordionSummary>
<AccordionDetails>
<HowLongToBeat info={howlongtobeat} />
</AccordionDetails>
</Accordion>
</div>
</PopoverComponent>
)
)
} else {
return (
<PopoverComponent
item={
<div
className="iconWithText"
title={t('info.clickToOpen', 'Click to open')}
>
<Speed />
<b>{t('howLongToBeat', 'How Long To Beat')}</b>
</div>
}
>
<div className="poppedElement">
<HowLongToBeat info={howlongtobeat} />
</div>
</PopoverComponent>
)
}
}

export default HLTB
Loading

0 comments on commit 2789f0e

Please sign in to comment.