Skip to content
Closed
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
59 changes: 40 additions & 19 deletions internal/frontend/lobby.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,22 @@ function hideMenu() {
menu.hidePopover();
}

// Since chromes implementation of the popup is dumb, we can't position
// it correctly without javascript.
if (!navigator.userAgent.toLowerCase().includes("firefox")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason you removed this guard? As far as I could tell, only chrome was too dumb to do this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I believe it is not needed anymore as I removed position: absolute from #menu. The popup appears at the correct location both on mobile and on desktop.

desktop:

image

mobile

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't needed to begin with. I just didn't want to meddle with default browser behaviour where I could, I think 🤔 Like the comment above mentions, firefox doesn't require the code at all to work correctly. I assume this would still be the case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I'm following. I removed the chrome-specific (or, rather, any non-firefox browser) guard because it wasn't required anymore as with the updated code the menu appears correctly on both chrome and firefox.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point was, that chrome NEEDED the code and firefox didn't. Maybe I wasn't clear.

Chrome with the toggle listener removed:

image

Firefox with the toggle listener removed:

image

However, it seems that there's now a way to achieve this WITHOUT Javascript.

I will try :)

Copy link
Member

@Bios-Marcel Bios-Marcel Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, if you are interested: a40b07a

I will merge in a separate PR to save you applying that stuff

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, nice 👍

const menu_button = document.getElementById("menu-button");
menu.addEventListener("toggle", (event) => {
if (event.newState === "open") {
const bounds = menu_button.getBoundingClientRect();
// Technically this won't correctly handle the scrolling
// position, but we'll cope for now.
menu.style.top = bounds.bottom + "px";
const menu_button = document.getElementById("menu-button");
menu.addEventListener("toggle", (event) => {
if (event.newState === "open") {
const bounds = menu_button.getBoundingClientRect();
menu.style.top = bounds.bottom + "px";

// making sure the menu doesn't go off-screen
const menuWidth = menu.offsetWidth;
const viewportWidth = window.innerWidth;
if (bounds.left + menuWidth > viewportWidth) {
menu.style.left = (viewportWidth - menuWidth - 5) + "px";
} else {
menu.style.left = bounds.left + "px";
}
});
}
}
});

function showDialog(id, title, contentNode, buttonBar) {
hideMenu();
Expand Down Expand Up @@ -1608,17 +1610,16 @@ function updateRoundsDisplay() {
const applyWordHints = (wordHints, dummy) => {
const isDrawer = drawerID === ownID;

// We abuse the container to prevent the layout from jumping.
if (!dummy) {
wordContainer.style.visibility = "visible";
} else {
wordContainer.style.visibility = "hidden";
}
let wordLengths = [];
let count = 0;

wordContainer.replaceChildren(
...wordHints.map((hint) => {
...wordHints.map((hint, index) => {
const hintSpan = document.createElement("span");
hintSpan.classList.add("hint");
if (dummy) {
hintSpan.style.visibility = "hidden";
}
if (hint.character === 0) {
hintSpan.classList.add("hint-underline");
hintSpan.innerHTML = " ";
Expand All @@ -1629,13 +1630,33 @@ const applyWordHints = (wordHints, dummy) => {
hintSpan.innerText = String.fromCharCode(hint.character);
}

// space
if (hint.character === 32) {
wordLengths.push(count);
count = 0;
} else if (index === wordHints.length - 1) {
count += 1;
wordLengths.push(count);
} else {
count += 1;
}

if (hint.revealed && isDrawer) {
hintSpan.classList.add("hint-revealed");
}

return hintSpan;
}),
);

const lengthHint = document.createElement("sub");
lengthHint.classList.add("word-length-hint");
if (dummy) {
lengthHint.style.visibility = "hidden";
}
lengthHint.setAttribute("dir", wordContainer.getAttribute("dir"));
lengthHint.innerText = `(${wordLengths.join(", ")})`;
wordContainer.appendChild(lengthHint);
};

const set_dummy_word_hints = () => {
Expand Down
64 changes: 58 additions & 6 deletions internal/frontend/resources/lobby.css
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ kbd {
grid-gap: 5px;
}

#lobby-header > * {
#lobby-header > *,
#menu-button-container {
background-color: white;
height: 100%;
align-items: center;
Expand All @@ -125,6 +126,7 @@ kbd {

#lobby-header-center-element {
display: flex;
justify-content: center;
/* Hack to remove extra space between buttons */
font-size: 0;
}
Expand Down Expand Up @@ -160,6 +162,12 @@ kbd {
overflow-x: hidden;
}

.word-length-hint {
font-family: monospace;
font-size: 0.8rem;
padding-top: 0.4rem;
}

.hint-chat-message {
white-space: pre;
text-wrap-mode: wrap;
Expand Down Expand Up @@ -668,11 +676,50 @@ kbd {
}

#lobby-header {
grid-template-columns: max-content auto max-content;

grid-column-start: 1;
background-color: transparent;
display: grid;
grid-column-end: 3;
grid-column-start: 1;
grid-gap: 5px;
grid-row: 1;
grid-template-columns: 1fr auto 1fr;
grid-template-rows: auto auto;
}

#lobby-header-center-element {
display: contents;
}

#menu-button-container {
grid-column: 2;
grid-row: 1;
display: flex;
justify-content: center;
}

#round-container {
grid-column: 1;
grid-row: 1;
justify-content: flex-start;
}

#time-left {
grid-column: 3;
grid-row: 1;
justify-content: flex-end;
}

#word-container {
grid-column: 1 / 4;
grid-row: 2;
background-color: white;
align-items: center;
padding: 0.1rem 0.2rem;
border-radius: var(--component-border-radius);
column-gap: 0.2rem;
width: auto;
min-width: 0;
overflow: visible;
}

#round-container,
Expand Down Expand Up @@ -727,9 +774,14 @@ kbd {
}
}

#menu-button-container {
position: relative;
}

#menu {
position: absolute;
inset: unset;
position: fixed;
margin: 0;
inset: auto;
border: 1px solid gray;
border-radius: var(--component-border-radius);
}
Expand Down
134 changes: 66 additions & 68 deletions internal/frontend/templates/lobby.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,77 +33,75 @@
</div>

<div id="lobby-header-center-element">
<div>
<div>
<button id="menu-button" popovertarget="menu" alt="Show menu" title="Show menu">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "menu.svg"}}'
class="header-button-image" />
</button>
<div id="menu" popover>
<div class="menu-list">
<!-- this button is basically behaving like a checkbox, but in order to
have a uniform look with the other buttons in the header, we are not using
a checkbox anymore. -->
<button id="toggle-sound-button" class="dialog-button menu-item header-button"
alt="{{.Translation.Get "toggle-soundeffects"}}"
title="{{.Translation.Get "toggle-soundeffects"}}">
<img id="sound-toggle-label" class="header-button-image" />
{{.Translation.Get "toggle-soundeffects"}}
</button>
<button id="toggle-pen-pressure-button"
class="dialog-button menu-item header-button"
alt="{{.Translation.Get "toggle-pen-pressure"}}"
title="{{.Translation.Get "toggle-pen-pressure"}}">
<img id="pen-pressure-toggle-label" class="header-button-image" />
{{.Translation.Get "toggle-pen-pressure"}}
</button>
<button id="name-change-button" class="dialog-button menu-item header-button"
alt="{{.Translation.Get "change-your-name"}}"
title="{{.Translation.Get "change-your-name"}}">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "user.svg"}}'
class="header-button-image" />
{{.Translation.Get "change-your-name"}}
</button>
<button id="toggle-fullscreen-button" class="dialog-button menu-item header-button"
alt="{{.Translation.Get "toggle-fullscreen"}}"
title="{{.Translation.Get "toggle-fullscreen"}}">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "fullscreen.svg"}}'
class="header-button-image" />
{{.Translation.Get "toggle-fullscreen"}}
</button>
<button id="toggle-spectate-button" class="dialog-button menu-item header-button"
alt="{{.Translation.Get "toggle-spectate"}}"
title="{{.Translation.Get "toggle-spectate"}}">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "spectate.svg"}}'
class="header-button-image" />
{{.Translation.Get "toggle-spectate"}}
</button>
<button id="help-button" class="dialog-button menu-item header-button"
alt="{{.Translation.Get "show-help"}}" title="{{.Translation.Get "show-help"}}">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "help.svg"}}'
class="header-button-image" />
{{.Translation.Get "show-help"}}
</button>
<button id="kick-button" class="dialog-button menu-item header-button"
alt="{{.Translation.Get "votekick-a-player"}}"
title="{{.Translation.Get "votekick-a-player"}}">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "kick.png"}}'
class="header-button-image" />
{{.Translation.Get "votekick-a-player"}}
</button>
<button id="lobby-settings-button" style="display: none;"
class="dialog-button menu-item header-button"
alt="{{.Translation.Get "change-lobby-settings-tooltip"}}"
title="{{.Translation.Get "change-lobby-settings-tooltip"}}">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "settings.svg"}}'
class="header-button-image" />
{{.Translation.Get "change-lobby-settings-tooltip"}}
</button>
</div>
<div id="menu-button-container">
<button id="menu-button" popovertarget="menu" alt="Show menu" title="Show menu">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "menu.svg"}}'
class="header-button-image" />
</button>
<div id="menu" popover>
<div class="menu-list">
<!-- this button is basically behaving like a checkbox, but in order to
have a uniform look with the other buttons in the header, we are not using
a checkbox anymore. -->
<button id="toggle-sound-button" class="dialog-button menu-item header-button"
alt="{{.Translation.Get "toggle-soundeffects"}}"
title="{{.Translation.Get "toggle-soundeffects"}}">
<img id="sound-toggle-label" class="header-button-image" />
{{.Translation.Get "toggle-soundeffects"}}
</button>
<button id="toggle-pen-pressure-button"
class="dialog-button menu-item header-button"
alt="{{.Translation.Get "toggle-pen-pressure"}}"
title="{{.Translation.Get "toggle-pen-pressure"}}">
<img id="pen-pressure-toggle-label" class="header-button-image" />
{{.Translation.Get "toggle-pen-pressure"}}
</button>
<button id="name-change-button" class="dialog-button menu-item header-button"
alt="{{.Translation.Get "change-your-name"}}"
title="{{.Translation.Get "change-your-name"}}">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "user.svg"}}'
class="header-button-image" />
{{.Translation.Get "change-your-name"}}
</button>
<button id="toggle-fullscreen-button" class="dialog-button menu-item header-button"
alt="{{.Translation.Get "toggle-fullscreen"}}"
title="{{.Translation.Get "toggle-fullscreen"}}">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "fullscreen.svg"}}'
class="header-button-image" />
{{.Translation.Get "toggle-fullscreen"}}
</button>
<button id="toggle-spectate-button" class="dialog-button menu-item header-button"
alt="{{.Translation.Get "toggle-spectate"}}"
title="{{.Translation.Get "toggle-spectate"}}">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "spectate.svg"}}'
class="header-button-image" />
{{.Translation.Get "toggle-spectate"}}
</button>
<button id="help-button" class="dialog-button menu-item header-button"
alt="{{.Translation.Get "show-help"}}" title="{{.Translation.Get "show-help"}}">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "help.svg"}}'
class="header-button-image" />
{{.Translation.Get "show-help"}}
</button>
<button id="kick-button" class="dialog-button menu-item header-button"
alt="{{.Translation.Get "votekick-a-player"}}"
title="{{.Translation.Get "votekick-a-player"}}">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "kick.png"}}'
class="header-button-image" />
{{.Translation.Get "votekick-a-player"}}
</button>
<button id="lobby-settings-button" style="display: none;"
class="dialog-button menu-item header-button"
alt="{{.Translation.Get "change-lobby-settings-tooltip"}}"
title="{{.Translation.Get "change-lobby-settings-tooltip"}}">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "settings.svg"}}'
class="header-button-image" />
{{.Translation.Get "change-lobby-settings-tooltip"}}
</button>
</div>
</div>
</div>
<div id="word-container" {{if eq .IsWordpackRtl true}} dir="rtl" {{else}} dir="ltr" {{end}}></div>
<div id="word-container" {{if eq .IsWordpackRtl true}} dir="rtl" {{else}} dir="ltr" {{end}}></div>
</div>
<div id="time-left">
<img src='{{.RootPath}}/resources/{{.WithCacheBust "clock.svg"}}' class="header-button-image" />
Expand Down