proj card fixes

This commit is contained in:
Prospector
2026-02-07 12:05:59 -08:00
parent a978873bff
commit 428efde36d
7 changed files with 178 additions and 168 deletions

View File

@@ -285,9 +285,9 @@
]" ]"
/> />
<div <ProjectCardList
v-if="projects && projects?.length > 0" v-if="projects && projects?.length > 0"
:class="'project-list display-mode--' + (cosmetics.searchDisplayMode.collection || 'list')" :layout="cosmetics.searchDisplayMode.collection"
> >
<ProjectCard <ProjectCard
v-for="project in (route.params.projectType !== undefined v-for="project in (route.params.projectType !== undefined
@@ -343,7 +343,7 @@
</button> </button>
</template> </template>
</ProjectCard> </ProjectCard>
</div> </ProjectCardList>
<div v-else> <div v-else>
<div class="mx-auto flex flex-col justify-center gap-8 p-6 text-center"> <div class="mx-auto flex flex-col justify-center gap-8 p-6 text-center">
<EmptyIllustration class="h-[120px] w-auto" /> <EmptyIllustration class="h-[120px] w-auto" />
@@ -402,6 +402,7 @@ import {
NormalPage, NormalPage,
OverflowMenu, OverflowMenu,
ProjectCard, ProjectCard,
ProjectCardList,
RadioButtons, RadioButtons,
SidebarCard, SidebarCard,
useRelativeTime, useRelativeTime,

View File

@@ -186,50 +186,47 @@
<div v-if="navLinks.length > 2" class="mb-4 max-w-full overflow-x-auto"> <div v-if="navLinks.length > 2" class="mb-4 max-w-full overflow-x-auto">
<NavTabs :links="navLinks" /> <NavTabs :links="navLinks" />
</div> </div>
<template v-if="projects && projects.length > 0"> <ProjectCardList v-if="projects && projects.length > 0">
<div class="project-list display-mode--list"> <ProjectCard
<ProjectCard v-for="project in (route.params.projectType !== undefined
v-for="project in (route.params.projectType !== undefined ? (projects ?? []).filter((x) =>
? (projects ?? []).filter((x) => x.project_types.includes(
x.project_types.includes( typeof route.params.projectType === 'string'
typeof route.params.projectType === 'string' ? route.params.projectType.slice(0, route.params.projectType.length - 1)
? route.params.projectType.slice(0, route.params.projectType.length - 1) : route.params.projectType[0]?.slice(
: route.params.projectType[0]?.slice( 0,
0, route.params.projectType[0].length - 1,
route.params.projectType[0].length - 1, ) || '',
) || '', ),
), )
) : (projects ?? [])
: (projects ?? []) )
) .slice()
.slice() .sort((a, b) => b.downloads - a.downloads)"
.sort((a, b) => b.downloads - a.downloads)" :key="project.id"
:key="project.id" :link="`/${project.project_types[0] ?? 'project'}/${project.slug || project.id}`"
:link="`/${project.project_types[0] ?? 'project'}/${project.slug || project.id}`" :title="project.name"
:title="project.name" :icon-url="project.icon_url"
:icon-url="project.icon_url" :banner="project.gallery.find((element) => element.featured)?.url"
:banner="project.gallery.find((element) => element.featured)?.url" :summary="project.summary"
:summary="project.summary" :date-updated="project.updated"
:date-updated="project.updated" :downloads="project.downloads"
:downloads="project.downloads" :followers="project.followers"
:followers="project.followers" :tags="project.categories"
:tags="project.categories" :environment="{
:environment="{ clientSide: project.client_side,
clientSide: project.client_side, serverSide: project.server_side,
serverSide: project.server_side, }"
}" :status="
:status=" auth.user &&
auth.user && (auth.user.id! === (user as any).id || tags.staffRoles.includes(auth.user.role))
(auth.user.id! === (user as any).id || tags.staffRoles.includes(auth.user.role)) ? (project.status as ProjectStatus)
? (project.status as ProjectStatus) : undefined
: undefined "
" :color="project.color"
:color="project.color" layout="list"
layout="list" />
/> </ProjectCardList>
</div>
</template>
<div v-else-if="true" class="error"> <div v-else-if="true" class="error">
<UpToDate class="icon" /> <UpToDate class="icon" />
<br /> <br />
@@ -267,6 +264,7 @@ import {
ContentPageHeader, ContentPageHeader,
OverflowMenu, OverflowMenu,
ProjectCard, ProjectCard,
ProjectCardList,
useVIntl, useVIntl,
} from '@modrinth/ui' } from '@modrinth/ui'
import type { Organization, ProjectStatus, ProjectType, ProjectV3 } from '@modrinth/utils' import type { Organization, ProjectStatus, ProjectType, ProjectV3 } from '@modrinth/utils'

View File

@@ -286,9 +286,9 @@
<NavTabs :links="navLinks" /> <NavTabs :links="navLinks" />
</div> </div>
<div v-if="projects.length > 0"> <div v-if="projects.length > 0">
<div <ProjectCardList
v-if="route.params.projectType !== 'collections'" v-if="route.params.projectType !== 'collections'"
:class="'project-list display-mode--' + cosmetics.searchDisplayMode.user" :layout="cosmetics.searchDisplayMode.user"
> >
<ProjectCard <ProjectCard
v-for="project in (route.params.projectType !== undefined v-for="project in (route.params.projectType !== undefined
@@ -315,6 +315,7 @@
...project.additional_categories, ...project.additional_categories,
]" ]"
:followers="project.followers" :followers="project.followers"
:banner="project.gallery.find((element) => element.featured)?.url"
:color="project.color ?? undefined" :color="project.color ?? undefined"
:environment="{ :environment="{
clientSide: project.client_side, clientSide: project.client_side,
@@ -328,7 +329,7 @@
" "
:status="project.status" :status="project.status"
/> />
</div> </ProjectCardList>
</div> </div>
<div <div
v-else-if=" v-else-if="
@@ -499,6 +500,7 @@ import {
NewModal, NewModal,
OverflowMenu, OverflowMenu,
ProjectCard, ProjectCard,
ProjectCardList,
TagItem, TagItem,
useRelativeTime, useRelativeTime,
useVIntl, useVIntl,

View File

@@ -1,7 +1,13 @@
<template> <template>
<div class="smart-clickable" :class="{ 'smart-clickable--has-clickable': !!$slots.clickable }"> <div class="smart-clickable" :class="{ 'smart-clickable--has-clickable': !!$slots.clickable }">
<slot name="clickable" /> <slot name="clickable" />
<div v-bind="$attrs" class="smart-clickable__contents pointer-events-none"> <div
v-bind="$attrs"
class="smart-clickable__contents"
:class="{
'pointer-events-none': !!$slots.clickable,
}"
>
<slot /> <slot />
</div> </div>
</div> </div>

View File

@@ -23,6 +23,6 @@ withDefaults(
<style scoped lang="scss"> <style scoped lang="scss">
.grid-project-list { .grid-project-list {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(150px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
} }
</style> </style>

View File

@@ -1,123 +1,52 @@
<template> <template>
<div class="w-full" @mouseenter="$emit('hover')"> <SmartClickable class="w-full project-card-container">
<SmartClickable class="w-full project-card-container"> <template v-if="link" #clickable>
<template v-if="link" #clickable> <AutoLink
<AutoLink :to="link"
:to="link" class="rounded-xl no-outline no-click-animation custom-focus-indicator"
class="rounded-xl no-outline no-click-animation custom-focus-indicator" @mouseenter="$emit('hover')"
></AutoLink> ></AutoLink>
</template> </template>
<div v-if="layout === 'grid'" :class="[baseCardStyle, 'flex flex-col']"> <div v-if="layout === 'grid'" :class="[baseCardStyle, 'flex flex-col']">
<div <div
:style="{ '--_project-color': cssColor }" :style="{ '--_project-color': cssColor }"
class="relative bg-project-gradient overflow-clip aspect-[2/1] w-full border-0 border-b-[1px] border-solid border-surface-4" class="relative bg-project-gradient overflow-clip aspect-[2/1] w-full border-0 border-b-[1px] border-solid border-surface-4"
> >
<img <img
v-if="banner" v-if="banner"
:src="banner" :src="banner"
alt="" alt=""
class="absolute w-full h-full inset-0 object-cover object-center" class="absolute w-full h-full inset-0 object-cover object-center"
/> />
<img <img
v-else v-else
src="https://cdn-raw.modrinth.com/landing-new/landing.webp" src="https://cdn-raw.modrinth.com/landing-new/landing.webp"
alt="" alt=""
class="absolute w-full h-full inset-0 object-cover object-center placeholder-banner scale-[200%]" class="absolute w-full h-full inset-0 object-cover object-center placeholder-banner scale-[200%]"
/> />
</div> </div>
<div class="p-4 flex flex-col gap-3 grow"> <div class="p-4 flex flex-col gap-3 grow">
<div class="flex gap-3"> <div class="flex gap-3">
<Avatar :src="iconUrl" size="96px" class="project-card__icon" no-shadow /> <Avatar :src="iconUrl" size="96px" class="project-card__icon" no-shadow />
<div class="flex flex-col gap-2 w-full"> <div class="flex flex-col gap-2 w-full">
<div class="grid grid-cols-[1fr_auto] gap-4"> <div class="grid grid-cols-[1fr_auto] gap-4">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<div class="flex gap-2 items-center"> <div class="flex gap-2 items-center">
<ProjectCardTitle :title="title" compact /> <ProjectCardTitle :title="title" compact />
<ProjectCardAuthor v-if="author" :author="author" /> <ProjectCardAuthor v-if="author" :author="author" />
</div> </div>
<div class="m-0 font-normal line-clamp-2"> <div class="m-0 font-normal line-clamp-2">
{{ summary }} {{ summary }}
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="flex gap-2 shrink-0 empty:hidden smart-clickable:allow-pointer-events">
<slot name="actions" />
</div>
<div class="mt-auto flex flex-col gap-3 flex-wrap overflow-hidden justify-between grow">
<div class="flex items-center gap-1 flex-wrap overflow-hidden">
<ProjectCardEnvironment
v-if="environment"
:client-side="environment.clientSide"
:server-side="environment.serverSide"
/>
<ProjectCardTags
v-if="tags"
:tags="tags"
:exclude-loaders="excludeLoaders"
:deprioritized-tags="deprioritizedTags"
:max-tags="6 + (!!environment ? 0 : 1)"
/>
</div>
<div
v-if="downloads !== undefined || followers !== undefined"
class="flex items-center gap-3 justify-between flex-wrap"
>
<div class="flex items-center gap-3 no-wrap flex-wrap">
<ProjectCardStats :downloads="downloads" :followers="followers" />
</div>
<ProjectCardDate
v-if="date && autoDisplayDate"
:type="autoDisplayDate"
:date="date"
/>
</div>
</div>
</div> </div>
</div> <div class="flex gap-2 shrink-0 empty:hidden smart-clickable:allow-pointer-events">
<div
v-else
:class="[
baseCardStyle,
'p-4 grid grid-project-card-list gap-x-3 gap-y-2',
{ 'has-actions': !!$slots.actions },
]"
>
<Avatar
:src="iconUrl"
size="100px"
class="project-card__icon grid-project-card-list__icon"
no-shadow
/>
<div class="flex flex-col gap-2 grid-project-card-list__info">
<div class="flex gap-2 items-center">
<ProjectCardTitle :title="title" />
<ProjectCardAuthor v-if="author" :author="author" />
<ProjectStatusBadge v-if="status" :status="status" />
</div>
<div class="project-card-summary m-0 font-normal line-clamp-2">
{{ summary }}
</div>
</div>
<div
v-if="!!$slots.actions"
class="flex gap-1 shrink-0 ml-auto empty:hidden smart-clickable:allow-pointer-events grid-project-card-list__actions"
>
<slot name="actions" /> <slot name="actions" />
</div> </div>
<div <div class="mt-auto flex flex-col gap-3 flex-wrap overflow-hidden justify-between grow">
class="flex flex-col gap-3 items-end shrink-0 ml-auto empty:hidden grid-project-card-list__stats" <div class="flex items-center gap-1 flex-wrap overflow-hidden">
:class="{ 'mt-3': !!$slots.actions }"
>
<div class="flex items-center gap-3">
<ProjectCardStats :downloads="downloads" :followers="followers" />
</div>
<ProjectCardDate v-if="date && autoDisplayDate" :type="autoDisplayDate" :date="date" />
</div>
<div class="mt-auto flex items-center gap-3 grid-project-card-list__tags">
<div class="flex items-center gap-1 flex-wrap">
<ProjectCardEnvironment <ProjectCardEnvironment
v-if="environment" v-if="environment"
:client-side="environment.clientSide" :client-side="environment.clientSide"
@@ -126,16 +55,82 @@
<ProjectCardTags <ProjectCardTags
v-if="tags" v-if="tags"
:tags="tags" :tags="tags"
:extra-tags="extraTags"
:exclude-loaders="excludeLoaders" :exclude-loaders="excludeLoaders"
:deprioritized-tags="deprioritizedTags" :deprioritized-tags="deprioritizedTags"
:max-tags="(!!$slots.actions ? 4 : 5) + (!!environment ? 0 : 1)" :max-tags="6 + (!!environment ? 0 : 1)"
/> />
</div> </div>
<div
v-if="downloads !== undefined || followers !== undefined"
class="flex items-center gap-3 justify-between flex-wrap"
>
<div class="flex items-center gap-3 no-wrap flex-wrap">
<ProjectCardStats :downloads="downloads" :followers="followers" />
</div>
<ProjectCardDate v-if="date && autoDisplayDate" :type="autoDisplayDate" :date="date" />
</div>
</div> </div>
</div> </div>
</SmartClickable> </div>
</div> <div
v-else
:class="[
baseCardStyle,
'p-4 grid grid-project-card-list gap-x-3 gap-y-2',
{ 'has-actions': !!$slots.actions },
]"
>
<Avatar
:src="iconUrl"
size="100px"
class="project-card__icon grid-project-card-list__icon"
no-shadow
/>
<div class="flex flex-col gap-2 grid-project-card-list__info">
<div class="flex gap-2 items-center">
<ProjectCardTitle :title="title" />
<ProjectCardAuthor v-if="author" :author="author" />
<ProjectStatusBadge v-if="status" :status="status" />
</div>
<div class="project-card-summary m-0 font-normal line-clamp-2">
{{ summary }}
</div>
</div>
<div
v-if="!!$slots.actions"
class="flex gap-1 shrink-0 ml-auto empty:hidden smart-clickable:allow-pointer-events grid-project-card-list__actions"
>
<slot name="actions" />
</div>
<div
class="flex flex-col gap-3 items-end shrink-0 ml-auto empty:hidden grid-project-card-list__stats"
:class="{ 'mt-3': !!$slots.actions }"
>
<div class="flex items-center gap-3">
<ProjectCardStats :downloads="downloads" :followers="followers" />
</div>
<ProjectCardDate v-if="date && autoDisplayDate" :type="autoDisplayDate" :date="date" />
</div>
<div class="mt-auto flex items-center gap-3 grid-project-card-list__tags">
<div class="flex items-center gap-1 flex-wrap">
<ProjectCardEnvironment
v-if="environment"
:client-side="environment.clientSide"
:server-side="environment.serverSide"
/>
<ProjectCardTags
v-if="tags"
:tags="tags"
:extra-tags="extraTags"
:exclude-loaders="excludeLoaders"
:deprioritized-tags="deprioritizedTags"
:max-tags="(!!$slots.actions ? 4 : 5) + (!!environment ? 0 : 1)"
/>
</div>
</div>
</div>
</SmartClickable>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@@ -20,6 +20,14 @@ const VERSIONS: VersionEntry[] = [
// - Adjusted pop-up design to include a border. // - Adjusted pop-up design to include a border.
// - Updated translations.`, // - Updated translations.`,
// }, // },
{
date: `2026-02-07T12:10:00-08:00`,
product: 'web',
body: `## Improvements
- Fixed grid project lists displaying too narrow sometimes.
- Fixed grid project list on user profiles not displaying project banners.
- Fixed grid project list cards not matching the height of their neighbor.`,
},
{ {
date: `2026-02-07T11:45:00-08:00`, date: `2026-02-07T11:45:00-08:00`,
product: 'web', product: 'web',