Add eslint a11y plugin

This commit is contained in:
Georges-Antoine Assi
2025-09-25 09:10:29 -04:00
parent e473cbe698
commit 237d6c1eef
28 changed files with 132 additions and 101 deletions
+2
View File
@@ -1,5 +1,6 @@
import eslint from "@eslint/js";
import vue from "eslint-plugin-vue";
import vuea11y from "eslint-plugin-vuejs-accessibility";
import globals from "globals";
import tseslint from "typescript-eslint";
@@ -7,6 +8,7 @@ export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
...vue.configs["flat/recommended"],
...vuea11y.configs["flat/recommended"],
{
ignores: [
"logs",
+33
View File
@@ -43,6 +43,7 @@
"@vue/tsconfig": "^0.7.0",
"eslint": "^9.34.0",
"eslint-plugin-vue": "^9.33.0",
"eslint-plugin-vuejs-accessibility": "^2.4.1",
"globals": "^16.0.0",
"openapi-typescript-codegen": "^0.29.0",
"typescript": "^5.7.3",
@@ -4223,6 +4224,15 @@
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/aria-query": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
"dev": true,
"engines": {
"node": ">= 0.4"
}
},
"node_modules/array-buffer-byte-length": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
@@ -5285,6 +5295,29 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint-plugin-vuejs-accessibility": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-vuejs-accessibility/-/eslint-plugin-vuejs-accessibility-2.4.1.tgz",
"integrity": "sha512-ZRZhPdslplZXSF71MtSG+zXYRAT5KiHR4JVuo/DERQf9noAkDvi5W418VOE1qllmJd7wTenndxi1q8XeDMxdHw==",
"dev": true,
"dependencies": {
"aria-query": "^5.3.0",
"emoji-regex": "^10.0.0",
"vue-eslint-parser": "^9.0.1"
},
"engines": {
"node": ">=16.0.0"
},
"peerDependencies": {
"eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0"
}
},
"node_modules/eslint-plugin-vuejs-accessibility/node_modules/emoji-regex": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz",
"integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==",
"dev": true
},
"node_modules/eslint-scope": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+1
View File
@@ -62,6 +62,7 @@
"@vue/tsconfig": "^0.7.0",
"eslint": "^9.34.0",
"eslint-plugin-vue": "^9.33.0",
"eslint-plugin-vuejs-accessibility": "^2.4.1",
"globals": "^16.0.0",
"openapi-typescript-codegen": "^0.29.0",
"typescript": "^5.7.3",
@@ -19,6 +19,7 @@ const romInfo = ref([
{ label: "SHA-1", value: props.rom.sha1_hash },
{ label: "MD5", value: props.rom.md5_hash },
{ label: "CRC", value: props.rom.crc_hash },
{ label: "Revision", value: props.rom.revision },
]);
async function toggleMainSibling() {
@@ -160,7 +160,8 @@ onMounted(() => {
<a
:href="`https://retroachievements.org/achievement/${achievement.ra_id}`"
target="_blank"
style="height: 100%; width: 100%"
class="h-100 w-100"
:aria-label="achievement.badge_id || 'Achievement badge'"
>
<v-img
:src="
@@ -168,6 +169,7 @@ onMounted(() => {
? (achievement.badge_path ?? '')
: (achievement.badge_path_lock ?? '')
"
:alt="achievement.badge_id || 'Achievement badge'"
/>
</a>
</v-avatar>
-12
View File
@@ -106,18 +106,6 @@ const hashMatches = computed(() => {
</v-col>
</v-row>
<v-row
v-if="smAndDown && rom.revision"
class="text-white text-shadow mt-2 text-center"
no-gutters
>
<v-col>
<v-chip v-if="rom.revision" size="small" class="ml-1">
Revision {{ rom.revision }}
</v-chip>
</v-col>
</v-row>
<v-row
v-if="rom.is_identified"
class="text-white text-shadow mt-2"
@@ -53,7 +53,6 @@ watch(
v-model="searchTerm"
density="default"
clearable
autofocus
hide-details
rounded="0"
:label="t('common.search')"
@@ -85,6 +85,7 @@ watch(documentY, () => {
title-on-hover
:enable3-d-tilt="enable3DEffect"
@hover="onHover"
@focus="onHover"
/>
</v-col>
</v-row>
@@ -81,6 +81,7 @@ function onClosedMenu() {
show-chips
:enable3-d-tilt="enable3DEffect"
@hover="onHover"
@focus="onHover"
@openedmenu="onOpenedMenu"
@closedmenu="onClosedMenu"
/>
@@ -63,6 +63,7 @@ function onHover(emitData: { isHovering: boolean; id: number }) {
:platform="platform"
:enable3-d-tilt="enable3DEffect"
@hover="onHover"
@focus="onHover"
/>
</v-col>
</v-row>
@@ -85,6 +85,7 @@ function onClosedMenu() {
show-action-bar
:enable3-d-tilt="enable3DEffect"
@hover="onHover"
@focus="onHover"
@openedmenu="onOpenedMenu"
@closedmenu="onClosedMenu"
/>
@@ -162,13 +162,13 @@ function closeDialog() {
"
>
<v-fade-transition>
<div
<v-btn
v-if="isHovering"
class="d-flex translucent cursor-pointer h-100 align-center justify-center text-h4"
class="d-flex translucent cursor-pointer h-100 w-100 align-center justify-center text-h4"
@click="triggerFileInput"
>
<v-icon>mdi-pencil</v-icon>
</div>
</v-btn>
</v-fade-transition>
<v-file-input
id="file-input"
@@ -199,6 +199,11 @@ onBeforeUnmount(() => {
emit('hover', { isHovering: false, id: collection.id });
}
"
@blur="
() => {
emit('hover', { isHovering: false, id: collection.id });
}
"
>
<v-row v-if="showTitle" class="pa-1 justify-center bg-surface">
<div
@@ -237,6 +237,11 @@ onBeforeUnmount(() => {
emit('hover', { isHovering: false, id: rom.id });
}
"
@blur="
() => {
emit('hover', { isHovering: false, id: rom.id });
}
"
>
<v-card-text class="pa-0">
<v-hover v-slot="{ isHovering, props: imgProps }" open-delay="800">
@@ -536,7 +536,6 @@ onBeforeUnmount(() => {
<v-text-field
id="search-text-field"
v-model="searchText"
autofocus
class="bg-toplayer"
:disabled="searching"
:label="t('common.search')"
@@ -4,7 +4,6 @@ import { storeToRefs } from "pinia";
import { computed } from "vue";
import { useRouter } from "vue-router";
import AdminMenu from "@/components/common/Game/AdminMenu.vue";
import FavBtn from "@/components/common/Game/FavBtn.vue";
import PlayBtn from "@/components/common/Game/PlayBtn.vue";
import RAvatarRom from "@/components/common/Game/RAvatar.vue";
import MissingFromFSIcon from "@/components/common/MissingFromFSIcon.vue";
@@ -282,7 +281,7 @@ function updateOptions({ sortBy }: { sortBy: SortBy }) {
:text="`Missing from filesystem: ${item.fs_path}/${item.fs_name}`"
class="mr-1 px-1 item-chip"
chip
chipSize="x-small"
chip-size="x-small"
/>
</template>
</v-list-item>
@@ -75,7 +75,7 @@ function collapse() {
>
<template #prepend>
<v-row no-gutters class="my-2 justify-center">
<HomeBtn aria-label="Home" tabindex="1" />
<HomeBtn aria-label="Home" />
</v-row>
</template>
@@ -84,7 +84,6 @@ function collapse() {
<v-btn
id="collapseBtn"
aria-label="Collapse main navbar"
tabindex="2"
size="small"
density="comfortable"
variant="flat"
@@ -101,41 +100,11 @@ function collapse() {
</v-icon>
</v-btn>
</v-row>
<SearchBtn
:with-tag="!mainBarCollapsed"
rounded
class="mt-4"
block
tabindex="3"
/>
<PlatformsBtn
:with-tag="!mainBarCollapsed"
rounded
class="mt-2"
block
tabindex="4"
/>
<CollectionsBtn
:with-tag="!mainBarCollapsed"
rounded
class="mt-2"
block
tabindex="5"
/>
<ScanBtn
:with-tag="!mainBarCollapsed"
rounded
class="mt-2"
block
tabindex="7"
/>
<ConsoleModeBtn
:with-tag="!mainBarCollapsed"
rounded
class="mt-2"
block
tabindex="8"
/>
<SearchBtn :with-tag="!mainBarCollapsed" rounded class="mt-4" block />
<PlatformsBtn :with-tag="!mainBarCollapsed" rounded class="mt-2" block />
<CollectionsBtn :with-tag="!mainBarCollapsed" rounded class="mt-2" block />
<ScanBtn :with-tag="!mainBarCollapsed" rounded class="mt-2" block />
<ConsoleModeBtn :with-tag="!mainBarCollapsed" rounded class="mt-2" block />
<template #append>
<UploadBtn
@@ -143,10 +112,9 @@ function collapse() {
rounded
class="mt-2 mb-6"
block
tabindex="9"
/>
<v-row no-gutters class="my-2 justify-center">
<UserBtn tabindex="10" aria-label="Settings menu" />
<UserBtn aria-label="Settings menu" />
</v-row>
</template>
</v-navigation-drawer>
@@ -38,6 +38,13 @@ async function fetchLatestVersion() {
document.removeEventListener("network-quiesced", fetchLatestVersion);
}
function openNewVersion() {
window.open(
`https://github.com/rommapp/romm/releases/tag/${GITHUB_VERSION.value}`,
"_blank",
);
}
onMounted(async () => {
document.addEventListener("network-quiesced", fetchLatestVersion);
});
@@ -53,23 +60,33 @@ onMounted(async () => {
!latestVersionDismissed
"
class="pa-1 border-selected mx-auto"
max-width="250"
max-width="fit-content"
>
<v-card-text class="text-center py-2 px-4">
<span class="text-white text-shadow">New version available</span>
<span class="text-primary ml-1">v{{ GITHUB_VERSION }}</span>
<v-row class="mt-1" no-gutters>
<v-col>
<span class="pointer text-grey" @click="dismissVersionBanner"
>Dismiss</span
><span class="ml-4"
><a
target="_blank"
:href="`https://github.com/rommapp/romm/releases/tag/${GITHUB_VERSION}`"
>See what's new!</a
></span
<span class="text-white text-body-1">New version available:</span>
<span class="text-primary ml-1 text-body-1">{{
GITHUB_VERSION
}}</span>
<v-row class="mt-2 flex justify-center" no-gutters>
<v-btn-group>
<v-btn
density="compact"
variant="outlined"
class="pointer"
size="small"
@click="dismissVersionBanner"
>
</v-col>
Dismiss
</v-btn>
<v-btn
variant="tonal"
density="compact"
size="small"
@click="openNewVersion"
>
See what's new!
</v-btn>
</v-btn-group>
</v-row>
</v-card-text>
</v-card>
@@ -61,6 +61,11 @@ onBeforeUnmount(() => {
emit('hover', { isHovering: false, id: platform.id });
}
"
@blur="
() => {
emit('hover', { isHovering: false, id: platform.id });
}
"
>
<v-card-text>
<v-row class="pa-1 justify-center align-center bg-background">
+1 -1
View File
@@ -611,7 +611,7 @@ onUnmounted(() => {
}"
tabindex="0"
@click="openDescription()"
@keydown.enter.prevent="openDescription()"
@keydown.enter="openDescription()"
>
{{ rom.summary }}
</div>
+12 -15
View File
@@ -1,14 +1,7 @@
<script setup lang="ts">
import { useLocalStorage } from "@vueuse/core";
import { storeToRefs } from "pinia";
import {
onMounted,
onBeforeUnmount,
ref,
watch,
nextTick,
onUnmounted,
} from "vue";
import { onMounted, onBeforeUnmount, ref, watch, nextTick } from "vue";
import { useRoute, useRouter } from "vue-router";
import type { DetailedRomSchema } from "@/__generated__/models/DetailedRomSchema";
import NavigationText from "@/console/components/NavigationText.vue";
@@ -159,7 +152,10 @@ async function saveAndExit() {
}
}
async function uploadState(stateFile: Uint8Array, screenshotFile: Uint8Array) {
async function uploadState(
stateFile: ArrayBuffer,
screenshotFile: ArrayBuffer,
) {
if (!romRef.value) return;
const filename = `${romRef.value.fs_name_no_ext.trim()} [${new Date()
.toISOString()
@@ -435,8 +431,8 @@ async function boot() {
state: stateFile,
screenshot: screenshotFile,
}: {
state: Uint8Array;
screenshot: Uint8Array;
state: ArrayBuffer;
screenshot: ArrayBuffer;
}) {
try {
const formData = new FormData();
@@ -463,15 +459,15 @@ async function boot() {
save: saveFile,
screenshot: screenshotFile,
}: {
save: Uint8Array;
screenshot: Uint8Array;
save: ArrayBuffer;
screenshot: ArrayBuffer;
}) {
console.info(
"EJS_onSaveSave callback triggered",
"saveFile:",
saveFile?.length,
saveFile?.byteLength,
"screenshotFile:",
screenshotFile?.length,
screenshotFile?.byteLength,
);
try {
@@ -779,6 +775,7 @@ onBeforeUnmount(() => {
role="button"
:aria-selected="focusedExitIndex === i"
@click="activateExitOption(opt.id)"
@keydown.enter="activateExitOption(opt.id)"
>
<div class="flex items-center gap-3">
<div class="flex-1">
@@ -268,6 +268,7 @@ onBeforeRouteUpdate(async (to, from) => {
@touchstart="onGameTouchStart"
@touchend="onGameTouchEnd"
@hover="onHover"
@focus="onHover"
@openedmenu="onOpenedMenu"
@closedmenu="onClosedMenu"
/>
+1
View File
@@ -278,6 +278,7 @@ onBeforeRouteUpdate(async (to, from) => {
@touchstart="onGameTouchStart"
@touchend="onGameTouchEnd"
@hover="onHover"
@focus="onHover"
@openedmenu="onOpenedMenu"
@closedmenu="onClosedMenu"
/>
+1
View File
@@ -182,6 +182,7 @@ onUnmounted(() => {
@touchstart="onGameTouchStart"
@touchend="onGameTouchEnd"
@hover="onHover"
@focus="onHover"
@openedmenu="onOpenedMenu"
@closedmenu="onClosedMenu"
/>
@@ -85,13 +85,13 @@ declare global {
EJS_VirtualGamepadSettings: Record<string, unknown>;
EJS_onGameStart: () => void;
EJS_onSaveState: (args: {
screenshot: Uint8Array;
state: Uint8Array;
screenshot: ArrayBuffer;
state: ArrayBuffer;
}) => void;
EJS_onLoadState: () => void;
EJS_onSaveSave: (args: {
screenshot: Uint8Array;
save: Uint8Array;
screenshot: ArrayBuffer;
save: ArrayBuffer;
}) => void;
EJS_onLoadSave: () => void;
}
@@ -20,8 +20,8 @@ export async function saveState({
screenshotFile,
}: {
rom: DetailedRom;
stateFile: Uint8Array;
screenshotFile?: Uint8Array;
stateFile: ArrayBuffer;
screenshotFile?: ArrayBuffer;
}): Promise<StateSchema | null> {
const filename = buildStateName(rom);
try {
@@ -62,8 +62,8 @@ export async function saveSave({
}: {
rom: DetailedRom;
save: SaveSchema | null;
saveFile: Uint8Array;
screenshotFile?: Uint8Array;
saveFile: ArrayBuffer;
screenshotFile?: ArrayBuffer;
}): Promise<SaveSchema | null> {
if (save) {
try {
+4 -1
View File
@@ -156,12 +156,15 @@ onMounted(async () => {
<v-col>
<v-card-title class="text-subtitle-1 pa-0 text-uppercase">
<v-icon class="mr-2"> mdi-palette </v-icon>
{{ t("play.background-color") }}
<label for="background-color-input">{{
t("play.select-background-color")
}}</label>
</v-card-title>
</v-col>
<v-col class="d-flex justify-end">
<input
v-model="backgroundColor"
id="background-color-input"
type="color"
class="h-100 w-50 text-right"
:title="t('play.select-background-color')"
+3 -3
View File
@@ -97,13 +97,13 @@ onUnmounted(() => {
"
>
<v-fade-transition>
<div
<v-btn
v-if="isHovering"
class="d-flex translucent cursor-pointer h-100 align-center justify-center text-h4"
class="d-flex translucent cursor-pointer h-100 w-100 align-center justify-center text-h4"
@click="triggerFileInput"
>
<v-icon>mdi-pencil</v-icon>
</div>
</v-btn>
</v-fade-transition>
<v-file-input
id="file-input"