Skip to content

Commit 2949cd9

Browse files
authored
impr(dom utils): add slideUp and slideDown utils (@Miodec) (monkeytypegame#7323)
!nuf
1 parent 33d5605 commit 2949cd9

File tree

7 files changed

+97
-75
lines changed

7 files changed

+97
-75
lines changed

frontend/__tests__/setup-tests.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ vi.mock("../src/ts/utils/dom", async (importOriginal) => {
6565
getOffsetLeft: vi.fn().mockReturnValue(0),
6666
animate: vi.fn().mockResolvedValue(null),
6767
promiseAnimate: vi.fn().mockResolvedValue(null),
68+
slideUp: vi.fn().mockResolvedValue(null),
69+
slideDown: vi.fn().mockResolvedValue(null),
6870
native: document.createElement("div"),
6971
// @ts-expect-error - mocking private method
7072
hasValue: vi.fn().mockReturnValue(false),

frontend/src/ts/commandline/lists/result-screen.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ const commands: Command[] = [
9797
display: "Toggle word history",
9898
icon: "fa-align-left",
9999
exec: (): void => {
100-
TestUI.toggleResultWords();
100+
void TestUI.toggleResultWords();
101101
},
102102
available: (): boolean => {
103103
return TestState.resultVisible;

frontend/src/ts/pages/settings.ts

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -753,30 +753,14 @@ function toggleSettingsGroup(groupName: string): void {
753753

754754
const groupEl = qs(`.pageSettings .settingsGroup.${groupName}`);
755755
if (!groupEl?.hasClass("slideup")) {
756-
groupEl?.setStyle({ overflow: "hidden" })?.animate({
757-
height: 0,
758-
duration: 250,
759-
onComplete: () => {
760-
groupEl
761-
?.hide()
762-
.setStyle({ height: "", overflow: "" })
763-
.addClass("slideup");
764-
},
765-
});
756+
void groupEl?.slideUp(250);
757+
groupEl?.addClass("slideup");
766758
$(`.pageSettings .sectionGroupTitle[group=${groupName}]`).addClass(
767759
"rotateIcon",
768760
);
769761
} else {
770-
groupEl?.show();
771-
groupEl?.setStyle({ height: "", overflow: "hidden" });
772-
const height = groupEl.getOffsetHeight();
773-
groupEl?.animate({
774-
height: [0, height],
775-
duration: 250,
776-
onComplete: () => {
777-
groupEl?.setStyle({ height: "", overflow: "" }).removeClass("slideup");
778-
},
779-
});
762+
void groupEl?.slideDown(250);
763+
groupEl?.removeClass("slideup");
780764
$(`.pageSettings .sectionGroupTitle[group=${groupName}]`).removeClass(
781765
"rotateIcon",
782766
);

frontend/src/ts/test/replay.ts

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import config from "../config";
22
import * as Sound from "../controllers/sound-controller";
33
import * as TestInput from "./test-input";
44
import * as Arrays from "../utils/arrays";
5+
import { qsr } from "../utils/dom";
56

67
type ReplayAction =
78
| "correctLetter"
@@ -30,6 +31,8 @@ let stopwatchList: NodeJS.Timeout[] = [];
3031
const toggleButton = document.getElementById("playpauseReplayButton")
3132
?.children[0];
3233

34+
const replayEl = qsr(".pageTest #resultReplay");
35+
3336
function replayGetWordsList(wordsListFromScript: string[]): void {
3437
wordsList = wordsListFromScript;
3538
}
@@ -187,24 +190,11 @@ function loadOldReplay(): number {
187190
}
188191

189192
function toggleReplayDisplay(): void {
190-
if ($("#resultReplay").stop(true, true).hasClass("hidden")) {
193+
if (replayEl.isHidden()) {
191194
initializeReplayPrompt();
192195
loadOldReplay();
193196
//show
194-
if (!$("#watchReplayButton").hasClass("loaded")) {
195-
$("#words").html(
196-
`<div class="preloader"><i class="fas fa-fw fa-spin fa-circle-notch"></i></div>`,
197-
);
198-
$("#resultReplay")
199-
.removeClass("hidden")
200-
.css("display", "none")
201-
.slideDown(250);
202-
} else {
203-
$("#resultReplay")
204-
.removeClass("hidden")
205-
.css("display", "none")
206-
.slideDown(250);
207-
}
197+
void replayEl.slideDown(250);
208198
} else {
209199
//hide
210200
if (
@@ -213,9 +203,7 @@ function toggleReplayDisplay(): void {
213203
) {
214204
pauseReplay();
215205
}
216-
$("#resultReplay").slideUp(250, () => {
217-
$("#resultReplay").addClass("hidden");
218-
});
206+
void replayEl.slideUp(250);
219207
}
220208
}
221209

frontend/src/ts/test/result.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,7 @@ export async function update(
11211121
);
11221122

11231123
if (Config.alwaysShowWordsHistory && canQuickRestart && !GlarsesMode.get()) {
1124-
TestUI.toggleResultWords(true);
1124+
void TestUI.toggleResultWords(true);
11251125
}
11261126
AdController.updateFooterAndVerticalAds(true);
11271127
void Funbox.clear();

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

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import * as XPBar from "../elements/xp-bar";
5757
import * as ModesNotice from "../elements/modes-notice";
5858
import * as Last10Average from "../elements/last-10-average";
5959
import * as MemoryFunboxTimer from "./funbox/memory-funbox-timer";
60+
import { qsr } from "../utils/dom";
6061

6162
export const updateHintsPositionDebounced = Misc.debounceUntilResolved(
6263
updateHintsPosition,
@@ -67,6 +68,7 @@ const wordsEl = document.querySelector(".pageTest #words") as HTMLElement;
6768
const wordsWrapperEl = document.querySelector(
6869
".pageTest #wordsWrapper",
6970
) as HTMLElement;
71+
const resultWordsHistoryEl = qsr(".pageTest #resultWordsHistory");
7072

7173
export let activeWordTop = 0;
7274
export let activeWordHeight = 0;
@@ -1391,42 +1393,18 @@ async function loadWordsHistory(): Promise<boolean> {
13911393
return true;
13921394
}
13931395

1394-
export function toggleResultWords(noAnimation = false): void {
1395-
if (TestState.resultVisible) {
1396-
ResultWordHighlight.updateToggleWordsHistoryTime();
1397-
if ($("#resultWordsHistory").stop(true, true).hasClass("hidden")) {
1398-
//show
1399-
1400-
if ($("#resultWordsHistory .words .word").length === 0) {
1401-
void loadWordsHistory().then(() => {
1402-
if (Config.burstHeatmap) {
1403-
void applyBurstHeatmap();
1404-
}
1405-
$("#resultWordsHistory")
1406-
.removeClass("hidden")
1407-
.css("display", "none")
1408-
.slideDown(noAnimation ? 0 : 250, () => {
1409-
if (Config.burstHeatmap) {
1410-
void applyBurstHeatmap();
1411-
}
1412-
});
1413-
});
1414-
} else {
1415-
if (Config.burstHeatmap) {
1416-
void applyBurstHeatmap();
1417-
}
1418-
$("#resultWordsHistory")
1419-
.removeClass("hidden")
1420-
.css("display", "none")
1421-
.slideDown(noAnimation ? 0 : 250);
1422-
}
1423-
} else {
1424-
//hide
1396+
export async function toggleResultWords(noAnimation = false): Promise<void> {
1397+
if (!TestState.resultVisible) return;
1398+
ResultWordHighlight.updateToggleWordsHistoryTime();
14251399

1426-
$("#resultWordsHistory").slideUp(250, () => {
1427-
$("#resultWordsHistory").addClass("hidden");
1428-
});
1400+
if (resultWordsHistoryEl.isHidden()) {
1401+
if (resultWordsHistoryEl.qsa(".words .word").length === 0) {
1402+
await loadWordsHistory();
14291403
}
1404+
void resultWordsHistoryEl.slideDown(noAnimation ? 0 : 250);
1405+
void applyBurstHeatmap();
1406+
} else {
1407+
void resultWordsHistoryEl.slideUp(noAnimation ? 0 : 250);
14301408
}
14311409
}
14321410

@@ -2005,7 +1983,7 @@ $("#wordsInput").on("focusout", () => {
20051983
});
20061984

20071985
$(".pageTest").on("click", "#showWordHistoryButton", () => {
2008-
toggleResultWords();
1986+
void toggleResultWords();
20091987
});
20101988

20111989
$("#wordsWrapper").on("click", () => {

frontend/src/ts/utils/dom.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,13 @@ export class ElementWithUtils<T extends HTMLElement = HTMLElement> {
209209
return this;
210210
}
211211

212+
/**
213+
* Check if the element has the "hidden" class
214+
*/
215+
isHidden(): boolean {
216+
return this.hasClass("hidden");
217+
}
218+
212219
/**
213220
* Check if element is visible
214221
*/
@@ -669,6 +676,69 @@ export class ElementWithUtils<T extends HTMLElement = HTMLElement> {
669676
});
670677
}
671678

679+
/**
680+
* Animate the element sliding down (expanding height from 0 to full height)
681+
* @param duration The duration of the animation in milliseconds (default: 250ms)
682+
*/
683+
async slideDown(duration = 250): Promise<void> {
684+
this.show().setStyle({
685+
height: "",
686+
overflow: "hidden",
687+
marginTop: "",
688+
marginBottom: "",
689+
});
690+
const height = this.getOffsetHeight();
691+
const computed = getComputedStyle(this.native);
692+
const marginTop = computed.marginTop;
693+
const marginBottom = computed.marginBottom;
694+
this.setStyle({ height: "0px", marginTop: "0px", marginBottom: "0px" });
695+
await this.promiseAnimate({
696+
height: [0, height],
697+
marginTop: [0, marginTop],
698+
marginBottom: [0, marginBottom],
699+
duration,
700+
onComplete: () => {
701+
this.setStyle({
702+
height: "",
703+
overflow: "",
704+
marginTop: "",
705+
marginBottom: "",
706+
});
707+
},
708+
});
709+
}
710+
711+
/**
712+
* Animate the element sliding up (collapsing height from full height to 0)
713+
* @param duration The duration of the animation in milliseconds (default: 250ms)
714+
*/
715+
async slideUp(duration = 250): Promise<void> {
716+
this.show().setStyle({
717+
overflow: "hidden",
718+
height: "",
719+
marginTop: "",
720+
marginBottom: "",
721+
});
722+
const height = this.getOffsetHeight();
723+
const computed = getComputedStyle(this.native);
724+
const marginTop = computed.marginTop;
725+
const marginBottom = computed.marginBottom;
726+
await this.promiseAnimate({
727+
height: [height, 0],
728+
marginTop: [marginTop, 0],
729+
marginBottom: [marginBottom, 0],
730+
duration,
731+
onComplete: () => {
732+
this.setStyle({
733+
height: "",
734+
overflow: "",
735+
marginTop: "",
736+
marginBottom: "",
737+
}).hide();
738+
},
739+
});
740+
}
741+
672742
/**
673743
* Focus the element
674744
*/

0 commit comments

Comments
 (0)