Skip to content

Commit a7f3067

Browse files
committed
a lot of changes
1 parent 15e00fd commit a7f3067

File tree

1 file changed

+96
-51
lines changed

1 file changed

+96
-51
lines changed

frontend/src/ts/test/test-ui.ts

Lines changed: 96 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ function updateWordsMargin(): void {
581581
} else {
582582
setTimeout(() => {
583583
$("#words").css("margin-left", "unset");
584-
$("#words .afterNewline").css("width", "");
584+
$("#words .afterNewline").css("margin-left", "unset");
585585
}, 0);
586586
}
587587
}
@@ -938,13 +938,43 @@ export async function updateActiveWordLetters(
938938
}
939939
}
940940

941+
function getNlCharWidthFromPreviousWord(
942+
element: Element | HTMLElement,
943+
checkIfIncorrect: boolean
944+
): number {
945+
let lastWordBeforeNewline: Element | null = element;
946+
while (lastWordBeforeNewline) {
947+
if (lastWordBeforeNewline.classList.contains("word")) break;
948+
lastWordBeforeNewline = lastWordBeforeNewline.previousElementSibling;
949+
}
950+
if (!lastWordBeforeNewline) return 0;
951+
952+
const letters = lastWordBeforeNewline.querySelectorAll<HTMLElement>("letter");
953+
for (const letter of letters) {
954+
if (letter.classList.contains("nlChar")) {
955+
if (checkIfIncorrect && letter.classList.contains("incorrect")) return 0;
956+
const letterComputedStyle = window.getComputedStyle(letter);
957+
const letterMargin =
958+
parseFloat(letterComputedStyle.marginLeft) +
959+
parseFloat(letterComputedStyle.marginRight);
960+
return letter.offsetWidth + letterMargin;
961+
}
962+
}
963+
964+
return 0;
965+
}
966+
941967
let allowWordRemoval = true;
942968
export function scrollTape(noRemove = false): void {
943969
if (ActivePage.get() !== "test" || resultVisible) return;
944970

945971
const waitForLineJumpAnimation = lineTransition && !allowWordRemoval;
946-
if (waitForLineJumpAnimation) noRemove = true;
972+
if (waitForLineJumpAnimation) {
973+
setTimeout(() => scrollTape(true), 50);
974+
return;
975+
}
947976

977+
// index of the active word in the collection of .word elements
948978
const wordElementIndex = TestState.activeWordIndex - activeWordElementOffset;
949979
const wordsWrapperWidth = (
950980
document.querySelector("#wordsWrapper") as HTMLElement
@@ -959,20 +989,21 @@ export function scrollTape(noRemove = false): void {
959989
const afterNewLineEls = wordsEl.getElementsByClassName("afterNewline");
960990

961991
let wordsWidthBeforeActive = 0;
962-
let fullLinesWidth = 0;
963-
let widthRemoved = 0;
992+
let fullLineWidths = 0;
964993
let wordsToRemoveCount = 0;
965994
let leadingNewLine = false;
966995
let lastAfterNewLineElement = undefined;
967-
const afterNewlinesWidths: number[] = [];
996+
let widthRemoved = 0;
997+
const widthRemovedFromLine: number[] = [];
998+
const afterNewlinesNewMargins: number[] = [];
968999
const toRemove: HTMLElement[] = [];
9691000

970-
// remove leading `.afterNewline` elements
1001+
/* remove leading `.afterNewline` elements */
9711002
for (const child of wordsChildrenArr) {
9721003
if (child.classList.contains("word")) {
9731004
// only last leading `.afterNewline` element pushes `.word`s to right
9741005
if (lastAfterNewLineElement) {
975-
widthRemoved += parseInt(lastAfterNewLineElement.style.width);
1006+
widthRemoved += parseFloat(lastAfterNewLineElement.style.marginLeft);
9761007
}
9771008
break;
9781009
} else if (child.classList.contains("afterNewline")) {
@@ -981,18 +1012,20 @@ export function scrollTape(noRemove = false): void {
9811012
lastAfterNewLineElement = child;
9821013
}
9831014
}
984-
// get last element to loop over
985-
const activeWordIndexBetweenWordsChildren =
986-
wordsChildrenArr.indexOf(activeWordEl);
1015+
1016+
/* get last element to loop over */
1017+
let lastElementIndex: number;
1018+
// index of the active word in all #words.children
1019+
// (which contains .word/.newline/.beforeNewline/.afterNewline elements)
1020+
const activeWordIndex = wordsChildrenArr.indexOf(activeWordEl);
9871021
// this will be 0 or 1
9881022
const newLinesBeforeActiveWord = wordsChildrenArr
989-
.slice(0, activeWordIndexBetweenWordsChildren)
1023+
.slice(0, activeWordIndex)
9901024
.filter((child) => child.classList.contains("afterNewline")).length;
9911025
// the second `.afterNewline` after active word is visible during line jump
9921026
let lastVisibleAfterNewline = afterNewLineEls[
9931027
newLinesBeforeActiveWord + 1
9941028
] as HTMLElement | undefined;
995-
let lastElementIndex;
9961029
if (lastVisibleAfterNewline) {
9971030
lastElementIndex = wordsChildrenArr.indexOf(lastVisibleAfterNewline);
9981031
} else {
@@ -1002,14 +1035,15 @@ export function scrollTape(noRemove = false): void {
10021035
if (lastVisibleAfterNewline) {
10031036
lastElementIndex = wordsChildrenArr.indexOf(lastVisibleAfterNewline);
10041037
} else {
1005-
lastElementIndex = activeWordIndexBetweenWordsChildren - 1;
1038+
lastElementIndex = activeWordIndex - 1;
10061039
}
10071040
}
10081041

1009-
const wordRightMargin = parseInt(
1042+
const wordRightMargin = parseFloat(
10101043
window.getComputedStyle(activeWordEl).marginRight
10111044
);
10121045

1046+
/*calculate .afterNewline & #words new margins + determine elements to remove*/
10131047
for (let i = 0; i <= lastElementIndex; i++) {
10141048
const child = wordsChildrenArr[i] as HTMLElement;
10151049
if (child.classList.contains("word")) {
@@ -1022,36 +1056,46 @@ export function scrollTape(noRemove = false): void {
10221056
widthRemoved += wordOuterWidth;
10231057
wordsToRemoveCount++;
10241058
} else {
1025-
fullLinesWidth += wordOuterWidth;
1026-
if (i < activeWordIndexBetweenWordsChildren)
1027-
wordsWidthBeforeActive = fullLinesWidth;
1059+
fullLineWidths += wordOuterWidth;
1060+
if (i < activeWordIndex) wordsWidthBeforeActive = fullLineWidths;
10281061
}
10291062
} else if (child.classList.contains("afterNewline")) {
1030-
if (!leadingNewLine) {
1031-
fullLinesWidth -= wordRightMargin;
1032-
if (i < activeWordIndexBetweenWordsChildren)
1033-
wordsWidthBeforeActive = fullLinesWidth;
1034-
if (fullLinesWidth > wordsEl.offsetWidth) {
1035-
afterNewlinesWidths.push(wordsEl.offsetWidth);
1036-
if (i < lastElementIndex)
1037-
afterNewlinesWidths.push(wordsEl.offsetWidth);
1038-
break;
1039-
} else afterNewlinesWidths.push(fullLinesWidth);
1063+
if (leadingNewLine) continue;
1064+
const nlCharWidth = getNlCharWidthFromPreviousWord(child, true);
1065+
fullLineWidths -= nlCharWidth + wordRightMargin;
1066+
if (i < activeWordIndex) wordsWidthBeforeActive = fullLineWidths;
1067+
1068+
if (fullLineWidths < wordsEl.offsetWidth) {
1069+
afterNewlinesNewMargins.push(fullLineWidths);
1070+
widthRemovedFromLine.push(widthRemoved);
1071+
} else {
1072+
afterNewlinesNewMargins.push(wordsEl.offsetWidth);
1073+
widthRemovedFromLine.push(widthRemoved);
1074+
if (i < lastElementIndex) {
1075+
// for the second .afterNewline after active word
1076+
afterNewlinesNewMargins.push(wordsEl.offsetWidth);
1077+
widthRemovedFromLine.push(widthRemoved);
1078+
}
1079+
break;
10401080
}
10411081
}
10421082
}
1083+
1084+
/* remove overflown elements */
10431085
if (toRemove.length > 0) {
10441086
activeWordElementOffset += wordsToRemoveCount;
10451087
toRemove.forEach((el) => el.remove());
1046-
for (let i = 0; i < afterNewlinesWidths.length; i++) {
1047-
const element = afterNewLineEls[i] as HTMLElement;
1048-
const currentChildMargin = parseInt(element.style.width) || 0;
1049-
element.style.width = `${currentChildMargin - widthRemoved}px`;
1050-
}
1051-
const currentWordsMargin = parseInt(wordsEl.style.marginLeft) || 0;
1088+
widthRemovedFromLine.forEach((width, index) => {
1089+
const afterNewlineEl = afterNewLineEls[index] as HTMLElement;
1090+
const currentLineIndent =
1091+
parseFloat(afterNewlineEl.style.marginLeft) || 0;
1092+
afterNewlineEl.style.marginLeft = `${currentLineIndent - width}px`;
1093+
});
1094+
const currentWordsMargin = parseFloat(wordsEl.style.marginLeft) || 0;
10521095
wordsEl.style.marginLeft = `${currentWordsMargin + widthRemoved}px`;
10531096
}
10541097

1098+
/* calculate current word width to add to #words margin */
10551099
let currentWordWidth = 0;
10561100
if (Config.tapeMode === "letter") {
10571101
if (TestInput.input.current.length > 0) {
@@ -1068,11 +1112,10 @@ export function scrollTape(noRemove = false): void {
10681112
}
10691113
}
10701114
}
1071-
1115+
/* change to new #words & .afterNewline margins */
10721116
const tapeMargin = wordsWrapperWidth * (Config.tapeMargin / 100);
1073-
let newMargin = tapeMargin - (wordsWidthBeforeActive + currentWordWidth);
1074-
if (waitForLineJumpAnimation)
1075-
newMargin = parseInt(wordsEl.style.marginLeft) || 0;
1117+
const newMargin = tapeMargin - (wordsWidthBeforeActive + currentWordWidth);
1118+
10761119
const jqWords = $(wordsEl);
10771120
if (Config.smoothLineScroll) {
10781121
jqWords.stop("leftMargin", true, false).animate(
@@ -1083,28 +1126,25 @@ export function scrollTape(noRemove = false): void {
10831126
duration: SlowTimer.get() ? 0 : 125,
10841127
queue: "leftMargin",
10851128
complete: () => {
1086-
if (noRemove) {
1087-
if (waitForLineJumpAnimation) scrollTape(true);
1088-
else scrollTape();
1089-
}
1129+
if (noRemove) scrollTape();
10901130
},
10911131
}
10921132
);
10931133
jqWords.dequeue("leftMargin");
1094-
afterNewlinesWidths.forEach((width, index) => {
1134+
afterNewlinesNewMargins.forEach((margin, index) => {
10951135
$(afterNewLineEls[index] as Element)
10961136
.stop(true, false)
10971137
.animate(
10981138
{
1099-
marginLeft: width,
1139+
marginLeft: margin,
11001140
},
11011141
SlowTimer.get() ? 0 : 125
11021142
);
11031143
});
11041144
} else {
11051145
wordsEl.style.marginLeft = `${newMargin}px`;
1106-
afterNewlinesWidths.forEach((width, index) => {
1107-
(afterNewLineEls[index] as HTMLElement).style.width = `${width}px`;
1146+
afterNewlinesNewMargins.forEach((margin, index) => {
1147+
(afterNewLineEls[index] as HTMLElement).style.marginLeft = `${margin}px`;
11081148
});
11091149
if (noRemove) scrollTape();
11101150
}
@@ -1153,16 +1193,21 @@ export function lineJump(currentTop: number, force = false): void {
11531193
if (currentTestLine > 0 || force) {
11541194
const hideBound = currentTop - 10;
11551195

1156-
const wordIndex = TestState.activeWordIndex - activeWordElementOffset;
1196+
// index of the active word in the collection of .word elements
1197+
const wordElementIndex =
1198+
TestState.activeWordIndex - activeWordElementOffset;
11571199
const wordsEl = document.getElementById("words") as HTMLElement;
11581200
const wordsChildrenArr = [...wordsEl.children];
11591201
const wordElements = wordsEl.querySelectorAll(".word");
1160-
const activeWordEl = wordElements[wordIndex];
1202+
const activeWordEl = wordElements[wordElementIndex];
11611203
if (!activeWordEl) return;
1162-
const activeWordIndexBetweenWordsChildren =
1163-
wordsChildrenArr.indexOf(activeWordEl);
1204+
1205+
// index of the active word in all #words.children
1206+
// (which contains .word/.newline/.beforeNewline/.afterNewline elements)
1207+
const activeWordIndex = wordsChildrenArr.indexOf(activeWordEl);
1208+
11641209
let lastElementToRemove = undefined;
1165-
for (let i = 0; i < activeWordIndexBetweenWordsChildren; i++) {
1210+
for (let i = 0; i < activeWordIndex; i++) {
11661211
const child = wordsChildrenArr[i] as HTMLElement;
11671212
if (child.classList.contains("hidden")) continue;
11681213
if (Math.floor(child.offsetTop) < hideBound) {
@@ -1228,7 +1273,7 @@ export function lineJump(currentTop: number, force = false): void {
12281273
currentLinesAnimating = 0;
12291274
activeWordTop = (
12301275
document.querySelectorAll("#words .word")?.[
1231-
wordIndex
1276+
wordElementIndex
12321277
] as HTMLElement
12331278
)?.offsetTop;
12341279
activeWordElementOffset +=

0 commit comments

Comments
 (0)