chore: pride month fundraiser conclusion (#6588)
* pride end blog post * Fix drago link * prepr oops
@@ -5,6 +5,10 @@
|
||||
<div class="pointer-events-none absolute inset-0 z-[-1]">
|
||||
<div id="absolute-background-teleport" class="relative"></div>
|
||||
</div>
|
||||
<div
|
||||
class="pride-backdrop pointer-events-none absolute inset-0 z-[-1]"
|
||||
:class="{ shown: showPrideBackdrop }"
|
||||
></div>
|
||||
<div class="pointer-events-none absolute inset-0 z-50">
|
||||
<div
|
||||
class="over-the-top-random-animation"
|
||||
@@ -801,6 +805,7 @@ import ProjectCreateModal from '~/components/ui/create/ProjectCreateModal.vue'
|
||||
import ModrinthFooter from '~/components/ui/ModrinthFooter.vue'
|
||||
import { getSignInRouteObj } from '~/composables/auth.ts'
|
||||
import { errors as generatedStateErrors } from '~/generated/state.json'
|
||||
import { provideCurrentProjectId } from '~/providers/current-project.ts'
|
||||
import { getProjectTypeMessage } from '~/utils/i18n-project-type.ts'
|
||||
import { hasActiveMidas } from '~/utils/user-membership.ts'
|
||||
|
||||
@@ -867,6 +872,32 @@ const showTinMismatchBanner = computed(() => {
|
||||
return !!auth.value.user && status === 'tin-mismatch'
|
||||
})
|
||||
|
||||
const PRIDE_COLLECTION_ID = 'M4c3ITvd'
|
||||
const PRIDE_ARTICLE_SLUGS = ['pride-campaign-2025', 'pride-campaign-2026', 'proud-of-you-2026']
|
||||
const PRIDE_CACHE_TIME = 1000 * 60 * 60 * 24
|
||||
|
||||
const { data: prideCollection } = useQuery({
|
||||
queryKey: computed(() => ['collection', PRIDE_COLLECTION_ID]),
|
||||
queryFn: () => client.labrinth.collections.get(PRIDE_COLLECTION_ID),
|
||||
staleTime: PRIDE_CACHE_TIME,
|
||||
gcTime: PRIDE_CACHE_TIME,
|
||||
})
|
||||
|
||||
const prideProjectIds = computed(() => new Set(prideCollection.value?.projects ?? []))
|
||||
|
||||
const currentProjectId = ref()
|
||||
provideCurrentProjectId(currentProjectId)
|
||||
|
||||
const showPrideBackdrop = computed(() => {
|
||||
if (PRIDE_ARTICLE_SLUGS.includes(route.params.slug)) {
|
||||
return true
|
||||
}
|
||||
if (route.params.collection === PRIDE_COLLECTION_ID) {
|
||||
return true
|
||||
}
|
||||
return !!currentProjectId.value && prideProjectIds.value.has(currentProjectId.value)
|
||||
})
|
||||
|
||||
const basePopoutId = useId()
|
||||
|
||||
async function fetchIntercomToken() {
|
||||
@@ -1695,4 +1726,21 @@ const { cycle: changeTheme } = useTheme()
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.pride-backdrop {
|
||||
background-image: linear-gradient(to right, #c20732, #f57203, #ffd632, #21ca8b, #2f9ff2, #e420fc);
|
||||
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0) 80%);
|
||||
height: 30rem;
|
||||
opacity: 0;
|
||||
transition: opacity 1s ease;
|
||||
}
|
||||
|
||||
.pride-backdrop.shown {
|
||||
opacity: 0.08;
|
||||
}
|
||||
|
||||
.light-mode .pride-backdrop.shown,
|
||||
.light .pride-backdrop.shown {
|
||||
opacity: 0.15;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1114,7 +1114,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import dayjs from 'dayjs'
|
||||
import { Tooltip } from 'floating-vue'
|
||||
import { nextTick, readonly, ref, useTemplateRef, watch } from 'vue'
|
||||
import { nextTick, onScopeDispose, readonly, ref, useTemplateRef, watch, watchEffect } from 'vue'
|
||||
|
||||
import { navigateTo } from '#app'
|
||||
import Accordion from '~/components/ui/Accordion.vue'
|
||||
@@ -1130,6 +1130,7 @@ import { saveFeatureFlags } from '~/composables/featureFlags.ts'
|
||||
import { STALE_TIME, STALE_TIME_LONG } from '~/composables/queries/project'
|
||||
import { versionQueryOptions } from '~/composables/queries/version'
|
||||
import { userCollectProject, userFollowProject } from '~/composables/user.js'
|
||||
import { injectCurrentProjectId } from '~/providers/current-project.ts'
|
||||
import {
|
||||
loadChecklistOpenState,
|
||||
saveChecklistOpenState,
|
||||
@@ -1705,6 +1706,16 @@ const project = computed(() => {
|
||||
// Use actual project ID for dependent queries (ensures cache consistency)
|
||||
const projectId = computed(() => projectRaw.value?.id)
|
||||
|
||||
const sharedProjectId = injectCurrentProjectId(null)
|
||||
if (sharedProjectId) {
|
||||
watchEffect(() => {
|
||||
sharedProjectId.value = projectId.value ?? undefined
|
||||
})
|
||||
onScopeDispose(() => {
|
||||
sharedProjectId.value = undefined
|
||||
})
|
||||
}
|
||||
|
||||
// V3 Project
|
||||
const {
|
||||
data: projectV3,
|
||||
|
||||
@@ -954,5 +954,9 @@ function openEditModal(event) {
|
||||
color: var(--color-brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { createContext } from '@modrinth/ui'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
/**
|
||||
* Informs the default layout of the current project ID, if any, because it can't be gleaned from the route which may be a slug
|
||||
*/
|
||||
export const [injectCurrentProjectId, provideCurrentProjectId] = createContext<
|
||||
Ref<string | undefined>
|
||||
>('root', 'currentProjectId')
|
||||
|
After Width: | Height: | Size: 777 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 138 KiB |
@@ -1,5 +1,12 @@
|
||||
{
|
||||
"articles": [
|
||||
{
|
||||
"title": "Proud of you all",
|
||||
"summary": "Over 2,400 of you came together to raise more than $40,000 for charity this Pride month!",
|
||||
"thumbnail": "https://modrinth.com/news/article/proud-of-you-2026/thumbnail.webp",
|
||||
"date": "2026-07-02T01:00:00.000Z",
|
||||
"link": "https://modrinth.com/news/article/proud-of-you-2026"
|
||||
},
|
||||
{
|
||||
"title": "Improving Modpack review delays",
|
||||
"summary": "Reducing the back-and-forth needed to get your modpack approved.",
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: Proud of you all
|
||||
summary: Over 2,400 of you came together to raise more than $40,000 for charity this Pride month!
|
||||
date: 2026-07-01T18:00:00-07:00
|
||||
authors: ['vNcGR3Fd', 'bOHH0P9Z', '2cqK8Q5p', 'VmRGDAOP', 'nHj8InZT', 'K86yjB3D']
|
||||
---
|
||||
|
||||
Earlier this month, we announced that we would be raising money for Rainbow Railroad throughout Pride Month.
|
||||
|
||||
When we created our campaign, we wanted to set a realistic goal, so we started at $5,000.
|
||||
|
||||
You hit it within 3 hours, before Pride Month had even begun. So we raised the goal to $10,000 - and less than an hour into June 1st (UTC), this was achieved.
|
||||
|
||||
We were completely astounded and, honestly, lost for words.
|
||||
|
||||
## Your Accomplishments
|
||||
|
||||

|
||||
|
||||
Together, you helped fund work that supports LGBTQIA+ people facing danger around the world. That can mean emergency support, temporary safe housing, travel documents, crisis response, local partner networks, and relocation pathways when staying where they are is no longer safe.
|
||||
|
||||

|
||||
|
||||
Thank you to all of our top donors!
|
||||
|
||||
By the end of the campaign, you raised over $40,000 for Rainbow Railroad. We thank all 2,478 who donated, no matter how big or small - you should all be incredibly proud.
|
||||
|
||||
Modrinth’s total made up nearly half of Rainbow Railroad’s entire total for their Solidarity in Pride event on Tiltify of $80,000. Absolutely incredible!
|
||||
|
||||
### Mr. Pack
|
||||
|
||||
Historically, Modrinth has had a Frog mascot for many years. It has appeared across our branding, social posts, Discord emotes, seasonal artwork, and community memes, but it never really had a proper name or identity.
|
||||
|
||||
Mr. Pack was created as a small thank-you to everyone who supported the fundraiser: a new mascot skin for the Modrinth App, with several Pride variants available to donors who donated more than $5. Mr. Pack is the 5th most popular skin on NameMC for June 2026 - over 4,000 players are using him and his variants!
|
||||
|
||||

|
||||
|
||||
_Special thanks to [Drago](https://x.com/iDragolyte) for creating Mr. Pack’s updated design and pride variants._
|
||||
|
||||
### Badges and Modrinth+
|
||||
|
||||
<img src="./pride-badge.webp" alt="Modrinth’s Pride badge over a rainbow background." width="150" height="150" style="float: right;margin-left: 1rem;" />
|
||||
|
||||
Everyone who donated to the fundraiser, regardless of the amount, received the limited Pride 2026 badge on their Modrinth profile as a thank you for supporting Rainbow Railroad. We also gave eligible donors a free month of Modrinth+ as an additional thank you - if you have this badge, you are a _very_ cool person.
|
||||
|
||||
### Pride Collection
|
||||
|
||||
This year, we launched our Pride Collection, an assortment of queer content hosted on Modrinth and submitted to us by our amazing community of creators.
|
||||
|
||||
<div id="pride-collection-widget"></div>
|
||||
|
||||
Thank you to everyone who submitted their projects; we look forward to adding even more content to this collection in the years to come!
|
||||
|
||||
## Pride Is All Year
|
||||
|
||||

|
||||
|
||||
Rainbow Railroad’s work continues all year round, and LGBTQIA+ people around the world continue to face danger simply for being who they are. This fundraiser was one way for us to turn Pride into practical support, but it cannot be the only time we show up. Pride month may be over, but the fight continues.
|
||||
|
||||
Modrinth is home to queer creators, players, developers, moderators, artists, and community members. They are part of what makes this platform what it is, and supporting them should not be limited to one month of the year.
|
||||
|
||||
If you’d still like to support this cause, you can donate to Rainbow Railroad at any time - and we encourage you to do so.
|
||||
|
||||
—
|
||||
|
||||
Thank you for what has been an incredible Pride month, and we look forward to doing even more next year. 💚
|
||||
@@ -30,6 +30,7 @@ import { article as new_site_beta } from "./new_site_beta";
|
||||
import { article as plugins_resource_packs } from "./plugins_resource_packs";
|
||||
import { article as pride_campaign_2025 } from "./pride_campaign_2025";
|
||||
import { article as pride_campaign_2026 } from "./pride_campaign_2026";
|
||||
import { article as proud_of_you_2026 } from "./proud_of_you_2026";
|
||||
import { article as redesign } from "./redesign";
|
||||
import { article as russian_censorship } from "./russian_censorship";
|
||||
import { article as server_access } from "./server_access";
|
||||
@@ -74,6 +75,7 @@ export const articles = [
|
||||
plugins_resource_packs,
|
||||
pride_campaign_2025,
|
||||
pride_campaign_2026,
|
||||
proud_of_you_2026,
|
||||
redesign,
|
||||
russian_censorship,
|
||||
server_access,
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const html = `<p>Earlier this month, we announced that we would be raising money for Rainbow Railroad throughout Pride Month.</p><p>When we created our campaign, we wanted to set a realistic goal, so we started at $5,000.</p><p>You hit it within 3 hours, before Pride Month had even begun. So we raised the goal to $10,000 - and less than an hour into June 1st (UTC), this was achieved.</p><p>We were completely astounded and, honestly, lost for words.</p><h2>Your Accomplishments</h2><p><img src="/news/article/proud-of-you-2026/stage.webp" alt="A Minecraft screenshot of a theater stage with a large Modrinth logo, featuring members of the Modrinth team. Mr. Pack is at the very front speaking into a microphone while a staff member leads a Ribbit Pride parade and another leads a bee Pride parade."></p><p>Together, you helped fund work that supports LGBTQIA+ people facing danger around the world. That can mean emergency support, temporary safe housing, travel documents, crisis response, local partner networks, and relocation pathways when staying where they are is no longer safe.</p><p><img src="/news/article/proud-of-you-2026/donors.webp" alt="A screenshot showing the top 10 donors for Modrinth’s 2026 Pride fundraiser."></p><p>Thank you to all of our top donors!</p><p>By the end of the campaign, you raised over $40,000 for Rainbow Railroad. We thank all 2,478 who donated, no matter how big or small - you should all be incredibly proud.</p><p>Modrinth’s total made up nearly half of Rainbow Railroad’s entire total for their Solidarity in Pride event on Tiltify of $80,000. Absolutely incredible!</p><h3>Mr. Pack</h3><p>Historically, Modrinth has had a Frog mascot for many years. It has appeared across our branding, social posts, Discord emotes, seasonal artwork, and community memes, but it never really had a proper name or identity.</p><p>Mr. Pack was created as a small thank-you to everyone who supported the fundraiser: a new mascot skin for the Modrinth App, with several Pride variants available to donors who donated more than $5. Mr. Pack is the 5th most popular skin on NameMC for June 2026 - over 4,000 players are using him and his variants!</p><p><img src="/news/article/proud-of-you-2026/pride-skins.webp" alt="All of Mr. Pack’s variants (MLM, Genderfluid, Lesbian, Transgender, Asexual, Intersex, Pride, Bisexual, and Nonbinary) posed and lined up in a row."></p><p><em>Special thanks to <a href="https://x.com/iDragolyte" rel="noopener nofollow ugc">Drago</a> for creating Mr. Pack’s updated design and pride variants.</em></p><h3>Badges and Modrinth+</h3><img src="./pride-badge.webp" alt="Modrinth’s Pride badge over a rainbow background." width="150" height="150" style="float: right;margin-left: 1rem;"><p>Everyone who donated to the fundraiser, regardless of the amount, received the limited Pride 2026 badge on their Modrinth profile as a thank you for supporting Rainbow Railroad. We also gave eligible donors a free month of Modrinth+ as an additional thank you - if you have this badge, you are a <em>very</em> cool person.</p><h3>Pride Collection</h3><p>This year, we launched our Pride Collection, an assortment of queer content hosted on Modrinth and submitted to us by our amazing community of creators.</p><div id="pride-collection-widget"></div><p>Thank you to everyone who submitted their projects; we look forward to adding even more content to this collection in the years to come!</p><h2>Pride Is All Year</h2><p><img src="/news/article/proud-of-you-2026/audience.webp" alt="A Minecraft screenshot of a theater featuring Modrinth team and community members in the audience. There are also many pride flags along the sides of the room."></p><p>Rainbow Railroad’s work continues all year round, and LGBTQIA+ people around the world continue to face danger simply for being who they are. This fundraiser was one way for us to turn Pride into practical support, but it cannot be the only time we show up. Pride month may be over, but the fight continues.</p><p>Modrinth is home to queer creators, players, developers, moderators, artists, and community members. They are part of what makes this platform what it is, and supporting them should not be limited to one month of the year.</p><p>If you’d still like to support this cause, you can donate to Rainbow Railroad at any time - and we encourage you to do so.</p><p>—</p><p>Thank you for what has been an incredible Pride month, and we look forward to doing even more next year. 💚</p>`;
|
||||
@@ -0,0 +1,12 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./proud_of_you_2026.content`).then(m => m.html),
|
||||
title: "Proud of you all",
|
||||
summary: "Over 2,400 of you came together to raise more than $40,000 for charity this Pride month!",
|
||||
date: "2026-07-02T01:00:00.000Z",
|
||||
slug: "proud-of-you-2026",
|
||||
authors: ["vNcGR3Fd","bOHH0P9Z","2cqK8Q5p","VmRGDAOP","nHj8InZT","K86yjB3D"],
|
||||
unlisted: false,
|
||||
thumbnail: true,
|
||||
|
||||
};
|
||||
|
After Width: | Height: | Size: 777 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 138 KiB |
@@ -1,12 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { type Component, computed } from 'vue'
|
||||
|
||||
import PrideCollectionWidget from './PrideCollectionWidget.vue'
|
||||
import SparkLiveWidget from './SparkLiveWidget.vue'
|
||||
import SparkLiveWidgetEmbed from './SparkLiveWidgetEmbed.vue'
|
||||
|
||||
const ARTICLE_WIDGETS: Record<string, Component> = {
|
||||
'spark-live-widget': SparkLiveWidget,
|
||||
'spark-live-widget-embed': SparkLiveWidgetEmbed,
|
||||
'pride-collection-widget': PrideCollectionWidget,
|
||||
}
|
||||
|
||||
type ArticleBodyPart = { type: 'html'; content: string } | { type: 'widget'; id: string }
|
||||
@@ -50,9 +52,9 @@ const parts = computed(() => parseArticleHtml(props.html))
|
||||
class="markdown-body"
|
||||
v-html="parts[0]?.content"
|
||||
/>
|
||||
<div v-else class="markdown-body">
|
||||
<div v-else class="flex flex-col gap-4">
|
||||
<template v-for="(part, index) in parts" :key="index">
|
||||
<div v-if="part.type === 'html'" v-html="part.content" />
|
||||
<div v-if="part.type === 'html'" class="markdown-body" v-html="part.content" />
|
||||
<component :is="ARTICLE_WIDGETS[part.id]" v-else />
|
||||
</template>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
<script setup lang="ts">
|
||||
import { DownloadIcon, HeartIcon, SearchIcon } from '@modrinth/assets'
|
||||
import { renderString } from '@modrinth/utils'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import { useCompactNumber } from '#ui/composables/format-number.ts'
|
||||
|
||||
import { defineMessages, useVIntl } from '../../composables/i18n'
|
||||
import { injectModrinthClient } from '../../providers/api-client'
|
||||
import AutoLink from '../base/AutoLink.vue'
|
||||
import Avatar from '../base/Avatar.vue'
|
||||
import StyledInput from '../base/StyledInput.vue'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const { formatCompactNumber } = useCompactNumber()
|
||||
|
||||
const props = defineProps<{
|
||||
collectionId: string
|
||||
}>()
|
||||
|
||||
const client = injectModrinthClient()
|
||||
|
||||
const { data: collection, isLoading: isLoadingCollection } = useQuery({
|
||||
queryKey: ['collection', () => props.collectionId],
|
||||
queryFn: () => client.labrinth.collections.get(props.collectionId),
|
||||
enabled: computed(() => !!props.collectionId),
|
||||
})
|
||||
|
||||
const projectIds = computed(() => collection.value?.projects ?? [])
|
||||
|
||||
const { data: projects, isLoading: isLoadingProjects } = useQuery({
|
||||
queryKey: ['collection-projects', () => projectIds.value],
|
||||
queryFn: () => client.labrinth.projects_v3.getMultiple(projectIds.value),
|
||||
enabled: computed(() => projectIds.value.length > 0),
|
||||
})
|
||||
|
||||
const query = ref<string>('')
|
||||
|
||||
const sortedProjects = computed(
|
||||
() =>
|
||||
projects.value
|
||||
?.slice()
|
||||
.sort((a, b) => b.followers - a.followers)
|
||||
.filter((project) => project.name.toLowerCase().includes(query.value.toLowerCase())) ?? [],
|
||||
)
|
||||
|
||||
const supportsMarkdown = computed(() => collection.value?.user === '2REoufqX')
|
||||
|
||||
const messages = defineMessages({
|
||||
projectCount: {
|
||||
id: 'collection-widget.project-count',
|
||||
defaultMessage: '{count, plural, one {# project} other {# projects}}',
|
||||
},
|
||||
searchPlaceholder: {
|
||||
id: 'collection-widget.search-placeholder',
|
||||
defaultMessage: 'Search projects',
|
||||
},
|
||||
loadingProjects: {
|
||||
id: 'collection-widget.loading-projects',
|
||||
defaultMessage: 'Loading projects...',
|
||||
},
|
||||
emptyCollection: {
|
||||
id: 'collection-widget.empty-collection',
|
||||
defaultMessage: 'This collection is empty.',
|
||||
},
|
||||
noSearchResults: {
|
||||
id: 'collection-widget.no-search-results',
|
||||
defaultMessage: 'No projects match your search.',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="rounded-2xl border border-solid border-surface-4 bg-surface-3 overflow-hidden grid grid-cols-[2fr_4fr]"
|
||||
>
|
||||
<div class="flex flex-col gap-2 p-4">
|
||||
<template v-if="isLoadingCollection">
|
||||
<div class="size-[96px] rounded-[16px] bg-surface-4 animate-pulse"></div>
|
||||
<div class="w-52 h-8 rounded-full bg-surface-4 animate-pulse"></div>
|
||||
<div class="w-28 h-5 rounded-full bg-surface-4 animate-pulse"></div>
|
||||
<div class="w-44 h-5 rounded-full bg-surface-4 animate-pulse"></div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<AutoLink
|
||||
:to="`/collection/${collection?.id}`"
|
||||
class="flex flex-col gap-2 hover:underline"
|
||||
target="_blank"
|
||||
>
|
||||
<Avatar :src="collection?.icon_url" size="96px" />
|
||||
<span class="text-contrast font-semibold text-xl">{{ collection?.name }}</span>
|
||||
</AutoLink>
|
||||
<span>
|
||||
{{ formatMessage(messages.projectCount, { count: collection?.projects.length ?? 0 }) }}
|
||||
</span>
|
||||
<div
|
||||
v-if="supportsMarkdown"
|
||||
class="description-body"
|
||||
v-html="renderString(collection?.description ?? '')"
|
||||
/>
|
||||
<p v-else class="m-0 break-words">{{ collection?.description }}</p>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col bg-surface-2 border-0 border-l border-solid border-surface-4 overflow-hidden"
|
||||
>
|
||||
<StyledInput
|
||||
v-if="(projects?.length ?? 0) > 5"
|
||||
v-model="query"
|
||||
:placeholder="formatMessage(messages.searchPlaceholder)"
|
||||
input-class="bg-transparent !rounded-l-none !rounded-b-none m-1"
|
||||
:icon="SearchIcon"
|
||||
:disabled="isLoadingCollection || isLoadingProjects"
|
||||
/>
|
||||
<div
|
||||
class="flex flex-col h-[20rem] overflow-y-auto"
|
||||
:class="{ 'border-0 border-t border-solid border-surface-4': (projects?.length ?? 0) > 5 }"
|
||||
>
|
||||
<span v-if="isLoadingProjects" class="w-full py-12 text-center">
|
||||
{{ formatMessage(messages.loadingProjects) }}
|
||||
</span>
|
||||
<template v-else>
|
||||
<AutoLink
|
||||
v-for="project in sortedProjects"
|
||||
:key="project.id"
|
||||
:to="`/project/${project.id}`"
|
||||
class="flex items-center gap-2 px-3 py-2 even:bg-surface-2.5 hover:bg-surface-4 group"
|
||||
target="_blank"
|
||||
>
|
||||
<Avatar :src="project.icon_url" size="48px" />
|
||||
<div class="flex flex-col gap-1 truncate">
|
||||
<span class="flex items-center gap-2">
|
||||
<span class="text-contrast font-medium text-base group-hover:underline">
|
||||
{{ project.name }}
|
||||
</span>
|
||||
<span class="flex items-center gap-1 text-sm">
|
||||
<DownloadIcon class="size-4 text-secondary" />
|
||||
{{ formatCompactNumber(project.downloads) }}
|
||||
</span>
|
||||
<span class="flex items-center gap-1 text-sm">
|
||||
<HeartIcon class="size-4 text-secondary" />
|
||||
{{ formatCompactNumber(project.followers) }}
|
||||
</span>
|
||||
</span>
|
||||
<span class="text-primary text-sm truncate">{{ project.summary }}</span>
|
||||
</div>
|
||||
</AutoLink>
|
||||
<span v-if="projects?.length === 0" class="w-full py-12 text-center">
|
||||
{{ formatMessage(messages.emptyCollection) }}
|
||||
</span>
|
||||
<span v-else-if="sortedProjects.length === 0" class="w-full py-12 text-center">
|
||||
{{ formatMessage(messages.noSearchResults) }}
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
:deep(.description-body) {
|
||||
p {
|
||||
margin: 0;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import CollectionWidget from './CollectionWidget.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<CollectionWidget collection-id="M4c3ITvd" />
|
||||
</ClientOnly>
|
||||
</template>
|
||||
@@ -317,6 +317,21 @@
|
||||
"changelog.product.web": {
|
||||
"defaultMessage": "Platform"
|
||||
},
|
||||
"collection-widget.empty-collection": {
|
||||
"defaultMessage": "This collection is empty."
|
||||
},
|
||||
"collection-widget.loading-projects": {
|
||||
"defaultMessage": "Loading projects..."
|
||||
},
|
||||
"collection-widget.no-search-results": {
|
||||
"defaultMessage": "No projects match your search."
|
||||
},
|
||||
"collection-widget.project-count": {
|
||||
"defaultMessage": "{count, plural, one {# project} other {# projects}}"
|
||||
},
|
||||
"collection-widget.search-placeholder": {
|
||||
"defaultMessage": "Search projects"
|
||||
},
|
||||
"collections.label.private": {
|
||||
"defaultMessage": "Private"
|
||||
},
|
||||
|
||||