mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-18 22:36:43 +09:00
chore(lint): apply the main eslint config to vue files (#39545)
Vue files only got `eslint-plugin-vue` rules, so stylistic and TypeScript rules never ran on them, and `import-x` could not see cycles between `.ts` and `.vue` files. - Apply the main ESLint config to `.vue` files - Let `import-x` parse `.vue` files, which surfaced a cycle between `repo-findfile.ts` and `RepoFileSearch.vue` - Fix the resulting lint issues `vue-eslint-parser` is added as a direct dependency because `import-x` resolves parsers by package name, see https://github.com/un-ts/eslint-plugin-import-x/issues/381.
This commit is contained in:
1 parent
986b0bcae0
commit
99573bde0e
12 files changed
+115
-112
No files matched your search
@@ -2,18 +2,19 @@
|
||||
import {ref, computed, watch, nextTick, useTemplateRef, onMounted, onUnmounted, type ShallowRef} from 'vue';
|
||||
import {generateElemId} from '../utils/dom.ts';
|
||||
import {GET} from '../modules/fetch.ts';
|
||||
import {filterRepoFilesWeighted} from '../features/repo-findfile.ts';
|
||||
import {pathEscapeSegments} from '../utils/url.ts';
|
||||
import SvgIcon from './SvgIcon.vue';
|
||||
import {throttle} from '../utils/func.ts';
|
||||
import type {RepoFilesFilter} from '../features/repo-findfile.ts';
|
||||
|
||||
const props = defineProps({
|
||||
repoLink: { type: String, required: true },
|
||||
currentRefNameSubURL: { type: String, required: true },
|
||||
treeListUrl: { type: String, required: true },
|
||||
noResultsText: { type: String, required: true },
|
||||
placeholder: { type: String, required: true },
|
||||
});
|
||||
const props = defineProps<{
|
||||
repoLink: string;
|
||||
currentRefNameSubURL: string;
|
||||
treeListUrl: string;
|
||||
noResultsText: string;
|
||||
placeholder: string;
|
||||
filterFiles: RepoFilesFilter;
|
||||
}>();
|
||||
|
||||
const refElemInput = useTemplateRef('searchInput') as Readonly<ShallowRef<HTMLInputElement>>;
|
||||
const refElemPopup = useTemplateRef('searchPopup') as Readonly<ShallowRef<HTMLDivElement>>;
|
||||
@@ -28,7 +29,7 @@ const showPopup = computed(() => searchQuery.value.length > 0);
|
||||
|
||||
const filteredFiles = computed(() => {
|
||||
if (!searchQuery.value) return [];
|
||||
return filterRepoFilesWeighted(allFiles.value, searchQuery.value);
|
||||
return props.filterFiles(allFiles.value, searchQuery.value);
|
||||
});
|
||||
|
||||
const applySearchQuery = throttle(() => {
|
||||
@@ -52,16 +53,17 @@ const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (!searchQuery.value || filteredFiles.value.length === 0) return;
|
||||
|
||||
const handleSelectedItem = (idx: number) => {
|
||||
e.preventDefault();
|
||||
selectedIndex.value = idx;
|
||||
const el = refElemPopup.value.querySelector(`.file-search-results > :nth-child(${idx+1} of .item)`);
|
||||
el?.scrollIntoView({ block: 'nearest', behavior: 'instant' });
|
||||
const el = refElemPopup.value.querySelector(`.file-search-results > :nth-child(${idx + 1} of .item)`);
|
||||
el?.scrollIntoView({block: 'nearest', behavior: 'instant'});
|
||||
};
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
handleSelectedItem(Math.min(selectedIndex.value + 1, filteredFiles.value.length - 1));
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
handleSelectedItem(Math.max(selectedIndex.value - 1, 0))
|
||||
e.preventDefault();
|
||||
handleSelectedItem(Math.max(selectedIndex.value - 1, 0));
|
||||
} else if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
const selectedFile = filteredFiles.value[selectedIndex.value];
|
||||
@@ -71,11 +73,10 @@ const handleKeyDown = (e: KeyboardEvent) => {
|
||||
}
|
||||
};
|
||||
|
||||
const clearSearch = () => {
|
||||
function clearSearch() {
|
||||
searchQuery.value = '';
|
||||
refElemInput.value.value = '';
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const handleClickOutside = (e: MouseEvent) => {
|
||||
if (!searchQuery.value) return;
|
||||
@@ -85,7 +86,7 @@ const handleClickOutside = (e: MouseEvent) => {
|
||||
if (!clickInside) clearSearch();
|
||||
};
|
||||
|
||||
const loadFileListForSearch = async () => {
|
||||
async function loadFileListForSearch() {
|
||||
if (hasLoadedFileList.value || isLoadingFileList.value) return;
|
||||
|
||||
isLoadingFileList.value = true;
|
||||
@@ -96,11 +97,11 @@ const loadFileListForSearch = async () => {
|
||||
} finally {
|
||||
isLoadingFileList.value = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function handleSearchResultClick(filePath: string) {
|
||||
clearSearch();
|
||||
window.location.href = `${props.repoLink}/src/${pathEscapeSegments(props.currentRefNameSubURL)}/${pathEscapeSegments(filePath)}`;
|
||||
window.location.assign(`${props.repoLink}/src/${pathEscapeSegments(props.currentRefNameSubURL)}/${pathEscapeSegments(filePath)}`);
|
||||
}
|
||||
|
||||
const updatePosition = () => {
|
||||
@@ -121,17 +122,19 @@ const updatePosition = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const resizeObserver = new ResizeObserver(updatePosition);
|
||||
|
||||
onMounted(() => {
|
||||
const searchPopupId = generateElemId('file-search-popup-');
|
||||
refElemPopup.value.setAttribute('id', searchPopupId);
|
||||
refElemInput.value.setAttribute('aria-controls', searchPopupId);
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
window.addEventListener('resize', updatePosition);
|
||||
resizeObserver.observe(document.documentElement);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
window.removeEventListener('resize', updatePosition);
|
||||
resizeObserver.disconnect();
|
||||
});
|
||||
|
||||
// Position search results below the input
|
||||
|
||||
Reference in new issue
Block a user