Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Make Game Selector into Dropdown #65

Merged
merged 3 commits into from
Jul 30, 2024
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
73 changes: 46 additions & 27 deletions src/client/gameselector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,86 @@ import { navigate, getLocationSlug } from './navigation';
import { notify } from './notices';

export class GameSelector {
dialogBox: HTMLDialogElement;
closeButton: HTMLButtonElement;
openButton: HTMLButtonElement;
element: HTMLButtonElement;
gameListElement: HTMLDivElement;

constructor() {
// Grab the elements we'll be using
this.dialogBox = document.querySelector<HTMLDialogElement>('#gameSelector');
this.closeButton = document.querySelector<HTMLButtonElement>('#gameSelector .close');
this.openButton = document.querySelector<HTMLButtonElement>('.show-game-selector');
this.element = document.querySelector<HTMLButtonElement>('#game-selector');
this.gameListElement = this.element.querySelector('.game-list');

// Hook up the button events
this.closeButton.addEventListener('click', () => this.hide());
this.openButton.addEventListener('click', () => this.show());
// Bind relevant events
window.addEventListener('click', (event) => {
if (!(event.target instanceof HTMLElement)) return;
if (event.target === this.element || event.target === this.element.firstElementChild) return this.toggle();
if (this.element.contains(event.target)) return;
this.hide();
});

window.addEventListener('keydown', (event) => {
if (event.key === 'Escape') this.hide();
});
}

/**
* Opens the Game Selector
* @param closeable Whether or not to display the close button
*/
show(closeable = true) {
this.closeButton.style.display = closeable ? 'block' : 'none';
this.dialogBox.showModal();
show() {
this.gameListElement.classList.add('active');
}

/**
* Closes the Game Selector
*/
hide() {
this.dialogBox.close();
this.closeButton.style.display = '';
this.gameListElement.classList.remove('active');
}

/**
* Toggles the Game Selector
*/
toggle() {
this.gameListElement.classList.toggle('active');
}

/**
* Populates the Game Selector with buttons
* @param games List of available games
*/
regenerate(games: { [game: string]: MenuGame }) {
// Grab and clear the selector container
const container = document.querySelector('#gameSelector .games');
container.innerHTML = '';
// Clear the current list
this.gameListElement.replaceChildren();

// Generate a button for each game
for (const [gameID, game] of Object.entries(games)) {
const btn = document.createElement('button');
btn.classList.add('game-selector', 'btn');
btn.onclick = () => {
this.switchGame(gameID);
};
btn.setAttribute('game', gameID);
btn.onclick = () => this.switchGame(gameID);
btn.classList.add('game-entry', 'btn');

const icon = document.createElement('img');
icon.src = game.icon;
icon.classList.add('icon');
icon.src = game.icon;
icon.style.background = game.color;

btn.append(icon);

const name = document.createElement('span');
name.innerText = game.name;

name.textContent = game.name;
btn.append(name);

container.append(btn);
this.gameListElement.append(btn);
}

this.updateSelected();
}

updateSelected() {
// Get currently-selected game
const currentGame = getLocationSlug().game;

// Update elements' classes
for (const entry of this.gameListElement.children) {
entry.classList.toggle('current-game', entry.getAttribute('game') === currentGame);
}
}

Expand All @@ -77,6 +95,7 @@ export class GameSelector {
const l: Slug = getLocationSlug();
l.game = game;
await navigate(l);
this.updateSelected();
} catch {
// Failed to navigate! Let's just head back to the game's home page then
await navigate(new Slug(game));
Expand Down
4 changes: 0 additions & 4 deletions src/client/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ async function init() {
gameSelector.regenerate(menu.games);
updateAllLinkListeners();

if (params.get('force') === 'gameselect') {
gameSelector.show(false);
}

navigate(slug, true, false);
}
window.addEventListener('load', init);
Expand Down
83 changes: 51 additions & 32 deletions static/resources/css/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -219,26 +219,10 @@ body:not(.nav-showTopics) .mobile-nav button#mobile-categories,
border-bottom-color: var(--theme);
}

dialog {
background: #222;
color: white;
border: var(--theme) 0.1rem solid;
border-radius: 0.5rem;
padding: 1rem;
}
dialog::backdrop {
background: #0000005f;
backdrop-filter: blur(5px);
}

dialog h2 {
margin-top: 0;
margin-bottom: 0.3rem;
}

.edit {
margin: 1rem;
}

.btn {
background: #333;
color: white;
Expand All @@ -251,30 +235,61 @@ dialog h2 {
border: none;
cursor: pointer;
}
dialog .close {
float: right;
background: none;
border: none;
cursor: pointer;
color: white;

#game-selector {
position: relative;
}

#game-selector .game-list {
display: none;
flex-direction: column;
gap: 0.2rem;

position: absolute;
top: 3.5rem;
left: 0;

padding: 0.3rem;
border-radius: 0.5rem;
background: #333;
box-shadow: 2px 4px 10px #0005;
}

.game-selector {
#game-selector .game-list.active {
display: flex;
align-items: center;
animation: 0.1s cubic-bezier(.25,.46,.45,.94) dropdown-slide;
}
.game-selector span {
padding-left: 0.4rem;

@keyframes dropdown-slide {
0% { transform: translateY(-10px); opacity: 0.0 }
100% { transform: translateY(0px); opacity: 1.0 }
}
#gameSelector .btn {

#game-selector .game-entry {
width: 100%;
margin-top: 0.5rem;
padding: 0.3rem;
border-radius: 0.4rem;
}

#game-selector .game-entry:hover {
background-color: #444;
}

#game-selector .game-entry.current-game {
background-color: #282828;
cursor: default;
}
.game-selector .icon {

#game-selector .game-entry .icon {
border-radius: 0.3rem;
padding: 0.3rem;
height: 1.7rem;
width: 1.7rem;
height: 1.5rem;
width: 1.5rem;
}

#game-selector .game-entry span {
padding: 0 0.5rem;
white-space: nowrap;
}

@media only screen and (min-width: 1000px) {
Expand Down Expand Up @@ -351,6 +366,10 @@ dialog .close {
gap: 0.5rem;
padding-left: 0.5rem;
}
#game-selector .game-list {
left: initial;
right: 0;
}
}

.content h1:first-child {
Expand Down
13 changes: 3 additions & 10 deletions templates/main.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@
<div id="current-game">%GAMENAME%</div>
<h2>Documentation</h2>
</a>
<button title="Switch game" class="icon-button show-game-selector">
<span class="mdi mdi-controller"></span>
<button title="Switch game" class="icon-button show-game-selector" id="game-selector">
<span class="mdi mdi-controller"></span>
<div class="game-list">Loading games...</div>
</button>
</div>
<div class="categories">
Expand Down Expand Up @@ -72,14 +73,6 @@ <h2>Documentation</h2>
<div class="footer">Commit: <code>%COMMIT%</code> Branch: <code>%BRANCH%</code></div>
</div>

<dialog id="gameSelector">
<button class="close">
<span class="mdi mdi-close"></span>
</button>
<h2>Select game</h2>
<div class="games">Loading games...</div>
</dialog>

<script src="/resources/js/index.bundle.js"></script>
</body>
</html>
Loading