Modrinth/components/ui/ProjectCard.vue
Geometrically 20785926e2
Rewrite Parity (#647)
* Rewrite Parity

* Update SEO, fix modals, add dashes to changelog

* Edit create version title

* Cache tags, SEO for search/partial noscript support, notifications fix

* Deploy?

* Fix vercel config

* Fix it again

* Finish user editing

* Remove broken docker build

* Switch reports to modals

* Update project card

* Navbar line animation in most places

* Add chips

* Move to navlink query params

* remove autogen file

* Add copy code

* Fix webkit text box outlines, port report modal

* Update error page

* Switch to avatar component

* Make keyboard nav work

* Fix team member spacing

* improve project ID display (#676)

* Bug fixes

* Update OG site title

* More fixes

* Design tweaks

* Fix card wrapping on mobile

* Darken light theme color a little

* Sidebar navigation for settings, notifications, and moderation

* Change follow icon from a heart to a bell

* Revert "Change follow icon from a heart to a bell"

This reverts commit e30b46ec5d93c57df847be88eba123c7419dd03b.

* Change follows icon in settings

* AaaaUUUUUUUGghghhhhhhhh

* Project sidebar transparent button animations

* Update file input button styling and change icon remove button text

* Fix environments filter condition being inverted

* Remove -> revert

* Improve readability of warning banners on light mode

* Fix mobile menu button colors

* Clean up notifications page more

* Creator dashboard and monetization work

* Add processing fees declarations and acknowledgement box

* Beta badges

* Downgrade Nuxt Vercel Builder

* Update the style of button groups to be more consistent

* More button consistency

* Remove desktop navbar on mobile

* Update home page progress indicators

* Fix page jumping (Thanks @stairman06)

* Make checkbox checked style consistent with other selection indicators

* More home page updates

* Properly reset NavRows

* Move filters menu on mobile

* Stylized checkbox updated to match active styling

* Filters icon

* Respect prefers-reduced-motion

* Add most backend payouts changes (untested)

* Finish tested payouts code

* Allow monetization unenrolling

* No longer use brand color for active highlights on standard nav elements

* More consistent button group on project page

* Rounded tables

* Fix some things (#716)

* Team member fixes + re-add changelog/versions stuff

* Remove dummy data

* The great CSS refactor

* Remove commented out css

* Give modals the legacy label styles and update profile edit labels

* Fix active chip size

* Remove shadow from selected chip

* Require email set for CMP

* Update styles of notifications to universal-card

* Equivalent exchange, trading some jank for some less bad jank

* Fix all gallery buttons being missing when there is only 1 image

* Update project creation modal

* Make beta badge less bright

* Beta badge heading styling

* Update withdraw processing fees info

* Remove redundant label

* be

* Fix inverted logic

* 2% is 0.02

* Add toggle to turn off alpha modpacks banner

* Why warning button?

* Add more footer links (#719)

* Add more footer links

* Move twitter

* Make items on user pages less comically large and move ad above navigation

* Bump text down a little on home page

* Update favicon colors

* Remove task list package and change default description to use bullet points

* I don't remember why I made this important but let's not

* Ah, yes

* this doesn't actually need to be important

* Align items in input groups

* Adjust some spacings and clear creation modal on opening

* Versions now clickable

* Add link to edit page to default description

* Improve monetization information text

* Make wrapped text inputs not shrink

* Make chips work better

* smol margin on clear mod message button

* Allow non-authenticated users to access settings

* Remove settings anchors

* Fix versions page button style on firefox

* Add advanced rendering toggle

* Update slug input and icon card in project edit page

* Legal sidebar

* h1 at beginning of description no longer has top margin

* Use universal card for legal pages

* Update email addresses on legal pages

* Update various page titles and descriptions for consistency

* Various fixes and consolidation to API URL retrieval

Prevents a bug where it's possible to generate the tags under one API, switch the API, and still have tags leftover from the old API

Also finally fixes staging URL being jank

* Make the theme button show regardless of login state

Also remove the change theme from the user dropdown because it's very redundant with the several other ways of changing theme

* Make mobile profile dropdown ordering consistent with desktop

* Change the base url back

* Revert "Change the base url back"

This reverts commit c1da89fddb83776b39f626eab33c8dc67f8a75e4.

* constantize

* Tiny fixes (#722)

* Box-shadow chip outlines

* Show settings when signed out

* mods -> projects

* space

* Beta badge border

* Slug input overflow fix, scrollable

* 🙈 it will all be okay 🙊 this is just temporary 🙉 😭😭 forgive me

* Fix minor bugs

* fix moderation  page

* More fixes

* Temp fix for download button

* BEGONE TABLES

* Fix download button

Co-authored-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com>
Co-authored-by: Prospector <prospectordev@gmail.com>
Co-authored-by: stairman06 <36215135+stairman06@users.noreply.github.com>
Co-authored-by: triphora <emmaffle@modrinth.com>
2022-11-12 17:57:40 -07:00

478 lines
10 KiB
Vue

<template>
<article class="project-card card" :aria-label="name" role="listitem">
<div class="columns">
<div class="icon">
<nuxt-link :to="`/${$getProjectTypeForUrl(type, categories)}/${id}`">
<Avatar :src="iconUrl" :alt="name" size="md" />
</nuxt-link>
</div>
<div class="card-content">
<div class="info">
<div class="top">
<h2 class="title">
<nuxt-link
:to="`/${$getProjectTypeForUrl(type, categories)}/${id}`"
>{{ name }}</nuxt-link
>
</h2>
<p v-if="author" class="author">
by
<nuxt-link class="title-link" :to="'/user/' + author"
>{{ author }}
</nuxt-link>
</p>
</div>
<div
v-if="
type !== 'resourcepack' &&
!(projectTypeDisplay === 'plugin' && search)
"
class="side-type"
>
<div
v-if="clientSide === 'optional' && serverSide === 'optional'"
class="side-descriptor"
>
<InfoIcon aria-hidden="true" />
Universal {{ projectTypeDisplay }}
</div>
<div
v-else-if="
(clientSide === 'optional' || clientSide === 'required') &&
(serverSide === 'optional' || serverSide === 'unsupported')
"
class="side-descriptor"
>
<InfoIcon aria-hidden="true" />
Client {{ projectTypeDisplay }}
</div>
<div
v-else-if="
(serverSide === 'optional' || serverSide === 'required') &&
(clientSide === 'optional' || clientSide === 'unsupported')
"
class="side-descriptor"
>
<InfoIcon aria-hidden="true" />
Server {{ projectTypeDisplay }}
</div>
<div v-else-if="moderation" class="side-descriptor">
<InfoIcon aria-hidden="true" />
A {{ projectTypeDisplay }}
</div>
</div>
<div v-else-if="moderation" class="side-descriptor">
<InfoIcon aria-hidden="true" />
A {{ projectTypeDisplay }}
</div>
<p class="description">
{{ description }}
</p>
<Categories
:categories="categories"
:type="type"
class="right-categories"
/>
<div class="dates">
<div
v-tooltip="
$dayjs(createdAt).format('MMMM D, YYYY [at] h:mm:ss A')
"
class="date"
>
<CalendarIcon aria-hidden="true" />
Created {{ $dayjs(createdAt).fromNow() }}
</div>
<div
v-tooltip="
$dayjs(updatedAt).format('MMMM D, YYYY [at] h:mm:ss A')
"
class="date"
>
<EditIcon aria-hidden="true" />
Updated {{ $dayjs(updatedAt).fromNow() }}
</div>
</div>
</div>
</div>
</div>
<div class="right-side">
<div v-if="downloads" class="stat">
<DownloadIcon aria-hidden="true" />
<p>
<strong>{{ $formatNumber(downloads) }}</strong> download<span
v-if="downloads !== '1'"
>s</span
>
</p>
</div>
<div v-if="follows" class="stat">
<HeartIcon aria-hidden="true" />
<p>
<strong>{{ $formatNumber(follows) }}</strong> follower<span
v-if="follows !== '1'"
>s</span
>
</p>
</div>
<div class="mobile-dates">
<div class="date">
<CalendarIcon aria-hidden="true" />
Created {{ $dayjs(createdAt).fromNow() }}
</div>
<div class="date">
<EditIcon aria-hidden="true" />
Updated {{ $dayjs(updatedAt).fromNow() }}
</div>
</div>
<div v-if="status" class="status">
<Badge
v-if="status === 'approved'"
color="green custom-circle"
:type="status"
/>
<Badge
v-else-if="status === 'processing' || status === 'archived'"
color="yellow custom-circle"
:type="status"
/>
<Badge
v-else-if="status === 'rejected'"
color="red custom-circle"
:type="status"
/>
<Badge v-else color="gray custom-circle" :type="status" />
</div>
<div class="buttons">
<slot />
</div>
</div>
</article>
</template>
<script>
import Categories from '~/components/ui/search/Categories'
import Badge from '~/components/ui/Badge'
import InfoIcon from '~/assets/images/utils/info.svg?inline'
import CalendarIcon from '~/assets/images/utils/calendar.svg?inline'
import EditIcon from '~/assets/images/utils/updated.svg?inline'
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
import HeartIcon from '~/assets/images/utils/heart.svg?inline'
import Avatar from '~/components/ui/Avatar'
export default {
name: 'ProjectCard',
components: {
Avatar,
Categories,
Badge,
InfoIcon,
CalendarIcon,
EditIcon,
DownloadIcon,
HeartIcon,
},
props: {
id: {
type: String,
default: 'modrinth-0',
},
type: {
type: String,
default: 'mod',
},
name: {
type: String,
default: 'Project Name',
},
author: {
type: String,
default: null,
},
description: {
type: String,
default: 'A _type description',
},
iconUrl: {
type: String,
default: '#',
required: false,
},
downloads: {
type: String,
default: null,
required: false,
},
follows: {
type: String,
default: null,
required: false,
},
createdAt: {
type: String,
default: '0000-00-00',
},
updatedAt: {
type: String,
default: null,
},
categories: {
type: Array,
default() {
return []
},
},
status: {
type: String,
default: null,
},
serverSide: {
type: String,
required: false,
default: '',
},
clientSide: {
type: String,
required: false,
default: '',
},
moderation: {
type: Boolean,
required: false,
default: false,
},
search: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
projectTypeDisplay() {
return this.$getProjectTypeForDisplay(this.type, this.categories)
},
},
}
</script>
<style lang="scss" scoped>
.columns {
width: 100%;
}
.project-card {
display: flex;
flex-direction: row;
padding: var(--spacing-card-bg);
width: calc(100% - 2 * var(--spacing-card-bg));
overflow: hidden;
@media screen and (min-width: 1024px) {
flex-direction: row;
justify-content: space-between;
}
.icon {
margin: 0 var(--spacing-card-md) var(--spacing-card-md) 0;
}
.card-content {
display: flex;
justify-content: space-between;
flex-grow: 1;
overflow: hidden;
.info {
display: flex;
flex-direction: column;
.top {
align-items: baseline;
display: flex;
flex-direction: row;
flex-wrap: wrap;
flex-shrink: 0;
margin-right: var(--spacing-card-md);
.title {
margin: 0 0.5rem 0 0;
overflow-wrap: anywhere;
color: var(--color-text-dark);
font-size: var(--font-size-xl);
word-wrap: break-word;
}
.author {
margin: auto 0 0 0;
color: var(--color-text);
line-break: anywhere;
}
}
.side-descriptor {
display: flex;
align-items: center;
font-weight: bolder;
font-size: var(--font-size-sm);
margin: 0.125rem 0;
svg {
width: auto;
height: 1rem;
margin-right: 0.125rem;
}
}
.description {
margin: var(--spacing-card-sm) var(--spacing-card-md)
var(--spacing-card-sm) 0;
}
.right-categories {
margin-bottom: var(--spacing-card-sm);
}
.dates {
display: flex;
flex-wrap: wrap;
.date {
display: flex;
align-items: center;
margin-right: 2rem;
cursor: default;
svg {
width: 1.25rem;
height: 1.25rem;
margin-right: 0.25rem;
}
}
}
}
}
.right-side {
min-width: fit-content;
.stat {
display: flex;
align-items: center;
margin-bottom: 0.5rem;
svg {
width: auto;
height: 1.25rem;
margin-left: auto;
margin-right: 0.25rem;
}
p {
margin: 0;
strong {
font-weight: bolder;
font-size: var(--font-size-lg);
}
}
}
.status {
margin-bottom: 0.5rem;
}
.buttons {
display: flex;
flex-direction: column;
button,
a {
margin-right: 0;
margin-left: auto;
margin-bottom: 0.5rem;
&:last-child {
margin-bottom: 0;
}
}
}
.mobile-dates {
display: none;
}
}
@media screen and (max-width: 800px) {
flex-wrap: wrap;
.card-content {
flex-direction: column;
.info {
.top {
flex-direction: column;
}
.dates {
display: none;
}
}
}
.right-side {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
align-items: center;
text-align: left;
.stat {
margin-bottom: 0;
}
.stat svg {
margin-left: 0;
}
.buttons {
flex: 1 1 100%;
}
.buttons button,
a {
margin-left: unset;
margin-right: unset;
}
.status {
margin-bottom: 0;
}
.mobile-dates {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 0.5rem;
color: var(--color-icon);
font-size: var(--font-size-nm);
.date {
display: flex;
align-items: center;
cursor: default;
svg {
width: 1rem;
height: 1rem;
margin-right: 0.25rem;
}
}
}
}
}
}
</style>