You've already forked AstralRinth
forked from didirus/AstralRinth
Add gallery view to search pages (#773)
This commit is contained in:
6
assets/images/utils/globe.svg
Normal file
6
assets/images/utils/globe.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="2" y1="12" x2="22" y2="12"></line>
|
||||
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 390 B |
7
assets/images/utils/grid.svg
Normal file
7
assets/images/utils/grid.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="7" height="7"></rect>
|
||||
<rect x="14" y="3" width="7" height="7"></rect>
|
||||
<rect x="14" y="14" width="7" height="7"></rect>
|
||||
<rect x="3" y="14" width="7" height="7"></rect>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 389 B |
6
assets/images/utils/image.svg
Normal file
6
assets/images/utils/image.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||
<circle cx="9" cy="9" r="2"></circle>
|
||||
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"></path>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 356 B |
@@ -433,27 +433,31 @@ tr.button-transparent {
|
||||
}
|
||||
}
|
||||
|
||||
.iconified-button {
|
||||
@extend .button-base;
|
||||
|
||||
.button-color-base {
|
||||
box-sizing: border-box;
|
||||
--text-color: var(--color-button-text);
|
||||
--background-color: var(--color-button-bg);
|
||||
|
||||
box-sizing: border-box;
|
||||
color: var(--text-color);
|
||||
background-color: var(--background-color);
|
||||
box-shadow: var(--shadow-inset-sm), 0 0 0 0 transparent;
|
||||
border-radius: var(--size-rounded-sm);
|
||||
}
|
||||
|
||||
.iconified-button {
|
||||
@extend .button-base;
|
||||
@extend .button-color-base;
|
||||
|
||||
display: flex;
|
||||
padding: var(--spacing-card-sm) var(--spacing-card-bg);
|
||||
margin: 0;
|
||||
font-size: var(--font-size-nm);
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
box-shadow: var(--shadow-inset-sm), 0 0 0 0 transparent;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
|
||||
color: var(--text-color);
|
||||
background-color: var(--background-color);
|
||||
text-decoration: none;
|
||||
border-radius: var(--size-rounded-sm);
|
||||
|
||||
svg {
|
||||
width: 1.1rem;
|
||||
@@ -479,6 +483,30 @@ tr.button-transparent {
|
||||
}
|
||||
}
|
||||
|
||||
.square-button {
|
||||
@extend .button-base;
|
||||
|
||||
--text-color: var(--color-button-text);
|
||||
--background-color: var(--color-button-bg);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 2.25rem;
|
||||
width: 2.25rem;
|
||||
border-radius: var(--size-rounded-sm);
|
||||
color: var(--text-color);
|
||||
background-color: var(--background-color);
|
||||
box-shadow: var(--shadow-inset-sm), 0 0 0 0 transparent;
|
||||
|
||||
svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header-button {
|
||||
@extend .iconified-button;
|
||||
|
||||
@@ -691,27 +719,6 @@ tr.button-transparent {
|
||||
}
|
||||
}
|
||||
|
||||
.download-button {
|
||||
@extend .button-base;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 2.25rem;
|
||||
width: 2.25rem;
|
||||
border-radius: var(--size-rounded-sm);
|
||||
color: var(--color-brand-inverted);
|
||||
background-color: var(--color-brand);
|
||||
margin-right: var(--spacing-card-sm);
|
||||
|
||||
svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.textarea-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1002,6 +1009,10 @@ h1 {
|
||||
padding: var(--spacing-card-lg);
|
||||
}
|
||||
|
||||
.padding-bg {
|
||||
padding: var(--spacing-card-bg);
|
||||
}
|
||||
|
||||
.padding-md {
|
||||
padding: var(--spacing-card-md);
|
||||
}
|
||||
@@ -1014,6 +1025,10 @@ h1 {
|
||||
padding-block: var(--spacing-card-lg);
|
||||
}
|
||||
|
||||
.padding-block-bg {
|
||||
padding-block: var(--spacing-card-bg);
|
||||
}
|
||||
|
||||
.padding-block-md {
|
||||
padding-block: var(--spacing-card-md);
|
||||
}
|
||||
@@ -1026,6 +1041,10 @@ h1 {
|
||||
padding-inline: var(--spacing-card-lg);
|
||||
}
|
||||
|
||||
.padding-inline-bg {
|
||||
padding-inline: var(--spacing-card-bg);
|
||||
}
|
||||
|
||||
.padding-inline-md {
|
||||
padding-inline: var(--spacing-card-md);
|
||||
}
|
||||
@@ -1278,3 +1297,52 @@ button {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.project-list {
|
||||
width: 100%;
|
||||
gap: var(--spacing-card-md);
|
||||
overflow: hidden;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: var(--spacing-card-md);
|
||||
}
|
||||
|
||||
&:not(:empty) {
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
}
|
||||
}
|
||||
|
||||
.project-list.display-mode--list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.project-list.display-mode--gallery {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
|
||||
@media screen and (max-width: 750px) {
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.project-list.display-mode--grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
|
||||
@media screen and (max-width: 80rem) {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
@media screen and (max-width: 860px) {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,144 +1,141 @@
|
||||
<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}`"
|
||||
>
|
||||
<IssuesIcon
|
||||
v-if="hasModMessage"
|
||||
v-tooltip="
|
||||
'Project has a message from the moderators. View the project to see more.'
|
||||
"
|
||||
aria-label="Project has a message from the moderators. View the project to see more."
|
||||
/>
|
||||
{{ 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)
|
||||
<article
|
||||
class="project-card base-card padding-bg"
|
||||
:aria-label="name"
|
||||
role="listitem"
|
||||
>
|
||||
<nuxt-link
|
||||
class="icon"
|
||||
tabindex="-1"
|
||||
:to="`/${$getProjectTypeForUrl(type, categories)}/${id}`"
|
||||
>
|
||||
<Avatar :src="iconUrl" :alt="name" size="md" />
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
class="gallery"
|
||||
tabindex="-1"
|
||||
:to="`/${$getProjectTypeForUrl(type, categories)}/${id}`"
|
||||
>
|
||||
<img
|
||||
v-if="galleryImages.length > 0"
|
||||
:src="galleryImages[0]"
|
||||
alt="Gallery image TODO: improve this lol"
|
||||
/>
|
||||
</nuxt-link>
|
||||
<div class="title">
|
||||
<nuxt-link :to="`/${$getProjectTypeForUrl(type, categories)}/${id}`">
|
||||
<h2 class="name">
|
||||
<IssuesIcon
|
||||
v-if="hasModMessage"
|
||||
v-tooltip="
|
||||
'Project has a message from the moderators. View the project to see more.'
|
||||
"
|
||||
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"
|
||||
aria-label="Project has a message from the moderators. View the project to see more."
|
||||
/>
|
||||
<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>
|
||||
{{ name }}
|
||||
</h2>
|
||||
</nuxt-link>
|
||||
<p v-if="author" class="author">
|
||||
by
|
||||
<nuxt-link class="title-link" :to="'/user/' + author"
|
||||
>{{ author }}
|
||||
</nuxt-link>
|
||||
</p>
|
||||
<Badge
|
||||
v-if="status && status !== 'approved'"
|
||||
:type="status"
|
||||
class="status"
|
||||
/>
|
||||
</div>
|
||||
<div class="right-side">
|
||||
<p class="description">
|
||||
{{ description }}
|
||||
</p>
|
||||
<Categories :categories="categories" :type="type" class="tags">
|
||||
<span v-if="moderation" class="environment">
|
||||
<InfoIcon aria-hidden="true" />
|
||||
A {{ projectTypeDisplay }}
|
||||
</span>
|
||||
<span
|
||||
v-else-if="
|
||||
type !== 'resourcepack' &&
|
||||
!(projectTypeDisplay === 'plugin' && search)
|
||||
"
|
||||
class="environment"
|
||||
>
|
||||
<template v-if="clientSide === 'optional' && serverSide === 'optional'">
|
||||
<GlobeIcon aria-hidden="true" />
|
||||
Client or server
|
||||
</template>
|
||||
<template
|
||||
v-else-if="clientSide === 'required' && serverSide === 'required'"
|
||||
>
|
||||
<GlobeIcon aria-hidden="true" />
|
||||
Client and server
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
(clientSide === 'optional' || clientSide === 'required') &&
|
||||
(serverSide === 'optional' || serverSide === 'unsupported')
|
||||
"
|
||||
>
|
||||
<ClientIcon aria-hidden="true" />
|
||||
Client
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
(serverSide === 'optional' || serverSide === 'required') &&
|
||||
(clientSide === 'optional' || clientSide === 'unsupported')
|
||||
"
|
||||
>
|
||||
<ServerIcon aria-hidden="true" />
|
||||
Server
|
||||
</template>
|
||||
<template v-else-if="moderation">
|
||||
<InfoIcon aria-hidden="true" />
|
||||
A {{ projectTypeDisplay }}
|
||||
</template>
|
||||
</span>
|
||||
</Categories>
|
||||
<div class="stats">
|
||||
<div v-if="downloads" class="stat">
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
<p>
|
||||
<strong>{{ $formatNumber(downloads) }}</strong> download<span
|
||||
v-if="downloads !== '1'"
|
||||
>s</span
|
||||
<strong>{{ $formatNumber(downloads) }}</strong
|
||||
><span class="stat-label">
|
||||
download<span v-if="downloads !== '1'">s</span></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
|
||||
<strong>{{ $formatNumber(follows) }}</strong
|
||||
><span class="stat-label">
|
||||
follower<span v-if="follows !== '1'">s</span></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 :type="status" />
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<slot />
|
||||
</div>
|
||||
<div
|
||||
v-if="showUpdatedDate"
|
||||
v-tooltip="$dayjs(updatedAt).format('MMMM D, YYYY [at] h:mm:ss A')"
|
||||
class="stat date"
|
||||
>
|
||||
<EditIcon aria-hidden="true" />
|
||||
<span class="date-label">Updated </span
|
||||
>{{ $dayjs(updatedAt).fromNow() }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
v-tooltip="$dayjs(createdAt).format('MMMM D, YYYY [at] h:mm:ss A')"
|
||||
class="stat date"
|
||||
>
|
||||
<CalendarIcon aria-hidden="true" />
|
||||
<span class="date-label">Published </span
|
||||
>{{ $dayjs(createdAt).fromNow() }}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</template>
|
||||
@@ -148,6 +145,9 @@ import Categories from '~/components/ui/search/Categories'
|
||||
import Badge from '~/components/ui/Badge'
|
||||
|
||||
import InfoIcon from '~/assets/images/utils/info.svg?inline'
|
||||
import ClientIcon from '~/assets/images/utils/client.svg?inline'
|
||||
import GlobeIcon from '~/assets/images/utils/globe.svg?inline'
|
||||
import ServerIcon from '~/assets/images/utils/server.svg?inline'
|
||||
import IssuesIcon from '~/assets/images/utils/issues.svg?inline'
|
||||
import CalendarIcon from '~/assets/images/utils/calendar.svg?inline'
|
||||
import EditIcon from '~/assets/images/utils/updated.svg?inline'
|
||||
@@ -162,6 +162,9 @@ export default {
|
||||
Categories,
|
||||
Badge,
|
||||
InfoIcon,
|
||||
ClientIcon,
|
||||
ServerIcon,
|
||||
GlobeIcon,
|
||||
IssuesIcon,
|
||||
CalendarIcon,
|
||||
EditIcon,
|
||||
@@ -246,6 +249,18 @@ export default {
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
galleryImages: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
showUpdatedDate: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
projectTypeDisplay() {
|
||||
@@ -256,230 +271,248 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.columns {
|
||||
width: 100%;
|
||||
.project-card {
|
||||
display: inline-grid;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.project-card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: var(--spacing-card-bg);
|
||||
width: calc(100% - 2 * var(--spacing-card-bg));
|
||||
overflow: hidden;
|
||||
.display-mode--list .project-card {
|
||||
grid-template:
|
||||
'icon title stats'
|
||||
'icon description stats'
|
||||
'icon tags stats';
|
||||
grid-template-columns: min-content 1fr auto;
|
||||
grid-template-rows: min-content 1fr min-content;
|
||||
column-gap: var(--spacing-card-md);
|
||||
row-gap: var(--spacing-card-sm);
|
||||
width: 100%;
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
@media screen and (max-width: 750px) {
|
||||
grid-template:
|
||||
'icon title'
|
||||
'icon description'
|
||||
'icon tags'
|
||||
'stats stats';
|
||||
grid-template-columns: min-content auto;
|
||||
grid-template-rows: min-content 1fr min-content min-content;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
grid-template:
|
||||
'icon title'
|
||||
'icon description'
|
||||
'tags tags'
|
||||
'stats stats';
|
||||
grid-template-columns: min-content auto;
|
||||
grid-template-rows: min-content 1fr min-content min-content;
|
||||
}
|
||||
}
|
||||
|
||||
.display-mode--gallery .project-card,
|
||||
.display-mode--grid .project-card {
|
||||
padding: 0 0 var(--spacing-card-bg) 0;
|
||||
grid-template: 'gallery gallery' 'icon title' 'description description' 'tags tags' 'stats stats';
|
||||
grid-template-columns: min-content 1fr;
|
||||
grid-template-rows: min-content min-content 1fr min-content min-content;
|
||||
row-gap: var(--spacing-card-sm);
|
||||
|
||||
.gallery {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 10rem;
|
||||
background-color: var(--color-button-bg-active);
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 10rem;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin: 0 var(--spacing-card-md) var(--spacing-card-md) 0;
|
||||
margin-left: var(--spacing-card-bg);
|
||||
margin-top: -3rem;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
.title {
|
||||
margin-left: var(--spacing-card-md);
|
||||
margin-right: var(--spacing-card-bg);
|
||||
flex-direction: column;
|
||||
|
||||
.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;
|
||||
|
||||
svg {
|
||||
width: auto;
|
||||
color: var(--color-badge-yellow-text);
|
||||
height: 1.5rem;
|
||||
margin-bottom: -0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
.name {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: var(--spacing-card-xs);
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-inline: var(--spacing-card-bg);
|
||||
}
|
||||
|
||||
.tags {
|
||||
margin-inline: var(--spacing-card-bg);
|
||||
}
|
||||
|
||||
.stats {
|
||||
margin-inline: var(--spacing-card-bg);
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.stat-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.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;
|
||||
flex-direction: row;
|
||||
gap: var(--spacing-card-sm);
|
||||
align-items: center;
|
||||
|
||||
text-align: left;
|
||||
|
||||
.stat {
|
||||
margin-bottom: 0;
|
||||
> :first-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.stat svg {
|
||||
margin-left: 0;
|
||||
&:first-child > :last-child {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttons:not(:empty) + .date {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.display-mode--grid .project-card {
|
||||
.gallery {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-top: calc(var(--spacing-card-bg) - var(--spacing-card-sm));
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: calc(var(--spacing-card-bg) - var(--spacing-card-sm));
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
grid-area: icon;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.gallery {
|
||||
display: none;
|
||||
height: 10rem;
|
||||
grid-area: gallery;
|
||||
}
|
||||
|
||||
.title {
|
||||
grid-area: title;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
column-gap: var(--spacing-card-sm);
|
||||
row-gap: 0;
|
||||
word-wrap: anywhere;
|
||||
|
||||
h2,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: auto;
|
||||
color: var(--color-special-orange);
|
||||
height: 1.5rem;
|
||||
margin-bottom: -0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.stats {
|
||||
grid-area: stats;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
align-items: end;
|
||||
gap: var(--spacing-card-md);
|
||||
|
||||
.stat {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
gap: var(--spacing-card-xs);
|
||||
--stat-strong-size: 1.25rem;
|
||||
|
||||
strong {
|
||||
font-size: var(--stat-strong-size);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
height: var(--stat-strong-size);
|
||||
width: var(--stat-strong-size);
|
||||
}
|
||||
}
|
||||
|
||||
.date {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 750px) {
|
||||
flex-direction: row;
|
||||
column-gap: var(--spacing-card-md);
|
||||
margin-top: var(--spacing-card-xs);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
margin-top: 0;
|
||||
|
||||
.stat-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.environment {
|
||||
color: var(--color-text) !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.description {
|
||||
grid-area: description;
|
||||
margin-block: 0;
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.tags {
|
||||
grid-area: tags;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
margin-top: var(--spacing-card-xs);
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-card-sm);
|
||||
align-items: end;
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="categories">
|
||||
<slot />
|
||||
<span
|
||||
v-for="category in categoriesFiltered"
|
||||
:key="category.name"
|
||||
|
||||
@@ -402,7 +402,7 @@
|
||||
')'
|
||||
"
|
||||
:href="findPrimary(version).url"
|
||||
class="download download-button"
|
||||
class="download square-button brand-button"
|
||||
:title="`Download ${version.name}`"
|
||||
@click.stop="(event) => event.stopPropagation()"
|
||||
>
|
||||
@@ -743,7 +743,7 @@
|
||||
')'
|
||||
"
|
||||
:href="findPrimary(version).url"
|
||||
class="download download-button"
|
||||
class="download square-button brand-button"
|
||||
:title="`Download ${version.name}`"
|
||||
@click.stop="(event) => event.stopPropagation()"
|
||||
>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
')'
|
||||
"
|
||||
:href="$parent.findPrimary(version).url"
|
||||
class="download-button"
|
||||
class="download-button square-button brand-button"
|
||||
:class="version.version_type"
|
||||
:title="`Download ${version.name}`"
|
||||
@click.stop="(event) => event.stopPropagation()"
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
}}
|
||||
</div>
|
||||
<span
|
||||
>from {{ $user.projects.length }} project{{
|
||||
$user.projects.length === 1 ? '' : 's'
|
||||
}}</span
|
||||
>from
|
||||
{{ downloadsProjectCount }}
|
||||
project{{ downloadsProjectCount === 1 ? '' : 's' }}</span
|
||||
>
|
||||
<!-- <NuxtLink class="goto-link" to="/dashboard/analytics"-->
|
||||
<!-- >View breakdown-->
|
||||
@@ -35,8 +35,8 @@
|
||||
</div>
|
||||
<span>
|
||||
<span
|
||||
>from {{ $user.projects.length }} project{{
|
||||
$user.projects.length === 1 ? '' : 's'
|
||||
>from {{ followersProjectCount }} project{{
|
||||
followersProjectCount === 1 ? '' : 's'
|
||||
}}</span
|
||||
></span
|
||||
>
|
||||
@@ -117,6 +117,16 @@ export default {
|
||||
head: {
|
||||
title: 'Creator dashboard - Modrinth',
|
||||
},
|
||||
computed: {
|
||||
downloadsProjectCount() {
|
||||
return this.$user.projects.filter((project) => project.downloads > 0)
|
||||
.length
|
||||
},
|
||||
followersProjectCount() {
|
||||
return this.$user.projects.filter((project) => project.followers > 0)
|
||||
.length
|
||||
},
|
||||
},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
29
pages/frog.vue
Normal file
29
pages/frog.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="normal-page">
|
||||
<div class="card">
|
||||
<h1>Frog</h1>
|
||||
<p>You've been frogged! 🐸</p>
|
||||
<img
|
||||
src="https://cdn.modrinth.com/frog.png"
|
||||
alt="a photorealistic painting of a frog labyrinth"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
auth: false,
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-block: 0 1.5rem;
|
||||
}
|
||||
</style>
|
||||
@@ -70,7 +70,7 @@
|
||||
</aside>
|
||||
</div>
|
||||
<div class="normal-page__content">
|
||||
<div class="projects">
|
||||
<div class="project-list display-mode--gallery">
|
||||
<ProjectCard
|
||||
v-for="project in $route.query.type !== undefined
|
||||
? projects.filter((x) => x.project_type === $route.query.type)
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
facet-name="client"
|
||||
@toggle="toggleEnv"
|
||||
>
|
||||
<ClientSide aria-hidden="true" />
|
||||
<ClientIcon aria-hidden="true" />
|
||||
</SearchFilter>
|
||||
<SearchFilter
|
||||
:active-filters="selectedEnvironments"
|
||||
@@ -165,7 +165,7 @@
|
||||
facet-name="server"
|
||||
@toggle="toggleEnv"
|
||||
>
|
||||
<ServerSide aria-hidden="true" />
|
||||
<ServerIcon aria-hidden="true" />
|
||||
</SearchFilter>
|
||||
</section>
|
||||
<h3 class="sidebar-menu-heading">Minecraft versions</h3>
|
||||
@@ -303,6 +303,28 @@
|
||||
@input="onSearchChange(currentPage)"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
v-tooltip="
|
||||
$capitalizeString($cosmetics.searchDisplayMode[projectType.id]) +
|
||||
' view'
|
||||
"
|
||||
:aria-label="
|
||||
$capitalizeString($cosmetics.searchDisplayMode[projectType.id]) +
|
||||
' view'
|
||||
"
|
||||
class="square-button"
|
||||
@click="cycleSearchDisplayMode()"
|
||||
>
|
||||
<GridIcon
|
||||
v-if="$cosmetics.searchDisplayMode[projectType.id] === 'grid'"
|
||||
/>
|
||||
<ImageIcon
|
||||
v-else-if="
|
||||
$cosmetics.searchDisplayMode[projectType.id] === 'gallery'
|
||||
"
|
||||
/>
|
||||
<ListIcon v-else />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<pagination
|
||||
@@ -317,11 +339,22 @@
|
||||
<LogoAnimated aria-hidden="true" />
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
<div v-else id="search-results" role="list" aria-label="Search results">
|
||||
<SearchResult
|
||||
<div
|
||||
v-else-if="true"
|
||||
id="search-results"
|
||||
class="project-list"
|
||||
:class="
|
||||
'display-mode--' + $cosmetics.searchDisplayMode[projectType.id]
|
||||
"
|
||||
role="list"
|
||||
aria-label="Search results"
|
||||
>
|
||||
<ProjectCard
|
||||
v-for="result in results"
|
||||
:id="result.slug ? result.slug : result.project_id"
|
||||
:key="result.project_id"
|
||||
:display="$cosmetics.searchDisplayMode[projectType.id]"
|
||||
:gallery-images="result.gallery"
|
||||
:type="result.project_type"
|
||||
:author="result.author"
|
||||
:name="result.title"
|
||||
@@ -335,6 +368,7 @@
|
||||
:server-side="result.server_side"
|
||||
:categories="result.display_categories"
|
||||
:search="true"
|
||||
:show-updated-date="sortType.name !== 'newest'"
|
||||
/>
|
||||
<div v-if="results && results.length === 0" class="no-results">
|
||||
<p>No results found for your query!</p>
|
||||
@@ -354,18 +388,21 @@
|
||||
|
||||
<script>
|
||||
import Multiselect from 'vue-multiselect'
|
||||
import SearchResult from '~/components/ui/ProjectCard'
|
||||
import ProjectCard from '~/components/ui/ProjectCard'
|
||||
import Pagination from '~/components/ui/Pagination'
|
||||
import SearchFilter from '~/components/ui/search/SearchFilter'
|
||||
import LogoAnimated from '~/components/ui/search/LogoAnimated'
|
||||
import Checkbox from '~/components/ui/Checkbox'
|
||||
|
||||
import ClientSide from '~/assets/images/categories/client.svg?inline'
|
||||
import ServerSide from '~/assets/images/categories/server.svg?inline'
|
||||
import ClientIcon from '~/assets/images/categories/client.svg?inline'
|
||||
import ServerIcon from '~/assets/images/categories/server.svg?inline'
|
||||
|
||||
import SearchIcon from '~/assets/images/utils/search.svg?inline'
|
||||
import ClearIcon from '~/assets/images/utils/clear.svg?inline'
|
||||
import FilterIcon from '~/assets/images/utils/filter.svg?inline'
|
||||
import GridIcon from '~/assets/images/utils/grid.svg?inline'
|
||||
import ListIcon from '~/assets/images/utils/list.svg?inline'
|
||||
import ImageIcon from '~/assets/images/utils/image.svg?inline'
|
||||
|
||||
import Advertisement from '~/components/ads/Advertisement'
|
||||
|
||||
@@ -373,16 +410,19 @@ export default {
|
||||
auth: false,
|
||||
components: {
|
||||
Advertisement,
|
||||
SearchResult,
|
||||
ProjectCard,
|
||||
Pagination,
|
||||
Multiselect,
|
||||
SearchFilter,
|
||||
Checkbox,
|
||||
ClientSide,
|
||||
ServerSide,
|
||||
ClientIcon,
|
||||
ServerIcon,
|
||||
SearchIcon,
|
||||
ClearIcon,
|
||||
FilterIcon,
|
||||
GridIcon,
|
||||
ListIcon,
|
||||
ImageIcon,
|
||||
LogoAnimated,
|
||||
},
|
||||
data() {
|
||||
@@ -408,7 +448,7 @@ export default {
|
||||
{ display: 'Relevance', name: 'relevance' },
|
||||
{ display: 'Download count', name: 'downloads' },
|
||||
{ display: 'Follow count', name: 'follows' },
|
||||
{ display: 'Recently created', name: 'newest' },
|
||||
{ display: 'Recently published', name: 'newest' },
|
||||
{ display: 'Recently updated', name: 'updated' },
|
||||
],
|
||||
sortType: { display: 'Relevance', name: 'relevance' },
|
||||
@@ -782,6 +822,15 @@ export default {
|
||||
|
||||
return url
|
||||
},
|
||||
async cycleSearchDisplayMode() {
|
||||
const value = this.$cosmetics.searchDisplayMode[this.projectType.id]
|
||||
const newValue = this.$cycleValue(value, this.$tag.projectViewModes)
|
||||
await this.$store.dispatch('cosmetics/saveSearchDisplayMode', {
|
||||
projectType: this.projectType.id,
|
||||
mode: newValue,
|
||||
$cookies: this.$cookies,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -892,6 +941,7 @@ export default {
|
||||
flex-direction: row;
|
||||
gap: var(--spacing-card-md);
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
.labeled-control {
|
||||
flex: 1;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div v-if="$user.follows.length > 0">
|
||||
<div v-if="$user.follows.length > 0" class="project-list display-mode--list">
|
||||
<ProjectCard
|
||||
v-for="project in $user.follows"
|
||||
:id="project.id"
|
||||
|
||||
@@ -59,6 +59,32 @@
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section class="universal-card">
|
||||
<h2>Project list display mode</h2>
|
||||
<div
|
||||
v-for="projectType in listTypes"
|
||||
:key="projectType.id + '-display-mode-selector'"
|
||||
class="adjacent-input small"
|
||||
>
|
||||
<label :for="projectType.id + '-search-display-mode'">
|
||||
<span class="label__title">{{ projectType.name }} display mode</span>
|
||||
<span class="label__description"
|
||||
>Change the display view for {{ projectType.display }}.</span
|
||||
>
|
||||
</label>
|
||||
<Multiselect
|
||||
:id="projectType + '-search-display-mode'"
|
||||
:value="searchDisplayMode[projectType.id]"
|
||||
:options="$tag.projectViewModes"
|
||||
:custom-label="$capitalizeString"
|
||||
:searchable="false"
|
||||
:close-on-select="true"
|
||||
:show-labels="false"
|
||||
:allow-empty="false"
|
||||
@input="(value) => setSearchDisplayMode(projectType.id, value)"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section class="universal-card">
|
||||
<h2>Feature flags</h2>
|
||||
<div class="adjacent-input small">
|
||||
@@ -129,25 +155,46 @@ export default {
|
||||
modpacksAlphaNotice: true,
|
||||
advancedRendering: true,
|
||||
externalLinksNewTab: true,
|
||||
searchDisplayMode: {
|
||||
mod: 'list',
|
||||
plugin: 'list',
|
||||
resourcepack: 'gallery',
|
||||
modpack: 'list',
|
||||
user: 'list',
|
||||
},
|
||||
}
|
||||
},
|
||||
fetch() {
|
||||
this.searchLayout =
|
||||
this.$store.state.cosmetics.searchLayout ?? this.searchLayout
|
||||
this.projectLayout =
|
||||
this.$store.state.cosmetics.projectLayout ?? this.projectLayout
|
||||
this.modpacksAlphaNotice =
|
||||
this.$store.state.cosmetics.modpacksAlphaNotice ??
|
||||
this.modpacksAlphaNotice
|
||||
this.advancedRendering =
|
||||
this.$store.state.cosmetics.advancedRendering ?? this.advancedRendering
|
||||
this.externalLinksNewTab =
|
||||
this.$store.state.cosmetics.externalLinksNewTab ??
|
||||
this.externalLinksNewTab
|
||||
this.searchLayout = this.$store.state.cosmetics.searchLayout
|
||||
this.projectLayout = this.$store.state.cosmetics.projectLayout
|
||||
this.modpacksAlphaNotice = this.$store.state.cosmetics.modpacksAlphaNotice
|
||||
this.advancedRendering = this.$store.state.cosmetics.advancedRendering
|
||||
this.externalLinksNewTab = this.$store.state.cosmetics.externalLinksNewTab
|
||||
this.searchDisplayMode = this.$store.state.cosmetics.searchDisplayMode
|
||||
},
|
||||
head: {
|
||||
title: 'Display settings - Modrinth',
|
||||
},
|
||||
computed: {
|
||||
listTypes() {
|
||||
const types = this.$tag.projectTypes.map((type) => {
|
||||
return {
|
||||
id: type.id,
|
||||
name: this.$formatProjectType(type.id) + ' search',
|
||||
display:
|
||||
'the ' +
|
||||
this.$formatProjectType(type.id).toLowerCase() +
|
||||
's search page',
|
||||
}
|
||||
})
|
||||
types.push({
|
||||
id: 'user',
|
||||
name: 'User page',
|
||||
display: 'user pages',
|
||||
})
|
||||
return types
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async saveCosmeticSettings() {
|
||||
await this.$store.dispatch('cosmetics/save', {
|
||||
@@ -156,9 +203,18 @@ export default {
|
||||
modpacksAlphaNotice: this.modpacksAlphaNotice,
|
||||
advancedRendering: this.advancedRendering,
|
||||
externalLinksNewTab: this.externalLinksNewTab,
|
||||
searchDisplayMode: this.searchDisplayMode,
|
||||
$cookies: this.$cookies,
|
||||
})
|
||||
},
|
||||
async setSearchDisplayMode(projectType, value) {
|
||||
await this.$store.dispatch('cosmetics/saveSearchDisplayMode', {
|
||||
projectType,
|
||||
mode: value,
|
||||
$cookies: this.$cookies,
|
||||
})
|
||||
this.searchDisplayMode = this.$store.state.cosmetics.searchDisplayMode
|
||||
},
|
||||
changeTheme() {
|
||||
const shift = event.shiftKey
|
||||
switch (this.$colorMode.preference) {
|
||||
|
||||
@@ -164,23 +164,55 @@
|
||||
}),
|
||||
]"
|
||||
/>
|
||||
<button
|
||||
v-if="$auth.user && $auth.user.id === user.id"
|
||||
class="iconified-button brand-button"
|
||||
@click="$refs.modal_creation.show()"
|
||||
>
|
||||
<PlusIcon />
|
||||
Create a project
|
||||
</button>
|
||||
<div class="input-group">
|
||||
<NuxtLink
|
||||
v-if="$auth.user && $auth.user.id === user.id"
|
||||
class="iconified-button"
|
||||
to="/dashboard/projects"
|
||||
>
|
||||
<SettingsIcon />
|
||||
Manage projects
|
||||
</NuxtLink>
|
||||
<button
|
||||
v-tooltip="
|
||||
$capitalizeString($cosmetics.searchDisplayMode.user) + ' view'
|
||||
"
|
||||
:aria-label="
|
||||
$capitalizeString($cosmetics.searchDisplayMode.user) + ' view'
|
||||
"
|
||||
class="square-button"
|
||||
@click="cycleSearchDisplayMode()"
|
||||
>
|
||||
<GridIcon v-if="$cosmetics.searchDisplayMode.user === 'grid'" />
|
||||
<ImageIcon
|
||||
v-else-if="$cosmetics.searchDisplayMode.user === 'gallery'"
|
||||
/>
|
||||
<ListIcon v-else />
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
<div v-if="projects.length > 0">
|
||||
<div
|
||||
v-if="projects.length > 0"
|
||||
class="project-list"
|
||||
:class="'display-mode--' + $cosmetics.searchDisplayMode.user"
|
||||
>
|
||||
<ProjectCard
|
||||
v-for="project in $route.query.type !== undefined
|
||||
v-for="project in ($route.query.type !== undefined
|
||||
? projects.filter((x) => x.project_type === $route.query.type)
|
||||
: projects"
|
||||
: projects
|
||||
)
|
||||
.slice()
|
||||
.sort((a, b) => b.downloads - a.downloads)"
|
||||
:id="project.slug || project.id"
|
||||
:key="project.id"
|
||||
:name="project.title"
|
||||
:display="$cosmetics.searchDisplayMode.user"
|
||||
:gallery-images="
|
||||
project.gallery
|
||||
.slice()
|
||||
.sort((a, b) => b.featured - a.featured)
|
||||
.map((x) => x.url)
|
||||
"
|
||||
:description="project.description"
|
||||
:created-at="project.published"
|
||||
:updated-at="project.updated"
|
||||
@@ -200,18 +232,7 @@
|
||||
"
|
||||
:has-mod-message="project.moderator_message"
|
||||
:type="project.project_type"
|
||||
>
|
||||
<nuxt-link
|
||||
v-if="$auth.user && $auth.user.id === user.id"
|
||||
class="iconified-button"
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/settings`"
|
||||
>
|
||||
<SettingsIcon />
|
||||
Settings
|
||||
</nuxt-link>
|
||||
</ProjectCard>
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="error">
|
||||
<UpToDate class="icon" /><br />
|
||||
@@ -239,13 +260,15 @@ import ReportIcon from '~/assets/images/utils/report.svg?inline'
|
||||
import SunriseIcon from '~/assets/images/utils/sunrise.svg?inline'
|
||||
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
||||
import SettingsIcon from '~/assets/images/utils/settings.svg?inline'
|
||||
import PlusIcon from '~/assets/images/utils/plus.svg?inline'
|
||||
import UpToDate from '~/assets/images/illustrations/up_to_date.svg?inline'
|
||||
import UserIcon from '~/assets/images/utils/user.svg?inline'
|
||||
import EditIcon from '~/assets/images/utils/edit.svg?inline'
|
||||
import HeartIcon from '~/assets/images/utils/heart.svg?inline'
|
||||
import CrossIcon from '~/assets/images/utils/x.svg?inline'
|
||||
import SaveIcon from '~/assets/images/utils/save.svg?inline'
|
||||
import GridIcon from '~/assets/images/utils/grid.svg?inline'
|
||||
import ListIcon from '~/assets/images/utils/list.svg?inline'
|
||||
import ImageIcon from '~/assets/images/utils/image.svg?inline'
|
||||
import FileInput from '~/components/ui/FileInput'
|
||||
import ModalReport from '~/components/ui/ModalReport'
|
||||
import ModalCreation from '~/components/ui/ModalCreation'
|
||||
@@ -269,7 +292,6 @@ export default {
|
||||
ReportIcon,
|
||||
Badge,
|
||||
SettingsIcon,
|
||||
PlusIcon,
|
||||
UpToDate,
|
||||
UserIcon,
|
||||
EditIcon,
|
||||
@@ -277,6 +299,9 @@ export default {
|
||||
HeartIcon,
|
||||
CrossIcon,
|
||||
SaveIcon,
|
||||
GridIcon,
|
||||
ListIcon,
|
||||
ImageIcon,
|
||||
},
|
||||
async asyncData(data) {
|
||||
try {
|
||||
@@ -486,6 +511,15 @@ export default {
|
||||
}
|
||||
this.$nuxt.$loading.finish()
|
||||
},
|
||||
async cycleSearchDisplayMode() {
|
||||
const value = this.$cosmetics.searchDisplayMode.user
|
||||
const newValue = this.$cycleValue(value, this.$tag.projectViewModes)
|
||||
await this.$store.dispatch('cosmetics/saveSearchDisplayMode', {
|
||||
projectType: 'user',
|
||||
mode: newValue,
|
||||
$cookies: this.$cookies,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -533,6 +567,7 @@ export default {
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
row-gap: 0.5rem;
|
||||
padding-right: var(--spacing-card-bg);
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
|
||||
@@ -175,6 +175,7 @@ export default (ctx, inject) => {
|
||||
return type
|
||||
}
|
||||
})
|
||||
inject('cycleValue', cycleValue)
|
||||
}
|
||||
|
||||
export const formatNumber = (number) => {
|
||||
@@ -333,3 +334,8 @@ export const formatVersions = (versionArray, store) => {
|
||||
|
||||
return output.join(', ')
|
||||
}
|
||||
|
||||
export const cycleValue = (value, values) => {
|
||||
const index = values.indexOf(value) + 1
|
||||
return values[index % values.length]
|
||||
}
|
||||
|
||||
@@ -6,14 +6,23 @@ const parameters = {
|
||||
path: '/',
|
||||
}
|
||||
|
||||
export const state = () => ({
|
||||
export const defaults = {
|
||||
searchLayout: false,
|
||||
projectLayout: false,
|
||||
modpacksAlphaNotice: true,
|
||||
advancedRendering: true,
|
||||
externalLinksNewTab: true,
|
||||
notUsingBlockers: false,
|
||||
})
|
||||
searchDisplayMode: {
|
||||
mod: 'list',
|
||||
plugin: 'list',
|
||||
resourcepack: 'gallery',
|
||||
modpack: 'list',
|
||||
user: 'list',
|
||||
},
|
||||
}
|
||||
|
||||
export const state = () => defaults
|
||||
|
||||
export const mutations = {
|
||||
SET_SEARCH_LAYOUT(state, searchLayout) {
|
||||
@@ -31,6 +40,9 @@ export const mutations = {
|
||||
SET_EXTERNAL_LINKS_NEW_TAB(state, externalLinksNewTab) {
|
||||
state.externalLinksNewTab = externalLinksNewTab
|
||||
},
|
||||
SET_SEARCH_DISPLAY_MODE(state, { projectType, mode }) {
|
||||
state.searchDisplayMode[projectType] = mode
|
||||
},
|
||||
SET_NOT_USING_BLOCKERS(state, notUsingBlockers) {
|
||||
state.notUsingBlockers = notUsingBlockers
|
||||
},
|
||||
@@ -38,11 +50,34 @@ export const mutations = {
|
||||
|
||||
export const actions = {
|
||||
fetchCosmetics({ commit }, $cookies) {
|
||||
commit('SET_PROJECT_LAYOUT', $cookies.get('project-layout'))
|
||||
commit('SET_SEARCH_LAYOUT', $cookies.get('search-layout'))
|
||||
commit('SET_MODPACKS_ALPHA_NOTICE', $cookies.get('modpacks-alpha-notice'))
|
||||
commit('SET_ADVANCED_RENDERING', $cookies.get('advanced-rendering'))
|
||||
commit('SET_EXTERNAL_LINKS_NEW_TAB', $cookies.get('external-links-new-tab'))
|
||||
commit(
|
||||
'SET_PROJECT_LAYOUT',
|
||||
$cookies.get('project-layout') ?? defaults.projectLayout
|
||||
)
|
||||
commit(
|
||||
'SET_SEARCH_LAYOUT',
|
||||
$cookies.get('search-layout') ?? defaults.searchLayout
|
||||
)
|
||||
commit(
|
||||
'SET_MODPACKS_ALPHA_NOTICE',
|
||||
$cookies.get('modpacks-alpha-notice') ?? defaults.modpacksAlphaNotice
|
||||
)
|
||||
commit(
|
||||
'SET_ADVANCED_RENDERING',
|
||||
$cookies.get('advanced-rendering') ?? defaults.advancedRendering
|
||||
)
|
||||
commit(
|
||||
'SET_EXTERNAL_LINKS_NEW_TAB',
|
||||
$cookies.get('external-links-new-tab') ?? defaults.externalLinksNewTab
|
||||
)
|
||||
Object.keys(defaults.searchDisplayMode).forEach((projectType) => {
|
||||
commit('SET_SEARCH_DISPLAY_MODE', {
|
||||
projectType,
|
||||
mode:
|
||||
$cookies.get('search-display-mode-' + projectType) ??
|
||||
defaults.searchDisplayMode[projectType],
|
||||
})
|
||||
})
|
||||
},
|
||||
save(
|
||||
{ commit },
|
||||
@@ -67,4 +102,9 @@ export const actions = {
|
||||
$cookies.set('advanced-rendering', advancedRendering, parameters)
|
||||
$cookies.set('external-links-new-tab', externalLinksNewTab, parameters)
|
||||
},
|
||||
saveSearchDisplayMode({ commit }, { projectType, mode, $cookies }) {
|
||||
commit('SET_SEARCH_DISPLAY_MODE', { projectType, mode })
|
||||
|
||||
$cookies.set('search-display-mode-' + projectType, mode, parameters)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -44,4 +44,5 @@ export const state = () => ({
|
||||
],
|
||||
modLoaders: ['forge', 'fabric', 'quilt', 'liteloader', 'modloader', 'rift'],
|
||||
},
|
||||
projectViewModes: ['list', 'grid', 'gallery'],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user