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
20 changes: 0 additions & 20 deletions frontend/src/ts/test/caret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import * as TestInput from "./test-input";
import * as SlowTimer from "../states/slow-timer";
import * as TestState from "../test/test-state";
import * as TestWords from "./test-words";
import { prefersReducedMotion } from "../utils/misc";
import { convertRemToPixels } from "../utils/numbers";
import { splitIntoCharacters, getWordDirection } from "../utils/strings";
import { safeNumber } from "@monkeytype/util/numbers";
Expand Down Expand Up @@ -262,25 +261,6 @@ export async function updatePosition(noAnim = false): Promise<void> {
jqcaret
.stop(true, false)
.animate(animation, SlowTimer.get() || noAnim ? 0 : smoothCaretSpeed);

if (Config.showAllLines) {
const browserHeight = window.innerHeight;
const middlePos = browserHeight / 2 - (jqcaret.outerHeight() as number) / 2;
const contentHeight = document.body.scrollHeight;

if (
newTop >= middlePos &&
contentHeight > browserHeight &&
TestState.isActive
) {
const newscrolltop = newTop - middlePos / 2;
window.scrollTo({
left: 0,
top: newscrolltop,
behavior: prefersReducedMotion() ? "instant" : "smooth",
});
}
}
}

function updateStyle(): void {
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/ts/test/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,11 @@ export async function update(
$("#result"),
250,
async () => {
$("#result").trigger("focus");
const result = document.querySelector<HTMLElement>("#result");
result?.focus({
preventScroll: true,
});
Misc.scrollToCenterOrTop(result);
void AdController.renderResult();
TestUI.setResultCalculating(false);
$("#words").empty();
Expand Down
130 changes: 75 additions & 55 deletions frontend/src/ts/test/test-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,45 @@ export function reset(): void {
}

export function focusWords(): void {
$("#wordsInput").trigger("focus");
const wordsInput = document.querySelector<HTMLElement>("#wordsInput");
wordsInput?.blur();
wordsInput?.focus({
preventScroll: true,
});
if (TestState.isActive) {
keepWordsInputInTheCenter(true);
} else {
const typingTest = document.querySelector<HTMLElement>("#typingTest");
Misc.scrollToCenterOrTop(typingTest);
}
}

export function blurWords(): void {
$("#wordsInput").trigger("blur");
}

export function keepWordsInputInTheCenter(force = false): void {
const wordsInput = document.querySelector<HTMLElement>("#wordsInput");
const wordsWrapper = document.querySelector<HTMLElement>("#wordsWrapper");
if (!wordsInput || !wordsWrapper) return;

const wordsWrapperHeight = wordsWrapper.offsetHeight;
const windowHeight = window.innerHeight;

// dont do anything if the wrapper can fit on screen
if (wordsWrapperHeight < windowHeight) return;

const wordsInputRect = wordsInput.getBoundingClientRect();
const wordsInputBelowCenter = wordsInputRect.top > windowHeight / 2;

// dont do anything if its above or at the center unless forced
if (!wordsInputBelowCenter && !force) return;

wordsInput.scrollIntoView({
block: "center",
});
}

export function getWordElement(index: number): HTMLElement | null {
const el = document.querySelector<HTMLElement>(
`#words .word[data-wordindex='${index}']`
Expand Down Expand Up @@ -197,9 +229,8 @@ export function updateActiveElement(

activeWordTop = newActiveWord.offsetTop;

if (!initial && shouldUpdateWordsInputPosition()) {
void updateWordsInputPosition();
}
void updateWordsInputPosition();

if (!initial && Config.tapeMode !== "off") {
void scrollTape();
}
Expand Down Expand Up @@ -448,7 +479,7 @@ function updateWordWrapperClasses(): void {

updateWordsWidth();
updateWordsWrapperHeight(true);
updateWordsMargin(updateWordsInputPosition, [true]);
updateWordsMargin(updateWordsInputPosition, []);
}

export function showWords(): void {
Expand Down Expand Up @@ -478,65 +509,54 @@ export function appendEmptyWordElement(
`<div class='word' data-wordindex='${index}'><letter class='invisible'>_</letter></div>`
);
}
let updateWordsInputPositionAnimationFrameId: null | number = null;
export async function updateWordsInputPosition(): Promise<void> {
if (updateWordsInputPositionAnimationFrameId !== null) {
cancelAnimationFrame(updateWordsInputPositionAnimationFrameId);
}
updateWordsInputPositionAnimationFrameId = requestAnimationFrame(async () => {
updateWordsInputPositionAnimationFrameId = null;
if (ActivePage.get() !== "test") return;
const currentLanguage = await JSONData.getCurrentLanguage(Config.language);
const isLanguageRTL = currentLanguage.rightToLeft;

const posUpdateLangList = ["japanese", "chinese", "korean"];
function shouldUpdateWordsInputPosition(): boolean {
const language = posUpdateLangList.some((l) => Config.language.startsWith(l));
return language || (Config.mode !== "time" && Config.showAllLines);
}

export async function updateWordsInputPosition(initial = false): Promise<void> {
if (ActivePage.get() !== "test") return;

const currentLanguage = await JSONData.getCurrentLanguage(Config.language);
const isLanguageRTL = currentLanguage.rightToLeft;

const el = document.querySelector<HTMLElement>("#wordsInput");
const el = document.querySelector<HTMLElement>("#wordsInput");

if (!el) return;
if (!el) return;

const activeWord = getActiveWordElement();

if (!activeWord) {
el.style.top = "0px";
el.style.left = "0px";
return;
}
const activeWord = getActiveWordElement();

const computed = window.getComputedStyle(activeWord);
const activeWordMargin =
parseInt(computed.marginTop) + parseInt(computed.marginBottom);
if (!activeWord) {
el.style.top = "0px";
el.style.left = "0px";
return;
}

const letterHeight = convertRemToPixels(Config.fontSize);
const targetTop =
activeWord.offsetTop + letterHeight / 2 - el.offsetHeight / 2 + 1; //+1 for half of border
const letterHeight = convertRemToPixels(Config.fontSize);
const targetTop =
activeWord.offsetTop + letterHeight / 2 - el.offsetHeight / 2 + 1; //+1 for half of border

if (Config.tapeMode !== "off") {
el.style.maxWidth = `${100 - Config.tapeMargin}%`;
} else {
el.style.maxWidth = "";
}
if (activeWord.offsetWidth < letterHeight) {
el.style.width = letterHeight + "px";
} else {
el.style.width = activeWord.offsetWidth + "px";
}
if (Config.tapeMode !== "off") {
el.style.maxWidth = `${100 - Config.tapeMargin}%`;
} else {
el.style.maxWidth = "";
}
if (activeWord.offsetWidth < letterHeight) {
el.style.width = letterHeight + "px";
} else {
el.style.width = activeWord.offsetWidth + "px";
}

if (
initial &&
!shouldUpdateWordsInputPosition() &&
Config.tapeMode === "off"
) {
el.style.top = targetTop + letterHeight + activeWordMargin + 4 + "px";
} else {
el.style.top = targetTop + "px";
}

if (activeWord.offsetWidth < letterHeight && isLanguageRTL) {
el.style.left = activeWord.offsetLeft - letterHeight + "px";
} else {
el.style.left = Math.max(0, activeWord.offsetLeft) + "px";
}
if (activeWord.offsetWidth < letterHeight && isLanguageRTL) {
el.style.left = activeWord.offsetLeft - letterHeight + "px";
} else {
el.style.left = Math.max(0, activeWord.offsetLeft) + "px";
}

keepWordsInputInTheCenter();
});
}

let centeringActiveLine: Promise<void> = Promise.resolve();
Expand Down
4 changes: 1 addition & 3 deletions frontend/src/ts/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,7 @@ const debouncedEvent = debounce(250, () => {
}
setTimeout(() => {
void TestUI.updateWordsInputPosition();
if ($("#wordsInput").is(":focus")) {
Caret.show(true);
}
TestUI.focusWords();
}, 250);
}
});
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/ts/utils/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -746,4 +746,15 @@ export function isMacLike(): boolean {
return isPlatform(/Mac|iPod|iPhone|iPad/);
}

export function scrollToCenterOrTop(el: HTMLElement | null): void {
if (!el) return;

const elementHeight = el.offsetHeight;
const windowHeight = window.innerHeight;

el.scrollIntoView({
block: elementHeight < windowHeight ? "center" : "start",
});
}

// DO NOT ALTER GLOBAL OBJECTSONSTRUCTOR, IT WILL BREAK RESULT HASHES
Loading