fix: website visual issues (#5675)

* fix no modpack loader showing as resource pack loader

* fix table overflow, add game version tags "+ {num}" overflow menu

* pnpm prepr
This commit is contained in:
Truman Gao
2026-03-26 11:40:44 -07:00
committed by GitHub
parent f1648298c4
commit 706eb800cb
6 changed files with 154 additions and 34 deletions

View File

@@ -5,7 +5,7 @@
v-if="!isServerProject" v-if="!isServerProject"
:project="data" :project="data"
:tags="{ loaders: allLoaders, gameVersions: allGameVersions }" :tags="{ loaders: allLoaders, gameVersions: allGameVersions }"
:v3-metadata="projectV3" :project-v3="projectV3"
class="project-sidebar-section" class="project-sidebar-section"
/> />
<ProjectSidebarServerInfo <ProjectSidebarServerInfo

View File

@@ -877,7 +877,7 @@
v-if="projectV3Loaded && !isServerProject" v-if="projectV3Loaded && !isServerProject"
:project="project" :project="project"
:tags="tags" :tags="tags"
:v3-metadata="projectV3" :project-v3="projectV3"
class="card flex-card experimental-styles-within" class="card flex-card experimental-styles-within"
/> />
<AdPlaceholder v-if="!auth.user && tags.approvedStatuses.includes(project.status)" /> <AdPlaceholder v-if="!auth.user && tags.approvedStatuses.includes(project.status)" />

View File

@@ -341,7 +341,8 @@
<div v-if="project.project_type !== 'resourcepack'"> <div v-if="project.project_type !== 'resourcepack'">
<h4>Loaders</h4> <h4>Loaders</h4>
<Categories :categories="version.loaders" :type="project.project_type" /> <span v-if="noModpackLoader">No mod loader</span>
<Categories v-else :categories="version.loaders ?? []" :type="project.project_type" />
</div> </div>
<div> <div>
<h4>Game versions</h4> <h4>Game versions</h4>
@@ -698,6 +699,25 @@ const title = computed(
() => `${isCreating.value ? 'Create Version' : version.value.name} - ${project.value.title}`, () => `${isCreating.value ? 'Create Version' : version.value.name} - ${project.value.title}`,
) )
const modpackLoaders = computed<string[]>(() => {
if (project.value.project_type !== 'modpack') {
return []
}
if (Array.isArray(version.value.mrpack_loaders) && version.value.mrpack_loaders.length > 0) {
return version.value.mrpack_loaders
}
return (version.value.loaders ?? []).filter((loader: string) => loader !== 'mrpack')
})
const noModpackLoader = computed(
() =>
project.value.project_type === 'modpack' &&
modpackLoaders.value.length === 1 &&
modpackLoaders.value[0] === 'minecraft',
)
const description = computed( const description = computed(
() => () =>
`Download ${project.value.title} ${ `Download ${project.value.title} ${

View File

@@ -593,6 +593,7 @@ export namespace Labrinth {
categories: string[] categories: string[]
additional_categories: string[] additional_categories: string[]
loaders: string[] loaders: string[]
mrpack_loaders: string[]
versions: string[] versions: string[]
icon_url?: string icon_url?: string
link_urls: Record<string, Link> link_urls: Record<string, Link>

View File

@@ -3,7 +3,7 @@
<div class="flex flex-wrap justify-between gap-2"> <div class="flex flex-wrap justify-between gap-2">
<VersionFilterControl <VersionFilterControl
ref="versionFilters" ref="versionFilters"
:versions="versions" :versions="normalizedVersions"
:game-versions="gameVersions" :game-versions="gameVersions"
:base-id="`${baseId}-filter`" :base-id="`${baseId}-filter`"
@update:query="updateQuery" @update:query="updateQuery"
@@ -110,13 +110,18 @@
</div> </div>
</div> </div>
<div <div
class="pointer-events-none relative z-[1] flex flex-col justify-center" class="pointer-events-none relative z-[1] flex flex-col justify-center overflow-hidden min-w-32"
:class="{ :class="{
'group-hover:underline': !!versionLink, 'group-hover:underline': !!versionLink,
}" }"
title="`${version.version_number} - ${version.name}`"
> >
<div class="font-bold text-contrast">{{ version.version_number }}</div> <div class="font-bold text-contrast text-ellipsis overflow-hidden">
<div class="text-xs font-medium">{{ version.name }}</div> {{ version.version_number }}
</div>
<div class="text-xs font-medium text-ellipsis overflow-hidden">
{{ version.name }}
</div>
</div> </div>
</div> </div>
<div class="flex flex-col justify-center gap-2 sm:contents"> <div class="flex flex-col justify-center gap-2 sm:contents">
@@ -127,7 +132,7 @@
v-for="gameVersion in formatVersionsForDisplay( v-for="gameVersion in formatVersionsForDisplay(
version.game_versions, version.game_versions,
gameVersions, gameVersions,
)" ).slice(0, maxGameVersionTags)"
:key="`version-tag-${gameVersion}`" :key="`version-tag-${gameVersion}`"
v-tooltip="`Toggle filter for ${gameVersion}`" v-tooltip="`Toggle filter for ${gameVersion}`"
class="z-[1]" class="z-[1]"
@@ -137,21 +142,61 @@
> >
{{ gameVersion }} {{ gameVersion }}
</TagItem> </TagItem>
<Menu
v-if="
formatVersionsForDisplay(version.game_versions, gameVersions).length >
maxGameVersionTags
"
:delay="{ hide: 50, show: 0 }"
no-auto-focus
class="z-[1] cursor-default"
>
<TagItem tabindex="0">
+{{
formatVersionsForDisplay(version.game_versions, gameVersions).length -
maxGameVersionTags
}}
</TagItem>
<template #popper>
<div class="flex gap-1 flex-wrap max-w-[20rem]">
<TagItem
v-for="gameVersion in formatVersionsForDisplay(
version.game_versions,
gameVersions,
).slice(maxGameVersionTags)"
:key="`overflow-version-tag-${gameVersion}`"
:action="
() =>
versionFilters?.toggleFilters('gameVersion', version.game_versions)
"
>
{{ gameVersion }}
</TagItem>
</div>
</template>
</Menu>
</div> </div>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<div class="flex flex-wrap gap-1"> <div class="flex flex-wrap gap-1">
<TagItem <template v-if="version.noModLoader">
v-for="platform in version.loaders" <TagItem class="z-[1] border !border-solid border-surface-5">
:key="`platform-tag-${platform}`" No mod loader
v-tooltip="`Toggle filter for ${platform}`" </TagItem>
class="z-[1]" </template>
:style="`--_color: var(--color-platform-${platform})`" <template v-else>
:action="() => versionFilters?.toggleFilter('platform', platform)" <TagItem
> v-for="platform in version.loaders"
<component :is="getLoaderIcon(platform)" v-if="getLoaderIcon(platform)" /> :key="`platform-tag-${platform}`"
<FormattedTag :tag="platform" enforce-type="loader" /> v-tooltip="`Toggle filter for ${platform}`"
</TagItem> class="z-[1]"
:style="`--_color: var(--color-platform-${platform})`"
:action="() => versionFilters?.toggleFilter('platform', platform)"
>
<component :is="getLoaderIcon(platform)" v-if="getLoaderIcon(platform)" />
<FormattedTag :tag="platform" enforce-type="loader" />
</TagItem>
</template>
</div> </div>
</div> </div>
<div v-if="hasMultipleEnvironments" class="flex items-center"> <div v-if="hasMultipleEnvironments" class="flex items-center">
@@ -162,7 +207,7 @@
class="z-[1] text-center" class="z-[1] text-center"
> >
<component :is="tag.icon" /> <component :is="tag.icon" />
{{ formatMessage(tag.label) }} {{ formatMessage(tag.label).replace('and', '&') }}
</TagItem> </TagItem>
</div> </div>
</div> </div>
@@ -233,6 +278,7 @@ import {
type GameVersionTag, type GameVersionTag,
type Version, type Version,
} from '@modrinth/utils' } from '@modrinth/utils'
import { Menu } from 'floating-vue'
import { computed, type Ref, ref } from 'vue' import { computed, type Ref, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
@@ -251,6 +297,11 @@ const formatDateTime = useFormatDateTime({
type VersionWithDisplayUrlEnding = Version & { type VersionWithDisplayUrlEnding = Version & {
displayUrlEnding: string displayUrlEnding: string
environment?: Labrinth.Projects.v3.Environment environment?: Labrinth.Projects.v3.Environment
mrpack_loaders?: string[]
}
type DisplayVersion = VersionWithDisplayUrlEnding & {
noModLoader: boolean
} }
const props = withDefaults( const props = withDefaults(
@@ -278,6 +329,39 @@ const props = withDefaults(
}, },
) )
function getModpackLoaders(version: VersionWithDisplayUrlEnding): string[] {
if (props.project.project_type !== 'modpack') {
return version.loaders
}
if (version.mrpack_loaders?.length) {
return version.mrpack_loaders
}
return version.loaders.filter((loader) => loader !== 'mrpack')
}
function hasNoModLoader(loaders: string[]): boolean {
return (
props.project.project_type === 'modpack' && loaders.length === 1 && loaders[0] === 'minecraft'
)
}
const normalizedVersions = computed<DisplayVersion[]>(() =>
props.versions.map((version) => {
const loaders = getModpackLoaders(version)
const noModLoader = hasNoModLoader(loaders)
return {
...version,
loaders: noModLoader ? [] : loaders,
noModLoader,
}
}),
)
const maxGameVersionTags = 6
const currentPage: Ref<number> = ref(1) const currentPage: Ref<number> = ref(1)
const pageSize: Ref<number> = ref(20) const pageSize: Ref<number> = ref(20)
const versionFilters: Ref<InstanceType<typeof VersionFilterControl> | null> = ref(null) const versionFilters: Ref<InstanceType<typeof VersionFilterControl> | null> = ref(null)
@@ -296,7 +380,7 @@ const hasMultipleEnvironments = computed(() => {
}) })
const filteredVersions = computed(() => { const filteredVersions = computed(() => {
return props.versions.filter( return normalizedVersions.value.filter(
(version) => (version) =>
hasAnySelected(version.game_versions, selectedGameVersions.value) && hasAnySelected(version.game_versions, selectedGameVersions.value) &&
hasAnySelected(version.loaders, selectedPlatforms.value) && hasAnySelected(version.loaders, selectedPlatforms.value) &&

View File

@@ -15,15 +15,22 @@
<section v-if="project.project_type !== 'resourcepack'" class="flex flex-col gap-2"> <section v-if="project.project_type !== 'resourcepack'" class="flex flex-col gap-2">
<h3 class="text-primary text-base m-0">{{ formatMessage(messages.platforms) }}</h3> <h3 class="text-primary text-base m-0">{{ formatMessage(messages.platforms) }}</h3>
<div class="flex flex-wrap gap-1"> <div class="flex flex-wrap gap-1">
<TagItem <template v-if="noModpackLoader">
v-for="platform in project.loaders" <TagItem class="border !border-solid border-surface-5 hover:no-underline">
:key="`platform-tag-${platform}`" No mod loader
:action="() => router.push(`/${project.project_type}s?g=categories:${platform}`)" </TagItem>
:style="`--_color: var(--color-platform-${platform})`" </template>
> <template v-else>
<component :is="getLoaderIcon(platform)" v-if="getLoaderIcon(platform)" /> <TagItem
<FormattedTag :tag="platform" enforce-type="loader" /> v-for="platform in project.loaders"
</TagItem> :key="`platform-tag-${platform}`"
:action="() => router.push(`/${project.project_type}s?g=categories:${platform}`)"
:style="`--_color: var(--color-platform-${platform})`"
>
<component :is="getLoaderIcon(platform)" v-if="getLoaderIcon(platform)" />
<FormattedTag :tag="platform" enforce-type="loader" />
</TagItem>
</template>
</div> </div>
</section> </section>
<section v-if="showEnvironments" class="flex flex-col gap-2"> <section v-if="showEnvironments" class="flex flex-col gap-2">
@@ -85,6 +92,7 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import { import {
ClientIcon, ClientIcon,
getLoaderIcon, getLoaderIcon,
@@ -93,7 +101,7 @@ import {
UserIcon, UserIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { FormattedTag, TagItem } from '@modrinth/ui' import { FormattedTag, TagItem } from '@modrinth/ui'
import type { EnvironmentV3, GameVersionTag, PlatformTag, ProjectV3Partial } from '@modrinth/utils' import type { EnvironmentV3, GameVersionTag, PlatformTag } from '@modrinth/utils'
import { getVersionsToDisplay } from '@modrinth/utils' import { getVersionsToDisplay } from '@modrinth/utils'
import { type Component, computed } from 'vue' import { type Component, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
@@ -128,17 +136,24 @@ const props = defineProps<{
gameVersions: GameVersionTag[] gameVersions: GameVersionTag[]
loaders: PlatformTag[] loaders: PlatformTag[]
} }
v3Metadata?: ProjectV3Partial projectV3?: Labrinth.Projects.v3.Project
}>() }>()
const noModpackLoader = computed(
() =>
props.projectV3?.project_types.includes('modpack') &&
props.projectV3?.mrpack_loaders.length === 1 &&
props.projectV3?.mrpack_loaders[0] === 'minecraft',
)
const showEnvironments = computed( const showEnvironments = computed(
() => () =>
TYPES_WITH_ENVS.some((x) => props.v3Metadata?.project_types.includes(x)) && TYPES_WITH_ENVS.some((x) => props.projectV3?.project_types.includes(x)) &&
primaryEnvironment.value, primaryEnvironment.value,
) )
const primaryEnvironment = computed<EnvironmentV3 | undefined>(() => const primaryEnvironment = computed<EnvironmentV3 | undefined>(() =>
props.v3Metadata?.environment?.find((x) => x !== 'unknown'), props.projectV3?.environment?.find((x) => x !== 'unknown'),
) )
type EnvironmentTag = { type EnvironmentTag = {