fix: clear button, page scroll, and metadata loading state

This commit is contained in:
tdgao
2026-08-14 12:47:37 -06:00
parent e2d5d4dd37
commit ee74727e36
3 changed files with 25 additions and 12 deletions
+7 -1
View File
@@ -39,7 +39,7 @@
@focusin="handleSearchFocus" @focusin="handleSearchFocus"
@focusout="handleSearchFocusout" @focusout="handleSearchFocusout"
@click="handleSearchClick" @click="handleSearchClick"
@clear="handleSearchInput" @clear="handleSearchClear"
> >
<template v-if="showChevron" #right> <template v-if="showChevron" #right>
<ChevronLeftIcon <ChevronLeftIcon
@@ -842,6 +842,12 @@ function handleSearchInput() {
} }
} }
function handleSearchClear() {
userHasTyped.value = true
emit('searchInput', searchQuery.value)
closeDropdown()
}
function handleSearchFocus(event: FocusEvent) { function handleSearchFocus(event: FocusEvent) {
const target = event.target const target = event.target
if (props.selectSearchTextOnFocus && target instanceof HTMLInputElement) { if (props.selectSearchTextOnFocus && target instanceof HTMLInputElement) {
@@ -91,7 +91,7 @@
type="button" type="button"
class="absolute right-0.5 z-[1] p-2 touch-manipulation bg-transparent border-none text-secondary hover:text-contrast transition-colors cursor-pointer select-none" class="absolute right-0.5 z-[1] p-2 touch-manipulation bg-transparent border-none text-secondary hover:text-contrast transition-colors cursor-pointer select-none"
aria-label="Clear input" aria-label="Clear input"
@click="clear" @click.stop="clear"
> >
<XIcon class="h-5 w-5" /> <XIcon class="h-5 w-5" />
</button> </button>
@@ -10,21 +10,21 @@
show-search-icon show-search-icon
@update:model-value="addIncludedProject" @update:model-value="addIncludedProject"
/> />
<div v-if="dependencyProjectIds.length > 0" class="flex flex-col gap-1"> <div v-if="loadedDependentProjects.length > 0" class="flex flex-col gap-1">
<div <div
v-for="projectId in dependencyProjectIds" v-for="{ projectId, project } in loadedDependentProjects"
:key="projectId" :key="projectId"
class="group flex min-w-0 items-center gap-2 rounded-xl px-1.5 py-1 text-primary" class="group flex min-w-0 items-center gap-2 rounded-xl px-1.5 py-1 text-primary"
> >
<img <img
v-if="dependentProjectMap.get(projectId)?.icon_url" v-if="project.icon_url"
:src="dependentProjectMap.get(projectId)?.icon_url ?? undefined" :src="project.icon_url"
:alt="dependentProjectMap.get(projectId)?.title ?? projectId" :alt="project.title"
class="size-6 shrink-0 rounded-md object-cover" class="size-6 shrink-0 rounded-md object-cover"
/> />
<PackageIcon v-else class="size-8 shrink-0 text-secondary" /> <PackageIcon v-else class="size-8 shrink-0 text-secondary" />
<span class="min-w-0 flex-1 truncate font-medium text-contrast"> <span class="min-w-0 flex-1 truncate font-medium text-contrast">
{{ dependentProjectMap.get(projectId)?.title ?? projectId }} {{ project.title }}
</span> </span>
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<button <button
@@ -33,7 +33,7 @@
class="flex shrink-0 cursor-pointer items-center justify-center rounded-xl border-0 bg-transparent px-2 py-1 text-secondary transition-all [@media(hover:hover)]:opacity-0 group-hover:opacity-100 hover:bg-button-bg hover:text-contrast active:scale-[0.96]" class="flex shrink-0 cursor-pointer items-center justify-center rounded-xl border-0 bg-transparent px-2 py-1 text-secondary transition-all [@media(hover:hover)]:opacity-0 group-hover:opacity-100 hover:bg-button-bg hover:text-contrast active:scale-[0.96]"
:aria-label=" :aria-label="
formatMessage(messages.removeIncludedProject, { formatMessage(messages.removeIncludedProject, {
project: dependentProjectMap.get(projectId)?.title ?? projectId, project: project.title,
}) })
" "
@click="removeIncludedProject(projectId)" @click="removeIncludedProject(projectId)"
@@ -68,7 +68,7 @@
</div> </div>
<div v-else :class="innerPanelClass" class="flex flex-col gap-3"> <div v-else :class="innerPanelClass" class="flex flex-col gap-3">
<ProjectCombobox <ProjectCombobox
v-show="!selectedProjectId" v-show="!showSelectedProject"
ref="projectCombobox" ref="projectCombobox"
:model-value="selectedProjectId" :model-value="selectedProjectId"
:project-types="selectableProjectTypes" :project-types="selectableProjectTypes"
@@ -78,7 +78,7 @@
show-search-icon show-search-icon
@update:model-value="setSelectedProjectId" @update:model-value="setSelectedProjectId"
/> />
<template v-if="selectedProjectId && selectedProject"> <template v-if="showSelectedProject">
<div class="flex items-center justify-between gap-3 px-2 text-secondary"> <div class="flex items-center justify-between gap-3 px-2 text-secondary">
<span> <span>
{{ {{
@@ -203,13 +203,19 @@ const { data: dependentProjects } = useQuery({
]), ]),
queryFn: () => labrinth.projects_v2.getMultiple(dependencyProjectIds.value), queryFn: () => labrinth.projects_v2.getMultiple(dependencyProjectIds.value),
enabled: computed(() => isModpack.value && dependencyProjectIds.value.length > 0), enabled: computed(() => isModpack.value && dependencyProjectIds.value.length > 0),
placeholderData: [], placeholderData: (previousData) => previousData,
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
}) })
const dependentProjectMap = computed( const dependentProjectMap = computed(
() => new Map(dependentProjects.value?.map((project) => [project.id, project]) ?? []), () => new Map(dependentProjects.value?.map((project) => [project.id, project]) ?? []),
) )
const loadedDependentProjects = computed(() =>
dependencyProjectIds.value.flatMap((projectId) => {
const project = dependentProjectMap.value.get(projectId)
return project ? [{ projectId, project }] : []
}),
)
const projectCombobox = ref<{ selectedProject: SearchHit | null } | null>(null) const projectCombobox = ref<{ selectedProject: SearchHit | null } | null>(null)
const selectedProject = computed(() => projectCombobox.value?.selectedProject ?? null) const selectedProject = computed(() => projectCombobox.value?.selectedProject ?? null)
const selectedDependencyFilter = computed(() => const selectedDependencyFilter = computed(() =>
@@ -220,6 +226,7 @@ const selectedProjectId = computed(() =>
? parseDependencyProjectFilterOption(selectedDependencyFilter.value.option).projectId ? parseDependencyProjectFilterOption(selectedDependencyFilter.value.option).projectId
: undefined, : undefined,
) )
const showSelectedProject = computed(() => Boolean(selectedProjectId.value && selectedProject.value))
const dependencyTypes = computed<DependencyType[]>(() => const dependencyTypes = computed<DependencyType[]>(() =>
selectedDependencyFilter.value selectedDependencyFilter.value