+
+
+
OLD
+
red
+
orange
+
yellow
+
green
+
blue
+
purple
+
gray
+
+
+
CSS
+
2xl
+
xl
+
lg
+
md
+
sm
+
xs
+
xxs
+
+
+
JS
+
+ 2xl
+ xl
+ lg
+ md
+ sm
+ xs
+ xxs
+
+
+
+
+ );
+}
diff --git a/frontend/src/ts/components/layout/overlays/Overlays.tsx b/frontend/src/ts/components/layout/overlays/Overlays.tsx
index f7626e5fe8f3..88aa2b82649c 100644
--- a/frontend/src/ts/components/layout/overlays/Overlays.tsx
+++ b/frontend/src/ts/components/layout/overlays/Overlays.tsx
@@ -1,16 +1,30 @@
import { JSXElement } from "solid-js";
-import { TailwindMediaQueryDebugger } from "../../utils/TailwindMediaQueryDebugger";
+import { showModal } from "../../../stores/modals";
+import { ScrollToTop } from "../footer/ScrollToTop";
import { Banners } from "./Banners";
import { FpsCounter } from "./FpsCounter";
import { LoaderBar } from "./LoaderBar";
+import { MediaQueryDebugger } from "./MediaQueryDebugger";
export function Overlays(): JSXElement {
return (
<>
+
+
-
+
>
diff --git a/frontend/src/ts/components/utils/TailwindMediaQueryDebugger.tsx b/frontend/src/ts/components/utils/TailwindMediaQueryDebugger.tsx
deleted file mode 100644
index 53da2f95869e..000000000000
--- a/frontend/src/ts/components/utils/TailwindMediaQueryDebugger.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { JSXElement, Match, Show, Switch } from "solid-js";
-
-import { bp } from "../../signals/breakpoints";
-import { isDevEnvironment } from "../../utils/misc";
-
-export function TailwindMediaQueryDebugger(): JSXElement {
- return (
-
-
-
2xl
-
xl
-
lg
-
md
-
sm
-
xs
-
xxs
-
- /
-
- 2xl
- xl
- lg
- md
- sm
- xs
-
-
-
-
- );
-}
diff --git a/frontend/src/ts/controllers/chart-controller.ts b/frontend/src/ts/controllers/chart-controller.ts
index 3f08b8b96adb..af7bb8e6fb78 100644
--- a/frontend/src/ts/controllers/chart-controller.ts
+++ b/frontend/src/ts/controllers/chart-controller.ts
@@ -1360,6 +1360,7 @@ ConfigEvent.subscribe(({ key, newValue }) => {
updateAccuracy();
updateAverage10();
updateAverage100();
+ accountHistory.update();
}
if (key === "fontFamily") setDefaultFontFamily(newValue);
});
diff --git a/frontend/src/ts/elements/account-settings/ape-key-table.ts b/frontend/src/ts/elements/account-settings/ape-key-table.ts
index b39c1cbd0bbc..2e57f3085f81 100644
--- a/frontend/src/ts/elements/account-settings/ape-key-table.ts
+++ b/frontend/src/ts/elements/account-settings/ape-key-table.ts
@@ -5,6 +5,8 @@ import { ApeKey, ApeKeys } from "@monkeytype/schemas/ape-keys";
import { format } from "date-fns/format";
import { SimpleModal, TextArea } from "../../utils/simple-modal";
import { isAuthenticated } from "../../firebase";
+import { qs, qsr } from "../../utils/dom";
+
const editApeKey = new SimpleModal({
id: "editApeKey",
title: "Edit Ape key",
@@ -104,11 +106,16 @@ const viewApeKey = new SimpleModal({
},
beforeShowFn: (_thisPopup): void => {
_thisPopup.canClose = false;
- $("#simpleModal textarea").css("height", "110px");
- $("#simpleModal .submitButton").addClass("hidden");
+
+ const modalEl = _thisPopup.modal.getModal();
+
+ modalEl.qs("textarea")?.setStyle({
+ height: "110px",
+ });
+ modalEl.qs(".submitButton")?.addClass("hidden");
setTimeout(() => {
_thisPopup.canClose = true;
- $("#simpleModal .submitButton").removeClass("hidden");
+ modalEl.qs(".submitButton")?.removeClass("hidden");
}, 5000);
},
});
@@ -160,7 +167,7 @@ const generateApeKey = new SimpleModal({
let apeKeys: ApeKeys | null = {};
-const element = $("#pageAccountSettings .tab[data-tab='apeKeys']");
+const element = qsr("#pageAccountSettings .tab[data-tab='apeKeys']");
async function getData(): Promise
{
if (!isAuthenticated()) return false;
@@ -186,10 +193,10 @@ async function getData(): Promise {
}
function showLoaderRow(): void {
- const table = element.find("table tbody");
+ const table = element.qs("table tbody");
- table.empty();
- table.append(
+ table?.empty();
+ table?.appendHtml(
" |
",
);
}
@@ -197,18 +204,18 @@ function showLoaderRow(): void {
function refreshList(): void {
const data = apeKeys;
if (data === undefined || data === null) return;
- const table = element.find("table tbody");
- table.empty();
+ const table = element.qs("table tbody");
+ table?.empty();
const apeKeyIds = Object.keys(data);
if (apeKeyIds.length === 0) {
- table.append(
+ table?.appendHtml(
"| No keys found |
",
);
return;
}
apeKeyIds.forEach((apeKeyId) => {
const key = data[apeKeyId] as ApeKey;
- table.append(`
+ table?.appendHtml(`
|
|
`);
});
- for (const tr of table.find("tr")) {
+ for (const tr of table?.qsa("tr") ?? []) {
const keyid = tr.getAttribute("keyid") as string;
- tr.querySelector("button.toggleActive")?.addEventListener("click", (e) => {
+ tr.qs("button.toggleActive")?.on("click", (e) => {
void toggleActiveKey(keyid);
});
- tr.querySelector("button.deleteButton")?.addEventListener("click", (e) => {
+ tr.qs("button.deleteButton")?.on("click", (e) => {
deleteApeKeyModal.show([keyid], {});
});
- tr.querySelector("button.editButton")?.addEventListener("click", (e) => {
+ tr.qs("button.editButton")?.on("click", (e) => {
editApeKey.show([keyid], {});
});
}
@@ -282,9 +289,9 @@ let lostAccess = false;
export async function update(onApeKeyChangee?: () => void): Promise {
if (lostAccess) {
- $(".pageAccountSettings .tab[data-tab='apeKeys'] table").remove();
- $(".pageAccountSettings .section.apeKeys .buttons").remove();
- $(".pageAccountSettings .section.apeKeys .lostAccess").removeClass(
+ qs(".pageAccountSettings .tab[data-tab='apeKeys'] table")?.remove();
+ qs(".pageAccountSettings .section.apeKeys .buttons")?.remove();
+ qs(".pageAccountSettings .section.apeKeys .lostAccess")?.removeClass(
"hidden",
);
return;
@@ -294,6 +301,6 @@ export async function update(onApeKeyChangee?: () => void): Promise {
refreshList();
}
-$(".pageAccountSettings").on("click", "#generateNewApeKey", () => {
+qs(".pageAccountSettings")?.onChild("click", "#generateNewApeKey", () => {
generateApeKey.show([], {});
});
diff --git a/frontend/src/ts/elements/account-settings/blocked-user-table.ts b/frontend/src/ts/elements/account-settings/blocked-user-table.ts
index 9bb28e169905..819ed1b77748 100644
--- a/frontend/src/ts/elements/account-settings/blocked-user-table.ts
+++ b/frontend/src/ts/elements/account-settings/blocked-user-table.ts
@@ -6,9 +6,10 @@ import { isAuthenticated } from "../../firebase";
import { getReceiverUid } from "../../pages/friends";
import * as DB from "../../db";
import { updateFriendRequestsIndicator } from "../account-button";
+import { qsr } from "../../utils/dom";
let blockedUsers: Connection[] = [];
-const element = $("#pageAccountSettings .tab[data-tab='blockedUsers']");
+const element = qsr("#pageAccountSettings .tab[data-tab='blockedUsers']");
async function getData(): Promise {
showLoaderRow();
@@ -37,19 +38,19 @@ export async function update(): Promise {
}
function showLoaderRow(): void {
- const table = element.find("table tbody");
+ const table = element.qs("table tbody");
- table.empty();
- table.append(
+ table?.empty();
+ table?.appendHtml(
" |
",
);
}
function refreshList(): void {
- const table = element.find("table tbody");
- table.empty();
+ const table = element.qs("table tbody");
+ table?.empty();
if (blockedUsers.length === 0) {
- table.append(
+ table?.appendHtml(
"| No blocked users |
",
);
return;
@@ -69,12 +70,12 @@ function refreshList(): void {
`,
);
- table.append(content.join());
+ table?.appendHtml(content.join());
}
-element.on("click", "table button.delete", async (e) => {
- const row = (e.target as HTMLElement).closest("tr") as HTMLElement;
- const id = row.dataset["id"];
+element.onChild("click", "table button.delete", async (e) => {
+ const row = (e.childTarget as HTMLElement).closest("tr") as HTMLElement;
+ const id = row?.dataset["id"];
if (id === undefined) {
throw new Error("Cannot find id of target.");
diff --git a/frontend/src/ts/elements/account/result-filters.ts b/frontend/src/ts/elements/account/result-filters.ts
index 8330eec32390..fffecacb1e48 100644
--- a/frontend/src/ts/elements/account/result-filters.ts
+++ b/frontend/src/ts/elements/account/result-filters.ts
@@ -20,6 +20,7 @@ import { Snapshot } from "../../constants/default-snapshot";
import { LanguageList } from "../../constants/languages";
import * as AuthEvent from "../../observables/auth-event";
import { sanitize } from "../../utils/sanitize";
+import { qs, qsa } from "../../utils/dom";
export function mergeWithDefaultFilters(
filters: Partial,
@@ -159,12 +160,12 @@ export async function setFilterPreset(id: string): Promise {
}
// make all filter preset butons inactive
- $(
+ qsa(
`.pageAccount .group.presetFilterButtons .filterBtns .filterPresets .select-filter-preset`,
).removeClass("active");
// make current filter presest button active
- $(
+ qsa(
`.pageAccount .group.presetFilterButtons .filterBtns .filterPresets .select-filter-preset[data-id=${id}]`,
).addClass("active");
}
@@ -233,7 +234,7 @@ async function deleteFilterPreset(id: string): Promise {
function deSelectFilterPreset(): void {
// make all filter preset buttons inactive
- $(
+ qsa(
".pageAccount .group.presetFilterButtons .filterBtns .filterPresets .select-filter-preset",
).removeClass("active");
}
@@ -323,29 +324,29 @@ export function updateActive(): void {
}
if (groupsUsingSelect.has(group)) {
- const option = $(
+ const option = qs(
`.pageAccount .group.filterButtons .filterGroup[group="${group}"] option[value="${filter}"]`,
);
if (filterValue === true) {
- option.prop("selected", true);
+ option?.setSelected(true);
} else {
- option.prop("selected", false);
+ option?.setSelected(false);
}
} else {
let buttonEl;
if (group === "date") {
- buttonEl = $(
+ buttonEl = qs(
`.pageAccount .group.topFilters .filterGroup[group="${group}"] button[filter="${filter}"]`,
);
} else {
- buttonEl = $(
+ buttonEl = qs(
`.pageAccount .group.filterButtons .filterGroup[group="${group}"] button[filter="${filter}"]`,
);
}
if (filterValue === true) {
- buttonEl.addClass("active");
+ buttonEl?.addClass("active");
} else {
- buttonEl.removeClass("active");
+ buttonEl?.removeClass("active");
}
}
}
@@ -358,12 +359,12 @@ export function updateActive(): void {
const newData = ss.store.getData();
- const allOption = $(
+ const allOption = qs(
`.pageAccount .group.filterButtons .filterGroup[group="${id}"] option[value="all"]`,
);
if (everythingSelected) {
- allOption.prop("selected", true);
+ allOption?.setSelected(true);
for (const data of newData) {
if ("value" in data) {
if (data.value === "all") data.selected = true;
@@ -373,7 +374,7 @@ export function updateActive(): void {
ss.store.setData(newData);
ss.render.renderValues();
} else {
- allOption.prop("selected", false);
+ allOption?.setSelected(false);
}
for (const data of newData) {
@@ -490,9 +491,7 @@ export function updateActive(): void {
//tags
chartString += addText("tags");
- setTimeout(() => {
- $(".pageAccount .group.chart .above").html(chartString);
- }, 0);
+ qs(".pageAccount .group.chart .above")?.setHtml(chartString);
}
function toggle(
@@ -523,52 +522,64 @@ function toggle(
}
}
-$(
- ".pageAccount .filterButtons .buttonsAndTitle .buttons, .pageAccount .group.topFilters .buttonsAndTitle.testDate .buttons",
-).on("click", "button", (e) => {
- const group = $(e.target)
- .parents(".buttons")
- .attr("group") as ResultFiltersGroup;
- const filter = $(e.target).attr("filter") as ResultFiltersGroupItem<
- typeof group
- >;
- if ($(e.target).hasClass("allFilters")) {
- Misc.typedKeys(getFilters()).forEach((group) => {
- // id and name field do not correspond to any ui elements, no need to update
- if (group === "_id" || group === "name") {
- return;
- }
+for (const el of qsa(`
+ .pageAccount .filterButtons .buttonsAndTitle .buttons,
+ .pageAccount .group.topFilters .buttonsAndTitle.testDate .buttons
+ `)) {
+ el.onChild("click", "button", (e) => {
+ const childTarget = e.childTarget as HTMLElement;
+ const group = (e.target as HTMLElement).parentElement?.getAttribute(
+ "group",
+ ) as ResultFiltersGroup | null;
+ if (group === null) {
+ throw new Error("Cannot find group of target.");
+ }
- setAllFilters(group, true);
- });
- setAllFilters("date", false);
- filters.date.all = true;
- } else if ($(e.target).hasClass("noFilters")) {
- Misc.typedKeys(getFilters()).forEach((group) => {
- // id and name field do not correspond to any ui elements, no need to update
- if (group === "_id" || group === "name") {
- return;
- }
+ const filter = childTarget.getAttribute("filter") as ResultFiltersGroupItem<
+ typeof group
+ > | null;
+ if (filter === null) {
+ throw new Error("Cannot find filter of target.");
+ }
+
+ if (childTarget.classList.contains("allFilters")) {
+ Misc.typedKeys(getFilters()).forEach((group) => {
+ // id and name field do not correspond to any ui elements, no need to update
+ if (group === "_id" || group === "name") {
+ return;
+ }
- if (group !== "date") {
+ setAllFilters(group, true);
+ });
+ setAllFilters("date", false);
+ filters.date.all = true;
+ } else if (childTarget.classList.contains("noFilters")) {
+ Misc.typedKeys(getFilters()).forEach((group) => {
+ // id and name field do not correspond to any ui elements, no need to update
+ if (group === "_id" || group === "name") {
+ return;
+ }
+
+ if (group !== "date") {
+ setAllFilters(group, false);
+ }
+ });
+ } else if ((e.target as HTMLElement).tagName === "BUTTON") {
+ if (e.shiftKey) {
setAllFilters(group, false);
+ filters[group][filter] =
+ true as ResultFilters[typeof group][typeof filter];
+ } else {
+ toggle(group, filter);
+ // filters[group][filter] = !filters[group][filter];
}
- });
- } else if ($(e.target).is("button")) {
- if (e.shiftKey) {
- setAllFilters(group, false);
- filters[group][filter] =
- true as ResultFilters[typeof group][typeof filter];
- } else {
- toggle(group, filter);
- // filters[group][filter] = !filters[group][filter];
}
- }
- updateActive();
- save();
-});
+ updateActive();
+ save();
+ });
+}
-$(".pageAccount .topFilters button.allFilters").on("click", () => {
+qs(".pageAccount .topFilters button.allFilters")?.on("click", () => {
// user is changing the filters -> current filter is no longer a filter preset
deSelectFilterPreset();
@@ -588,7 +599,7 @@ $(".pageAccount .topFilters button.allFilters").on("click", () => {
save();
});
-$(".pageAccount .topFilters button.currentConfigFilter").on("click", () => {
+qs(".pageAccount .topFilters button.currentConfigFilter")?.on("click", () => {
// user is changing the filters -> current filter is no longer a filter preset
deSelectFilterPreset();
@@ -673,11 +684,19 @@ $(".pageAccount .topFilters button.currentConfigFilter").on("click", () => {
save();
});
-$(".pageAccount .topFilters button.toggleAdvancedFilters").on("click", () => {
- $(".pageAccount .filterButtons").slideToggle(250);
- $(".pageAccount .topFilters button.toggleAdvancedFilters").toggleClass(
- "active",
+qs(".pageAccount .topFilters button.toggleAdvancedFilters")?.on("click", () => {
+ const buttons = qs(".pageAccount .filterButtons");
+ const advancedFiltersButton = qs(
+ ".pageAccount .topFilters button.toggleAdvancedFilters",
);
+
+ if (buttons?.isVisible()) {
+ void buttons.slideUp(250);
+ advancedFiltersButton?.removeClass("active");
+ } else {
+ void buttons?.slideDown(250);
+ advancedFiltersButton?.addClass("active");
+ }
});
function adjustScrollposition(
@@ -873,19 +892,19 @@ export async function appendDropdowns(
}
function tagDropdownUpdate(snapshot: Snapshot): void {
- const tagsSection = $(
+ const tagsSection = qs(
".pageAccount .content .filterButtons .buttonsAndTitle.tags",
);
if (snapshot.tags.length === 0) {
- tagsSection.addClass("hidden");
+ tagsSection?.addClass("hidden");
if (groupSelects["tags"]) {
groupSelects["tags"].destroy();
delete groupSelects["tags"];
}
setFilter("tags", "none", true);
} else {
- tagsSection.removeClass("hidden");
+ tagsSection?.removeClass("hidden");
updateTagsDropdownOptions();
@@ -922,13 +941,13 @@ function tagDropdownUpdate(snapshot: Snapshot): void {
}
}
-$(".group.presetFilterButtons .filterBtns").on(
- "click",
- ".filterPresets .delete-filter-preset",
- (e) => {
- void deleteFilterPreset($(e.currentTarget).data("id") as string);
- },
-);
+for (const el of qsa(".group.presetFilterButtons .filterBtns")) {
+ el.onChild("click", ".filterPresets .delete-filter-preset", (e) => {
+ void deleteFilterPreset(
+ (e.childTarget as HTMLElement).dataset["id"] as string,
+ );
+ });
+}
function verifyResultFiltersStructure(filterIn: ResultFilters): ResultFilters {
const filter = mergeWithDefaultFilters(
diff --git a/frontend/src/ts/elements/profile.ts b/frontend/src/ts/elements/profile.ts
index f56bcfe29eb4..d45378c2a0c0 100644
--- a/frontend/src/ts/elements/profile.ts
+++ b/frontend/src/ts/elements/profile.ts
@@ -20,6 +20,7 @@ import { getAvatarElement } from "../utils/discord-avatar";
import { formatXp } from "../utils/levels";
import { formatTopPercentage } from "../utils/misc";
import { get as getServerConfiguration } from "../ape/server-configuration";
+import { qs } from "../utils/dom";
type ProfileViewPaths = "profile" | "account";
type UserProfileOrSnapshot = UserProfile | Snapshot;
@@ -31,11 +32,11 @@ export async function update(
profile: UserProfileOrSnapshot,
): Promise {
const elementClass = where.charAt(0).toUpperCase() + where.slice(1);
- const profileElement = $(`.page${elementClass} .profile`);
- const details = $(`.page${elementClass} .profile .details`);
+ const profileElement = qs(`.page${elementClass} .profile`);
+ const details = qs(`.page${elementClass} .profile .details`);
- profileElement.attr("uid", profile.uid ?? "");
- profileElement.attr("name", profile.name ?? "");
+ profileElement?.setAttribute("uid", profile.uid ?? "");
+ profileElement?.setAttribute("name", profile.name ?? "");
// ============================================================================
// DO FREAKING NOT USE .HTML OR .APPEND HERE - USER INPUT!!!!!!
@@ -52,8 +53,8 @@ export async function update(
return;
}
- const avatar = details.find(".avatarAndName .avatar");
- avatar.replaceWith(getAvatarElement(profile, { size: 256 }));
+ const avatar = details?.qs(".avatarAndName .avatar");
+ avatar?.replaceWith(getAvatarElement(profile, { size: 256 }));
if (profile.inventory?.badges && !banned) {
let mainHtml = "";
@@ -67,27 +68,27 @@ export async function update(
}
}
- details.find(".badges").empty().append(mainHtml);
- details.find(".allBadges").empty().append(restHtml);
+ details?.qs(".badges")?.empty().appendHtml(mainHtml);
+ details?.qs(".allBadges")?.empty().appendHtml(restHtml);
}
- details.find(".name").text(profile.name);
+ details?.qs(".name")?.setText(profile.name);
details
- .find(".userFlags")
- .html(
+ ?.qs(".userFlags")
+ ?.setHtml(
getHtmlByUserFlags({ ...profile, isFriend: DB.isFriend(profile.uid) }),
);
if (profile.lbOptOut === true) {
if (where === "profile") {
profileElement
- .find(".lbOptOutReminder")
- .removeClass("hidden")
- .text(
+ ?.qs(".lbOptOutReminder")
+ ?.removeClass("hidden")
+ ?.setText(
"Note: This account has opted out of the leaderboards, meaning their results aren't verified by the anticheat system and may not be legitimate.",
);
} else {
- profileElement.find(".lbOptOutReminder").addClass("hidden");
+ profileElement?.qs(".lbOptOutReminder")?.addClass("hidden");
}
}
@@ -100,14 +101,17 @@ export async function update(
const creationDate = new Date(profile.addedAt);
const diffDays = differenceInDays(new Date(), creationDate);
const balloonText = `${diffDays} day${diffDays !== 1 ? "s" : ""} ago`;
- details.find(".joined").text(joinedText).attr("aria-label", balloonText);
+ details
+ ?.qs(".joined")
+ ?.setText(joinedText)
+ .setAttribute("aria-label", balloonText);
let hoverText = "";
if (profile.streak && profile?.streak > 1) {
details
- .find(".streak")
- .text(
+ ?.qs(".streak")
+ ?.setText(
`Current streak: ${profile.streak} ${
profile.streak === 1 ? "day" : "days"
}`,
@@ -116,7 +120,7 @@ export async function update(
profile.maxStreak === 1 ? "day" : "days"
}`;
} else {
- details.find(".streak").text("");
+ details?.qs(".streak")?.setText("");
hoverText = "";
}
@@ -189,29 +193,29 @@ export async function update(
}
details
- .find(".streak")
- .attr("aria-label", hoverText)
- .attr("data-balloon-break", "");
+ ?.qs(".streak")
+ ?.setAttribute("aria-label", hoverText)
+ ?.setAttribute("data-balloon-break", "");
const { completedPercentage, restartRatio } = Misc.formatTypingStatsRatio(
profile.typingStats,
);
- const typingStatsEl = details.find(".typingStats");
+ const typingStatsEl = details?.qs(".typingStats");
typingStatsEl
- .find(".started .value")
- .text(profile.typingStats?.startedTests ?? 0);
+ ?.qs(".started .value")
+ ?.setText(`${profile.typingStats?.startedTests ?? 0}`);
typingStatsEl
- .find(".completed .value")
- .text(profile.typingStats?.completedTests ?? 0)
- .attr("data-balloon-pos", "up")
- .attr(
+ ?.qs(".completed .value")
+ ?.setText(`${profile.typingStats?.completedTests ?? 0}`)
+ .setAttribute("data-balloon-pos", "up")
+ .setAttribute(
"aria-label",
`${completedPercentage}% (${restartRatio} restarts per completed test)`,
);
typingStatsEl
- .find(".timeTyping .value")
- .text(
+ ?.qs(".timeTyping .value")
+ ?.setText(
secondsToString(
Math.round(profile.typingStats?.timeTyping ?? 0),
true,
@@ -225,10 +229,10 @@ export async function update(
if (!banned) {
bio = !!(profile.details?.bio ?? "");
- details.find(".bio .value").text(profile.details?.bio ?? "");
+ details?.qs(".bio .value")?.setText(profile.details?.bio ?? "");
keyboard = !!(profile.details?.keyboard ?? "");
- details.find(".keyboard .value").text(profile.details?.keyboard ?? "");
+ details?.qs(".keyboard .value")?.setText(profile.details?.keyboard ?? "");
if (
(profile.details?.socialProfiles?.github !== undefined &&
@@ -239,12 +243,12 @@ export async function update(
profile.details?.socialProfiles?.website !== "")
) {
socials = true;
- const socialsEl = details.find(".socials .value");
- socialsEl.empty();
+ const socialsEl = details?.qs(".socials .value");
+ socialsEl?.empty();
const git = profile.details?.socialProfiles.github ?? "";
if (git) {
- socialsEl.append(
+ socialsEl?.appendHtml(
` {
}
});
-$(window).on("resize", () => {
+window.addEventListener("resize", () => {
throttledEvent();
});
diff --git a/frontend/src/ts/elements/result-batches.ts b/frontend/src/ts/elements/result-batches.ts
index 500c63545abe..d72645da9991 100644
--- a/frontend/src/ts/elements/result-batches.ts
+++ b/frontend/src/ts/elements/result-batches.ts
@@ -3,13 +3,14 @@ import * as ServerConfiguration from "../ape/server-configuration";
import { blendTwoHexColors } from "../utils/colors";
import { mapRange } from "@monkeytype/util/numbers";
import { getTheme } from "../signals/theme";
+import { qs } from "../utils/dom";
export function hide(): void {
- $(".pageAccount .resultBatches").addClass("hidden");
+ qs(".pageAccount .resultBatches")?.addClass("hidden");
}
export function show(): void {
- $(".pageAccount .resultBatches").removeClass("hidden");
+ qs(".pageAccount .resultBatches")?.removeClass("hidden");
}
export async function update(): Promise {
@@ -38,27 +39,29 @@ export async function update(): Promise {
: limits.regularUser;
const percentageLimit = Math.round((results?.length / currentLimit) * 100);
- const barsWrapper = $(".pageAccount .resultBatches .bars");
+ const barsWrapper = qs(".pageAccount .resultBatches .bars");
const bars = {
downloaded: {
- fill: barsWrapper.find(".downloaded .fill"),
- rightText: barsWrapper.find(".downloaded.rightText"),
+ fill: barsWrapper?.qs(".downloaded .fill"),
+ rightText: barsWrapper?.qs(".downloaded.rightText"),
},
limit: {
- fill: barsWrapper.find(".limit .fill"),
- rightText: barsWrapper.find(".limit.rightText"),
+ fill: barsWrapper?.qs(".limit .fill"),
+ rightText: barsWrapper?.qs(".limit.rightText"),
},
};
- bars.downloaded.fill.css("width", Math.min(percentageDownloaded, 100) + "%");
- bars.downloaded.rightText.text(
+ bars.downloaded.fill?.setStyle({
+ width: Math.min(percentageDownloaded, 100) + "%",
+ });
+ bars.downloaded.rightText?.setText(
`${results?.length} / ${completedTests} (${percentageDownloaded}%)`,
);
const colors = getTheme();
- bars.limit.fill.css({
+ bars.limit.fill?.setStyle({
width: Math.min(percentageLimit, 100) + "%",
background: blendTwoHexColors(
colors.sub,
@@ -66,12 +69,12 @@ export async function update(): Promise {
mapRange(percentageLimit, 50, 100, 0, 1),
),
});
- bars.limit.rightText.text(
+ bars.limit.rightText?.setText(
`${results?.length} / ${currentLimit} (${percentageLimit}%)`,
);
- const text = $(".pageAccount .resultBatches > .text");
- text.text("");
+ const text = qs(".pageAccount .resultBatches > .text");
+ text?.setText("");
if (results.length >= completedTests) {
disableButton();
@@ -91,15 +94,15 @@ export async function update(): Promise {
}
export function disableButton(): void {
- $(".pageAccount .resultBatches button").prop("disabled", true);
+ qs(".pageAccount .resultBatches button")?.disable();
}
export function enableButton(): void {
- $(".pageAccount .resultBatches button").prop("disabled", false);
+ qs(".pageAccount .resultBatches button")?.enable();
}
export function updateButtonText(text: string): void {
- $(".pageAccount .resultBatches button").text(text);
+ qs(".pageAccount .resultBatches button")?.setText(text);
}
export function showOrHideIfNeeded(): void {
diff --git a/frontend/src/ts/elements/result-word-highlight.ts b/frontend/src/ts/elements/result-word-highlight.ts
index fb2905feafe3..8869b71f4135 100644
--- a/frontend/src/ts/elements/result-word-highlight.ts
+++ b/frontend/src/ts/elements/result-word-highlight.ts
@@ -5,6 +5,7 @@
import * as Misc from "../utils/misc";
import * as TestState from "../test/test-state";
+import { qsr } from "../utils/dom";
const PADDING_X = 16;
const PADDING_Y = 12;
@@ -22,8 +23,8 @@ type Line = {
// Array of Line objects
let lines: Line[] = [];
-// JQuery collection of all word elements
-let wordEls: JQuery;
+// collection of all word elements
+let wordEls: HTMLElement[];
// Dictionary mapping word indices to line indices
let wordIndexToLineIndexDict: Record = {};
@@ -196,9 +197,9 @@ async function init(): Promise {
);
}
- RWH_el = $("#resultWordsHistory")[0] as HTMLElement;
+ RWH_el = qsr("#resultWordsHistory").native;
RWH_rect = RWH_el.getBoundingClientRect();
- wordEls = $(RWH_el).find(".words .word[input]");
+ wordEls = qsr("#resultWordsHistory").qsa(".words .word[input]").native;
// remove non-input words
if (wordEls.length === 0) {
diff --git a/frontend/src/ts/elements/settings/account-settings-notice.ts b/frontend/src/ts/elements/settings/account-settings-notice.ts
index e0ee323f5a68..7b54c68708dc 100644
--- a/frontend/src/ts/elements/settings/account-settings-notice.ts
+++ b/frontend/src/ts/elements/settings/account-settings-notice.ts
@@ -1,6 +1,7 @@
import { z } from "zod";
import { LocalStorageWithSchema } from "../../utils/local-storage-with-schema";
import { navigate } from "../../controllers/route-controller";
+import { qs, qsa } from "../../utils/dom";
const ls = new LocalStorageWithSchema({
key: "accountSettingsMessageDismissed",
@@ -9,11 +10,11 @@ const ls = new LocalStorageWithSchema({
});
if (ls.get()) {
- $(".pageSettings .accountSettingsNotice").remove();
+ qsa(".pageSettings .accountSettingsNotice")?.remove();
}
-$(".pageSettings .accountSettingsNotice .dismissAndGo").on("click", () => {
+qs(".pageSettings .accountSettingsNotice .dismissAndGo")?.on("click", () => {
ls.set(true);
void navigate("/account-settings");
- $(".pageSettings .accountSettingsNotice").remove();
+ qsa(".pageSettings .accountSettingsNotice")?.remove();
});
diff --git a/frontend/src/ts/elements/settings/theme-picker.ts b/frontend/src/ts/elements/settings/theme-picker.ts
index 8d779f3b62e3..125f967e7e95 100644
--- a/frontend/src/ts/elements/settings/theme-picker.ts
+++ b/frontend/src/ts/elements/settings/theme-picker.ts
@@ -140,37 +140,37 @@ export async function fillPresetButtons(): Promise {
export async function fillCustomButtons(): Promise {
// Update custom theme buttons
- const customThemesEl = $(
+ const customThemesEl = qs(
".pageSettings .section.themes .allCustomThemes.buttons",
- ).empty();
- const addButton = $(".pageSettings .section.themes .addCustomThemeButton");
- const saveButton = $(
+ )?.empty();
+ const addButton = qs(".pageSettings .section.themes .addCustomThemeButton");
+ const saveButton = qs(
".pageSettings .section.themes .tabContent.customTheme #saveCustomThemeButton",
);
if (!isAuthenticated()) {
- saveButton.text("save");
- addButton.addClass("hidden");
- customThemesEl.css("margin-bottom", "0");
+ saveButton?.setText("save");
+ addButton?.addClass("hidden");
+ customThemesEl?.setStyle({ marginBottom: "0" });
return;
}
- saveButton.text("save as new");
- addButton.removeClass("hidden");
+ saveButton?.setText("save as new");
+ addButton?.removeClass("hidden");
const customThemes = DB.getSnapshot()?.customThemes ?? [];
if (customThemes.length === 0) {
- customThemesEl.css("margin-bottom", "0");
+ customThemesEl?.setStyle({ marginBottom: "0" });
} else {
- customThemesEl.css("margin-bottom", "1rem");
+ customThemesEl?.setStyle({ marginBottom: "1rem" });
}
for (const customTheme of customThemes) {
const bgColor = customTheme.colors[0];
const mainColor = customTheme.colors[1];
- customThemesEl.append(
+ customThemesEl?.appendHtml(
``;
- bannerCenter?.appendChild(warning);
+ addBanner({
+ level: "error",
+ icon: "fas fa-exclamation-triangle",
+ text: "Warning: qs/qsr detected selector(s) matching multiple elements, check console for details.",
+ });
}
}