Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { GameList } from './components/GameList/GameList';
import { Header } from './components/Header/Header';
import { GAMES } from './graphql/games';
import type { Suggestion } from './graphql/searchSuggestions';
import type { GameSummary } from './types/game';
import type { GameSummary, GameView } from './types/game';
import './App.css';

/**
Expand All @@ -13,7 +13,7 @@ import './App.css';
*/
function App() {
const [favorites, setFavorites] = useState<Set<string>>(new Set());
const [view, setView] = useState<'list' | 'grid'>('list');
const [view, setView] = useState<GameView>('list');
const [showFavorites, setShowFavorites] = useState(false);

// Søket i hovedlista. Settes først når brukeren trykker Enter, ikke for hver bokstav
Expand Down Expand Up @@ -70,23 +70,37 @@ function App() {
<div className="results-bar">
<h2 className="label">{showFavorites ? 'Favoritter' : 'Søkeresultater'}</h2>

{/* Bytter mellom visning 1 (liste) og visning 2 (rutenett) */}
<div role="group" aria-label="Visning" className="results-bar__views">
<div role="group" aria-label="Visning" className="results-bar__views">
<span aria-hidden="true">visning</span>
<button
type="button"
className="text-button"
aria-pressed={view === 'list'}
aria-label="Visning 1: liste"
title="Liste"
onClick={() => setView('list')}
>
liste
1
</button>
<button
type="button"
className="text-button"
aria-pressed={view === 'grid'}
aria-label="Visning 2: rutenett"
title="Rutenett"
onClick={() => setView('grid')}
>
rutenett
2
</button>
<button
type="button"
className="text-button"
aria-pressed={view === 'showcase'}
aria-label="Visning 3: utstilling"
title="Utstilling"
onClick={() => setView('showcase')}
>
3
</button>
</div>
</div>
Expand Down
57 changes: 57 additions & 0 deletions frontend/src/components/GameCard/GameCard.css
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,60 @@
margin-top: 6px;
}
}

/* ===== Visning 3: utstillingsrad (1 per rad, stort bilde til venstre) ===== */
.game-card--showcase {
grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
gap: 28px;
padding: 28px 0;
}

.game-card--showcase .game-card__body {
gap: 6px;
padding-right: 0; /* knappen ligger nederst her, ikke ved tittelen */
}

.game-card--showcase .game-card__title {
font-size: 26px;
}

/* Store tall med små etiketter over, som i trykk */
.game-card--showcase .game-card__scores {
gap: 32px;
margin: 16px 0;
}

.game-card--showcase .game-card__scores div {
flex-direction: column;
gap: 2px;
}

.game-card--showcase .game-card__scores dt {
font-family: var(--mono);
font-size: 12px;
letter-spacing: 0.12em;
text-transform: uppercase;
}

.game-card--showcase .game-card__scores dd {
font-size: 28px;
line-height: 1.1;
}

/* Favoritt-knappen nederst i tekstkolonnen i stedet for oppe i hjørnet */
.game-card--showcase .game-card__favorite {
position: relative;
top: auto;
right: auto;
align-self: flex-start;
margin-top: auto;
}

/* Smal skjerm: bildet over teksten */
@media (max-width: 640px) {
.game-card--showcase {
grid-template-columns: 1fr;
gap: 14px;
}
}

13 changes: 7 additions & 6 deletions frontend/src/components/GameCard/GameCard.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import type { GameSummary } from '../../types/game';
import type { GameSummary, GameView } from '../../types/game';
import './GameCard.css';

interface GameCardProps {
game: GameSummary;
isFavorite: boolean;
onToggleFavorite: (id: string) => void;
/** 'list' = visning 1 (rad), 'grid' = visning 2 (oppsummert rute) */
view?: 'list' | 'grid';
view?: GameView;
}

//Viser ett spill i resultatlista. Lenken i tittelen dekker hele kortet (via CSS), så hele kortet er klikkbart, men skjermlesere hører bare én lenke per kort.

export function GameCard({ game, isFavorite, onToggleFavorite, view = 'list' }: GameCardProps) {
const year = game.released?.slice(0, 4) ?? null;
const meta = [year, game.genres.slice(0, view === 'grid' ? 1 : 2).join(', ')]
.filter(Boolean)
.join(' · ');
// Rutenett: 1 sjanger, liste: 2, utstillingsrad: alle (undefined = ingen grense)
const maxGenres = view === 'grid' ? 1 : view === 'list' ? 2 : undefined;
const meta = [year, game.genres.slice(0, maxGenres).join(', ')].filter(Boolean).join(' · ');


return (
<article className={`game-card game-card--${view}`}>
Expand Down Expand Up @@ -47,7 +48,7 @@ export function GameCard({ game, isFavorite, onToggleFavorite, view = 'list' }:
<dt>Rating</dt>
<dd>{game.rating != null ? `${game.rating.toFixed(1)} / 5` : 'Ingen'}</dd>
</div>
{view === 'list' && (
{view !== 'grid' && (
<div>
<dt>Metacritic</dt>
<dd>{game.metacritic ?? 'Ingen'}</dd>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/GameList/GameList.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { GameSummary } from '../../types/game';
import type { GameSummary, GameView } from '../../types/game';
import { GameCard } from '../GameCard/GameCard';
import './GameList.css';

interface GameListProps {
games: GameSummary[];
favorites: Set<string>;
onToggleFavorite: (id: string) => void;
view: 'list' | 'grid';
view: GameView;
}

/**
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/types/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ export interface GameSummary {
metacritic: number | null;
genres: string[];
}

/** 'list' = visning 1 (rad), 'grid' = visning 2 (rute), 'showcase' = visning 3 (utstillingsrad) */
export type GameView = 'list' | 'grid' | 'showcase';
Loading