You've already forked AstralRinth
forked from didirus/AstralRinth
Add initial support for the v2 of the API (Still WIP) (#250)
* Functionally implement modpacks * Add LogoAnimated to logo license * Fix eslint errors * Add `z-index: 20` to user dropdown (#287) * Fix pages not working, add changelog page, redesign versions page * Update theme colors, add OLED theme, update some project creation text. (#292) * Update theme colors, add OLED theme, update some project creation text. * Make summary normal text color * Update favicons, update logos to use dynamic colors, updated filters panel a bit * Update wording from #250 * Version page rework * Manually apply some commits from master, other minor v2 fixes (#296) * Homepage styling improvements (#285) * Add border radius to video + example code colors * Change color + allow overflow scroll * Minor v2 fixes - Makes multiple loaders display correctly (used to be `Fabric,Forge` is now `Fabric, Forge` - Fix oopses in #292 - Allow .jar and .zip in file prompt - Apply 30cbd3a6c372940d1e86cc8134d0dfc7e8e5ee9c to pages/create/project.vue - Display `fabric, forge` instead of broken icons on pages/create/project.vue * Markdown styling fixes (#268) * Add table color variables (+ prettier fixes) * Add details and table styling to .markdown-body * Add indexing meta value depending on the status of the mod. (#261) * General UI Improvement (again) (#255) * Add and fix some stuff * Add warning when leaving to `mod/create` * Fix mods/create not working * Fix a bug & add improvements to a couple moderation aspects (#278) This PR fixes reports on the moderation dashboard going to `/dashboard/mod/_id` instead of to `/mod/_id`. It also allows the ability for moderators to unlist mods in the queue from the frontend instead of having to do it via the backend.  Unlisted mods should have the ability to resubmit for approval, so I've also changed "Submit for Review" to "Submit for approval", allowing unlisted mods to do that as well.  * Add project guidelines to Terms page (#275) * Add project guidelines to Terms page This adds the project guidelines as outlined [here](https://discord.com/channels/734077874708938864/734077874708938867/806556531491471368). NOTE: I've made a few tweaks in wording to accommodate this format, so this is not an exact copy. * Move rules to its own page * Allow users to login from search page when it is rendered serverside (#272) * Change `this.$route.fullPath` → `this.$route.path` * Closes modrinth/knossos#256 * Wrap mod icon and title in link (#273) * Wrap mod icon and title in link * Fixes #218 * Editor's note Skipped #249 (search was rewritten), #266 (couldn't figure out how to apply it), #270 (didn't seem to apply properly), #252 (manually merged in with #292), #262 (superceded by #270), #282, #271, #277, #283, and #281 (those five didn't get wiped) Co-authored-by: venashial <venashial.levo@aleeas.com> Co-authored-by: Redblueflame <contact@redblueflame.com> Co-authored-by: Johan Novak <wickedtree@wickedtree.codes> * SSR descriptions, version edit page * Working version editing + dependency management (besides files) * Version create page, file functionality * Fix some issues with the version page * More versions page fixes * Project gallery * Box shadows, user profile page, WIP header * Finish user dashboard * Finish search and fix minor issues * Moderator page + messages, notifications page * Fix dropdown menu, fix XSS, fix team members page * Change doc url on main page (#309) * Re-Fix docs url (#313) * Clean up. Part 1: Fix immediate problems (#316) * Clean up tabs and cards CSS a little * Fix project page; Remove bad styles from search * Yeet and flatten lots of styles; fix font sizes * Restyle search; fix moderation * Fix profile page * Remove injected SCSS entirely * Fix a mobile layout overflowing * Apiv2-support fixes (#320) * Fix member user_id -> user.id * Fix incorrect report redirect * Change theme switcher from button to multiselect * Fix remaining items Co-authored-by: Jai A <jaiagr+gpg@pm.me> * Fix bugs * Full mobile support, update create project page, fix various bugs * New Dark Mode brand colors (#325) * Use "color-brand-hover" for auth-prompt when hover over * New dark mode brand colors * Fix new version featured bug * Remove old home page, other fixes * Fix error when merging * Fix prettier error :( Co-authored-by: Jai A <jaiagr+gpg@pm.me> Co-authored-by: venashial <venashial.levo@aleeas.com> Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com> Co-authored-by: Emma <emmaffle@modrinth.com> Co-authored-by: Johan Novak <wickedtree@wickedtree.codes> Co-authored-by: Jai A <jaiagr@pm.me> Co-authored-by: Mysterious_Dev <40738104+Mysterious-Dev@users.noreply.github.com> Co-authored-by: Mikhail Oleynikov <contact@falseresync.ru> Co-authored-by: Christian Popov <30723811+Xrey274@users.noreply.github.com>
This commit is contained in:
@@ -79,9 +79,20 @@ export default {
|
||||
onSmallScreen: false,
|
||||
windowResizeListenerDebounce: null,
|
||||
ethicalAdLoad: null,
|
||||
tries: 0,
|
||||
}
|
||||
},
|
||||
head: {
|
||||
script: [
|
||||
{
|
||||
hid: 'ethical_ads_script',
|
||||
type: 'text/javascript',
|
||||
src: 'https://media.ethicalads.io/media/client/ethicalads.min.js',
|
||||
async: true,
|
||||
body: true,
|
||||
defer: true,
|
||||
}, // Insert in body
|
||||
],
|
||||
},
|
||||
computed: {
|
||||
ethical_ads_on() {
|
||||
return (
|
||||
@@ -120,7 +131,7 @@ export default {
|
||||
this.displayed = true
|
||||
if (process.browser) {
|
||||
this.handleWindowResize()
|
||||
this.refresh_ad(true)
|
||||
this.refresh_ad()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -134,14 +145,14 @@ export default {
|
||||
this.format = sizes[this.type]
|
||||
this.displayed = true
|
||||
// Refresh ad
|
||||
this.refresh_ad(true)
|
||||
this.refresh_ad()
|
||||
}
|
||||
return
|
||||
}
|
||||
if (this.onSmallScreen === false) {
|
||||
// Reload ad
|
||||
this.onSmallScreen = true
|
||||
this.refresh_ad(true)
|
||||
this.refresh_ad()
|
||||
}
|
||||
this.onSmallScreen = true
|
||||
if (this.smallScreen === 'destroy') {
|
||||
@@ -152,47 +163,26 @@ export default {
|
||||
}
|
||||
}, 300)
|
||||
},
|
||||
refresh_ad(reset = false) {
|
||||
if (reset) {
|
||||
this.tries = 0
|
||||
}
|
||||
if (this.tries >= 3) {
|
||||
// Too many tries, we stop
|
||||
return
|
||||
}
|
||||
refresh_ad() {
|
||||
if (this.ethical_ads_on) {
|
||||
clearTimeout(this.ethicalAdLoad)
|
||||
this.ethicalAdLoad = setTimeout(() => {
|
||||
try {
|
||||
ethicalads.load()
|
||||
} catch (e) {
|
||||
if (typeof window.ethicalads === 'undefined') {
|
||||
console.log('EthicalAds are not loaded yet, retrying...')
|
||||
this.refresh_ad()
|
||||
this.tries++
|
||||
}
|
||||
ethicalads.load()
|
||||
}, 100)
|
||||
}
|
||||
},
|
||||
},
|
||||
head: {
|
||||
script: [
|
||||
{
|
||||
hid: 'ethical_ads_script',
|
||||
type: 'text/javascript',
|
||||
src: 'https://media.ethicalads.io/media/client/ethicalads.min.js',
|
||||
async: true,
|
||||
body: true,
|
||||
defer: true,
|
||||
}, // Insert in body
|
||||
],
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ad-wrapper {
|
||||
width: 100%;
|
||||
@extend %card;
|
||||
// @extend %card;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
class="container"
|
||||
:style="{ visibility: shown ? 'visible' : 'hidden' }"
|
||||
>
|
||||
<div class="banner">
|
||||
<div class="card banner">
|
||||
<span>
|
||||
Modrinth uses cookies for various purposes, including advertising.<br />
|
||||
We encourage you to review your privacy settings by clicking on the
|
||||
@@ -24,14 +24,14 @@
|
||||
import scopes from '~/privacy-toggles'
|
||||
export default {
|
||||
name: 'CookieConsent',
|
||||
fetch() {
|
||||
this.checkVisibility()
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
shown: false,
|
||||
}
|
||||
},
|
||||
fetch() {
|
||||
this.checkVisibility()
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.checkVisibility()
|
||||
@@ -43,7 +43,7 @@ export default {
|
||||
|
||||
this.shown =
|
||||
!this.$store.state.consent.is_consent_given &&
|
||||
this.$route.path !== '/dashboard/privacy'
|
||||
this.$route.path !== '/settings/privacy'
|
||||
},
|
||||
hide() {
|
||||
this.$store.commit('consent/set_consent', true)
|
||||
@@ -57,7 +57,7 @@ export default {
|
||||
},
|
||||
review() {
|
||||
this.shown = false
|
||||
this.$router.push('/dashboard/privacy')
|
||||
this.$router.push('/settings/privacy')
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -73,7 +73,6 @@ export default {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
.banner {
|
||||
@extend %card;
|
||||
padding: 1rem;
|
||||
font-size: 1.05rem;
|
||||
border-radius: 0;
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
<template>
|
||||
<footer :class="{ centered, padded, hideBig, hideSmall }">
|
||||
<span>
|
||||
Modrinth is open source software. You may view the source code at
|
||||
<a target="_blank" href="https://github.com/modrinth">our GitHub page</a>.
|
||||
</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a @click="changeTheme">
|
||||
<span v-if="$colorMode.value === 'light'">Switch to Dark Mode</span>
|
||||
<span v-else>Switch to Light Mode</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<nuxt-link to="/legal/terms">Terms</nuxt-link>
|
||||
</li>
|
||||
<li>
|
||||
<nuxt-link to="/legal/privacy">Privacy</nuxt-link>
|
||||
</li>
|
||||
<li>
|
||||
<nuxt-link to="/about">About</nuxt-link>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<a target="_blank" href="https://blog.modrinth.com">Blog</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" href="https://discord.gg/gFRbNQ2">Discord</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" href="https://twitter.com/modrinth">Twitter</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<nuxt-link to="/dashboard/privacy">Change privacy settings</nuxt-link>
|
||||
</li>
|
||||
</ul>
|
||||
<span> © Guavy LLC </span><br />
|
||||
<span v-if="version !== 'unknown'">Version: {{ version }}</span>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
version: {
|
||||
type: String,
|
||||
default: process.env.version || 'unknown',
|
||||
},
|
||||
centered: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
padded: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hideSmall: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hideBig: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
changeTheme() {
|
||||
this.$colorMode.preference =
|
||||
this.$colorMode.value === 'dark' ? 'light' : 'dark'
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.centered {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.padded {
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 2rem 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
li {
|
||||
margin-bottom: var(--spacing-card-sm);
|
||||
&:not(:last-child)::after {
|
||||
content: '•';
|
||||
padding: 0;
|
||||
margin: 0 var(--spacing-card-sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-bottom: var(--spacing-card-sm);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hideSmall {
|
||||
@media screen and (max-width: 1024px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.hideBig {
|
||||
@media screen and (min-width: 1024px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,76 +0,0 @@
|
||||
<template>
|
||||
<div class="avatar-icon">
|
||||
<img :src="this.$auth.user.avatar_url" class="icon" />
|
||||
<div v-if="notifCount > 0" class="bubble" :class="{ dropdownBg }">
|
||||
{{ displayNotifCount }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AvatarIcon',
|
||||
props: {
|
||||
notifCount: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
dropdownBg: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
displayNotifCount() {
|
||||
return this.notifCount < 100 ? this.notifCount : '99+'
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.avatar-icon {
|
||||
position: relative;
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
margin-left: 0.5rem;
|
||||
margin-right: 0.25rem;
|
||||
|
||||
.icon {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.bubble {
|
||||
position: absolute;
|
||||
bottom: -0.25rem;
|
||||
right: -0.3rem;
|
||||
|
||||
border-radius: 0.9rem;
|
||||
height: 0.9rem;
|
||||
min-width: 0.45rem;
|
||||
padding: 0 0.22rem;
|
||||
font-size: 0.65rem;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
background-color: #e02914;
|
||||
color: white;
|
||||
border: 0.15rem solid var(--color-raised-bg);
|
||||
|
||||
&.dropdownBg {
|
||||
border-color: var(--color-button-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.dropdown:hover {
|
||||
.bubble {
|
||||
border-color: var(--color-button-bg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
78
components/ui/Badge.vue
Normal file
78
components/ui/Badge.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<span :class="'version-badge ' + color">
|
||||
<span class="circle" /> {{ type }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VersionBadge',
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.version-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
text-transform: capitalize;
|
||||
|
||||
.circle {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
&.custom-circle {
|
||||
@media screen and (min-width: 560px) {
|
||||
.circle {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.red {
|
||||
color: var(--color-badge-red-text);
|
||||
|
||||
.circle {
|
||||
background-color: var(--color-badge-red-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&.green {
|
||||
color: var(--color-badge-green-text);
|
||||
|
||||
.circle {
|
||||
background-color: var(--color-badge-green-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&.yellow {
|
||||
color: var(--color-badge-yellow-text);
|
||||
|
||||
.circle {
|
||||
background-color: var(--color-badge-yellow-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&.gray {
|
||||
color: var(--color-badge-gray-text);
|
||||
|
||||
.circle {
|
||||
background-color: var(--color-badge-gray-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<div class="checkbox-outer" :class="{ disabled }" @click="toggle">
|
||||
<button class="checkbox" :disabled="disabled" :class="{ border }">
|
||||
<button class="checkbox" :disabled="disabled" :class="{ checked: value }">
|
||||
<CheckIcon v-if="value" />
|
||||
</button>
|
||||
<p>{{ label }}</p>
|
||||
<p v-if="label">{{ label }}</p>
|
||||
<slot v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -24,11 +25,11 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
value: Boolean,
|
||||
clickEvent: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
@@ -62,8 +63,9 @@ export default {
|
||||
}
|
||||
|
||||
p {
|
||||
user-select: none;
|
||||
padding: 0.2rem 0rem;
|
||||
margin: 0 0 0 0.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,30 +75,22 @@ export default {
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin: 0 0.5rem 0 0;
|
||||
|
||||
svg {
|
||||
color: var(--color-brand-light);
|
||||
stroke-width: 0.2rem;
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
flex-shrink: 0;
|
||||
&.checked {
|
||||
background-color: var(--color-brand);
|
||||
}
|
||||
|
||||
&.border {
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
|
||||
border: 0.15rem solid var(--color-text);
|
||||
|
||||
svg {
|
||||
height: 0.9rem;
|
||||
width: 0.9rem;
|
||||
}
|
||||
svg {
|
||||
color: var(--color-text-inverted);
|
||||
stroke-width: 0.2rem;
|
||||
height: 0.8rem;
|
||||
width: 0.8rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div v-if="pages.length > 1" class="columns paginates">
|
||||
<button
|
||||
:class="{ disabled: currentPage === 1 }"
|
||||
class="paginate has-icon"
|
||||
class="left-arrow paginate has-icon"
|
||||
aria-label="Previous Page"
|
||||
@click="currentPage !== 1 ? switchPage(currentPage - 1) : null"
|
||||
>
|
||||
@@ -17,7 +17,7 @@
|
||||
}"
|
||||
class="page-number-container"
|
||||
>
|
||||
<div v-if="item == '-'" class="has-icon">
|
||||
<div v-if="item === '-'" class="has-icon">
|
||||
<GapIcon />
|
||||
</div>
|
||||
<button
|
||||
@@ -36,7 +36,7 @@
|
||||
:class="{
|
||||
disabled: currentPage === pages[pages.length - 1],
|
||||
}"
|
||||
class="paginate has-icon"
|
||||
class="right-arrow paginate has-icon"
|
||||
aria-label="Next Page"
|
||||
@click="
|
||||
currentPage !== pages[pages.length - 1]
|
||||
@@ -83,22 +83,27 @@ export default {
|
||||
|
||||
<style scoped lang="scss">
|
||||
button {
|
||||
box-shadow: var(--shadow-card);
|
||||
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
border-radius: 2em;
|
||||
background: transparent;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: 2rem;
|
||||
background: var(--color-raised-bg);
|
||||
|
||||
&.page-number.current {
|
||||
background: var(--color-button-bg-hover);
|
||||
color: var(--color-button-text-hover);
|
||||
background: var(--color-brand);
|
||||
color: var(--color-brand-inverted);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&.paginate.disabled {
|
||||
background: none;
|
||||
color: var(--color-button-text-disabled);
|
||||
background-color: var(--color-button-bg);
|
||||
cursor: default;
|
||||
color: var(--color-icon);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--color-button-bg-active);
|
||||
color: var(--color-button-text-active);
|
||||
@@ -127,8 +132,9 @@ button,
|
||||
.paginates {
|
||||
height: 2em;
|
||||
margin: 0.5rem 0;
|
||||
> div {
|
||||
margin: 0 0.1em;
|
||||
> div,
|
||||
.has-icon {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
font-size: 80%;
|
||||
@media screen and (min-width: 350px) {
|
||||
@@ -141,4 +147,12 @@ button,
|
||||
height: 2.225em;
|
||||
width: 2.225em;
|
||||
}
|
||||
|
||||
.left-arrow {
|
||||
margin-left: auto !important;
|
||||
}
|
||||
|
||||
.right-arrow {
|
||||
margin-right: auto !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 11;
|
||||
box-shadow: 0 2px 3px 1px var(--color-button-bg);
|
||||
border-radius: 10px;
|
||||
border-radius: var(--size-rounded-lg);
|
||||
max-height: 80%;
|
||||
overflow-y: auto;
|
||||
background-color: var(--color-raised-bg);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<article class="project-card">
|
||||
<article class="project-card card">
|
||||
<div class="columns">
|
||||
<div class="icon">
|
||||
<nuxt-link v-if="isModrinth" :to="'/mod/' + id">
|
||||
<nuxt-link :to="`/${type}/${id}`">
|
||||
<img
|
||||
:src="iconUrl || 'https://cdn.modrinth.com/placeholder.svg?inline'"
|
||||
:alt="name"
|
||||
@@ -11,130 +11,133 @@
|
||||
</nuxt-link>
|
||||
<Categories :categories="categories" class="left-categories" />
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="top">
|
||||
<h2 class="title">
|
||||
<nuxt-link v-if="isModrinth" :to="'/mod/' + id">{{
|
||||
name
|
||||
}}</nuxt-link>
|
||||
<a v-else :href="pageUrl">{{ name }}</a>
|
||||
</h2>
|
||||
<p v-if="author" class="author">
|
||||
by <nuxt-link :to="'/user/' + author">{{ author }}</nuxt-link>
|
||||
</p>
|
||||
</div>
|
||||
<p class="description">
|
||||
{{ description }}
|
||||
</p>
|
||||
<div :class="{ vertical: editMode }" class="bottom">
|
||||
<div class="stats">
|
||||
<div v-if="status !== null" class="stat">
|
||||
<div class="info">
|
||||
<h4>Status</h4>
|
||||
<span v-if="status === 'approved'" class="badge green">
|
||||
Approved
|
||||
</span>
|
||||
<span v-if="status === 'rejected'" class="badge red">
|
||||
Rejected
|
||||
</span>
|
||||
<span v-if="status === 'draft'" class="badge yellow"
|
||||
>Draft</span
|
||||
>
|
||||
<span v-if="status === 'processing'" class="badge yellow">
|
||||
Under review
|
||||
</span>
|
||||
<span v-if="status === 'unlisted'" class="badge gray">
|
||||
Unlisted
|
||||
</span>
|
||||
<span v-if="status === 'unknown'" class="badge gray">
|
||||
Unknown
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="info">
|
||||
<div class="top">
|
||||
<h2 class="title">
|
||||
<nuxt-link :to="`/${type}/${id}`">{{ name }}</nuxt-link>
|
||||
</h2>
|
||||
<p v-if="author" class="author">
|
||||
by <nuxt-link :to="'/user/' + author">{{ author }}</nuxt-link>
|
||||
</p>
|
||||
</div>
|
||||
<div class="side-type">
|
||||
<div
|
||||
v-if="clientSide === 'optional' && serverSide === 'optional'"
|
||||
class="side-descriptor"
|
||||
>
|
||||
<InfoIcon />
|
||||
Universal {{ type }}
|
||||
</div>
|
||||
<div class="stat">
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
<div class="info">
|
||||
<h4>Downloads</h4>
|
||||
<p class="value">{{ formatNumber(downloads) }}</p>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="
|
||||
(clientSide === 'optional' || clientSide === 'required') &&
|
||||
(serverSide === 'optional' || serverSide === 'unsupported')
|
||||
"
|
||||
class="side-descriptor"
|
||||
>
|
||||
<InfoIcon />
|
||||
Client {{ type }}
|
||||
</div>
|
||||
<div class="stat">
|
||||
<CalendarIcon aria-hidden="true" />
|
||||
<div class="info">
|
||||
<h4>Created</h4>
|
||||
<p
|
||||
v-tooltip="
|
||||
$dayjs(createdAt).format(
|
||||
'[Created on] YYYY-MM-DD [at] HH:mm A'
|
||||
)
|
||||
"
|
||||
class="value"
|
||||
>
|
||||
{{ $dayjs(createdAt).fromNow() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<EditIcon aria-hidden="true" />
|
||||
<div class="info">
|
||||
<h4>Updated</h4>
|
||||
<p
|
||||
v-tooltip="
|
||||
$dayjs(updatedAt).format(
|
||||
'[Updated on] YYYY-MM-DD [at] HH:mm A'
|
||||
)
|
||||
"
|
||||
class="value"
|
||||
>
|
||||
{{ $dayjs(updatedAt).fromNow() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="latestVersion" class="stat">
|
||||
<TagIcon aria-hidden="true" />
|
||||
<div class="info">
|
||||
<h4>Available For</h4>
|
||||
<p class="value">
|
||||
{{ latestVersion }}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="
|
||||
(serverSide === 'optional' || serverSide === 'required') &&
|
||||
(clientSide === 'optional' || clientSide === 'unsupported')
|
||||
"
|
||||
class="side-descriptor"
|
||||
>
|
||||
<InfoIcon />
|
||||
Server {{ type }}
|
||||
</div>
|
||||
</div>
|
||||
<p class="description">
|
||||
{{ description }}
|
||||
</p>
|
||||
<Categories :categories="categories" class="right-categories" />
|
||||
<div class="dates">
|
||||
<div class="date">
|
||||
<CalendarIcon />
|
||||
Created {{ $dayjs(createdAt).fromNow() }}
|
||||
</div>
|
||||
<div class="date">
|
||||
<EditIcon />
|
||||
Updated {{ $dayjs(updatedAt).fromNow() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-side">
|
||||
<div v-if="downloads" class="stat">
|
||||
<DownloadIcon />
|
||||
<p>
|
||||
<strong>{{ formatNumber(downloads) }}</strong> downloads
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="follows" class="stat">
|
||||
<HeartIcon />
|
||||
<p>
|
||||
<strong>{{ formatNumber(follows) }}</strong> followers
|
||||
</p>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="editMode" class="buttons">
|
||||
<slot />
|
||||
</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 DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
||||
import EditIcon from '~/assets/images/utils/edit.svg?inline'
|
||||
import TagIcon from '~/assets/images/utils/tag.svg?inline'
|
||||
import EditIcon from '~/assets/images/utils/updated.svg?inline'
|
||||
import DownloadIcon from '~/assets/images/utils/download-alt.svg?inline'
|
||||
import HeartIcon from '~/assets/images/utils/heart.svg?inline'
|
||||
|
||||
export default {
|
||||
name: 'ProjectCard',
|
||||
components: {
|
||||
Categories,
|
||||
Badge,
|
||||
InfoIcon,
|
||||
CalendarIcon,
|
||||
DownloadIcon,
|
||||
EditIcon,
|
||||
TagIcon,
|
||||
DownloadIcon,
|
||||
HeartIcon,
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: 'modrinth-0',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'mod',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: 'Mod Name',
|
||||
default: 'Project Name',
|
||||
},
|
||||
author: {
|
||||
type: String,
|
||||
@@ -142,23 +145,22 @@ export default {
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: 'A mod description',
|
||||
},
|
||||
pageUrl: {
|
||||
type: String,
|
||||
default: '#',
|
||||
},
|
||||
authorUrl: {
|
||||
type: String,
|
||||
default: '#',
|
||||
default: 'A _type description',
|
||||
},
|
||||
iconUrl: {
|
||||
type: String,
|
||||
default: '#',
|
||||
required: false,
|
||||
},
|
||||
downloads: {
|
||||
type: String,
|
||||
default: '0',
|
||||
default: null,
|
||||
required: false,
|
||||
},
|
||||
follows: {
|
||||
type: String,
|
||||
default: null,
|
||||
required: false,
|
||||
},
|
||||
createdAt: {
|
||||
type: String,
|
||||
@@ -168,27 +170,25 @@ export default {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
latestVersion: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
categories: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
editMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
isModrinth: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
serverSide: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
clientSide: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@@ -200,11 +200,16 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.project-card {
|
||||
@extend %row;
|
||||
@extend %card-spaced-b;
|
||||
.columns {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.project-card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-direction: column;
|
||||
padding: var(--spacing-card-bg);
|
||||
width: calc(100% - 2 * var(--spacing-card-bg));
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
flex-direction: row;
|
||||
@@ -212,96 +217,172 @@ export default {
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin: auto 0;
|
||||
img {
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
margin: var(--spacing-card-md);
|
||||
margin: 0 var(--spacing-card-md) var(--spacing-card-md) 0;
|
||||
border-radius: var(--size-rounded-icon);
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
.info {
|
||||
@extend %column;
|
||||
flex-grow: 1;
|
||||
.top {
|
||||
@extend %row;
|
||||
flex-wrap: wrap;
|
||||
flex-shrink: 0;
|
||||
margin-top: var(--spacing-card-md);
|
||||
margin-right: var(--spacing-card-md);
|
||||
.title {
|
||||
margin: 0;
|
||||
color: var(--color-text-dark);
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
.author {
|
||||
margin: auto 0 0 0.5rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
.description {
|
||||
margin: var(--spacing-card-sm) var(--spacing-card-md) 0 0;
|
||||
height: 100%;
|
||||
color: var(--color-text-dark);
|
||||
}
|
||||
.bottom {
|
||||
@extend %column;
|
||||
flex-shrink: 0;
|
||||
margin-top: var(--spacing-card-sm);
|
||||
margin-right: var(--spacing-card-md);
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
.card-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-grow: 1;
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.top {
|
||||
align-items: baseline;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
&.vertical {
|
||||
flex-direction: column;
|
||||
.categories {
|
||||
margin-top: var(--spacing-card-sm);
|
||||
flex-wrap: wrap;
|
||||
flex-shrink: 0;
|
||||
margin-right: var(--spacing-card-md);
|
||||
|
||||
.title {
|
||||
margin: 0 0.5rem 0 0;
|
||||
color: var(--color-text-dark);
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
|
||||
.author {
|
||||
margin: auto 0 0 0;
|
||||
color: var(--color-text);
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stats {
|
||||
@extend %row;
|
||||
flex-wrap: wrap;
|
||||
.side-descriptor {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bolder;
|
||||
font-size: var(--font-size-sm);
|
||||
|
||||
@media screen and (min-width: 900px) {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
margin-top: 0.125rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.stat {
|
||||
@extend %stat;
|
||||
svg {
|
||||
width: auto;
|
||||
height: 1rem;
|
||||
margin-right: 0.125rem;
|
||||
}
|
||||
}
|
||||
.categories {
|
||||
@media screen and (min-width: 1024px) {
|
||||
flex-direction: row;
|
||||
margin: auto 0;
|
||||
|
||||
.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;
|
||||
|
||||
svg {
|
||||
height: 1.25rem;
|
||||
margin-right: 0.125rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-side {
|
||||
min-width: 8.75rem;
|
||||
text-align: right;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left-categories {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 560px) {
|
||||
.card-content {
|
||||
flex-direction: column;
|
||||
.right-side {
|
||||
padding-top: var(--spacing-card-sm);
|
||||
|
||||
text-align: left;
|
||||
|
||||
.stat svg {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.buttons button,
|
||||
a {
|
||||
margin-left: unset;
|
||||
margin-right: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left-categories {
|
||||
display: flex;
|
||||
margin: 0 0.75rem 0.75rem 0.75rem;
|
||||
margin: 0 0 0.75rem 0.75rem;
|
||||
width: 7rem;
|
||||
}
|
||||
|
||||
.right-categories {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.buttons {
|
||||
@extend %column;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
75
components/ui/SmartFileInput.vue
Normal file
75
components/ui/SmartFileInput.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="columns">
|
||||
<label class="iconified-button" @drop.prevent="addFile" @dragover.prevent>
|
||||
<UploadIcon />
|
||||
{{ prompt }}
|
||||
<input
|
||||
type="file"
|
||||
:multiple="multiple"
|
||||
:accept="accept"
|
||||
@change="onChange"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UploadIcon from '~/assets/images/utils/upload.svg?inline'
|
||||
|
||||
export default {
|
||||
name: 'SmartFileInput',
|
||||
components: {
|
||||
UploadIcon,
|
||||
},
|
||||
props: {
|
||||
prompt: {
|
||||
type: String,
|
||||
default: 'Select file',
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
accept: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
files: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(files, shouldNotReset) {
|
||||
if (!shouldNotReset) this.files = files.target.files
|
||||
|
||||
this.$emit('change', this.files)
|
||||
},
|
||||
addFile(e) {
|
||||
const droppedFiles = e.dataTransfer.files
|
||||
|
||||
if (!this.multiple) this.files = []
|
||||
|
||||
if (!droppedFiles) return
|
||||
;[...droppedFiles].forEach((f) => {
|
||||
this.files.push(f)
|
||||
})
|
||||
|
||||
if (!this.multiple && this.files.length > 0) this.files = [this.files[0]]
|
||||
|
||||
if (this.files.length > 0) this.onChange(null, true)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
53
components/ui/ThisOrThat.vue
Normal file
53
components/ui/ThisOrThat.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class="styled-tabs">
|
||||
<button
|
||||
v-for="item in items"
|
||||
:key="item"
|
||||
class="tab"
|
||||
:class="{ selected: selected === item }"
|
||||
@click="toggleItem(item)"
|
||||
>
|
||||
<span>{{ item }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ThisOrThat',
|
||||
props: {
|
||||
items: {
|
||||
required: true,
|
||||
type: Array,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selected: '',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.items.length > 0) {
|
||||
this.selected = this.items[0]
|
||||
this.$emit('input', this.selected)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleItem(item) {
|
||||
this.selected = item
|
||||
this.$emit('input', item)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-transform: capitalize;
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
color: inherit;
|
||||
}
|
||||
</style>
|
||||
@@ -1,99 +1,16 @@
|
||||
<template>
|
||||
<div class="categories">
|
||||
<p v-if="categories.includes('fabric')">
|
||||
<FabricLoader aria-hidden="true" />
|
||||
Fabric
|
||||
</p>
|
||||
<p v-if="categories.includes('forge')">
|
||||
<ForgeLoader aria-hidden="true" />
|
||||
Forge
|
||||
</p>
|
||||
<p v-if="categories.includes('technology')">
|
||||
<TechCategory aria-hidden="true" />
|
||||
Technology
|
||||
</p>
|
||||
<p v-if="categories.includes('adventure')">
|
||||
<AdventureCategory aria-hidden="true" />
|
||||
Adventure
|
||||
</p>
|
||||
<p v-if="categories.includes('magic')">
|
||||
<MagicCategory aria-hidden="true" />
|
||||
Magic
|
||||
</p>
|
||||
<p v-if="categories.includes('utility')">
|
||||
<UtilityCategory aria-hidden="true" />
|
||||
Utility
|
||||
</p>
|
||||
<p v-if="categories.includes('decoration')">
|
||||
<DecorationCategory aria-hidden="true" />
|
||||
Decoration
|
||||
</p>
|
||||
<p v-if="categories.includes('library')">
|
||||
<LibraryCategory aria-hidden="true" />
|
||||
Library
|
||||
</p>
|
||||
<p v-if="categories.includes('cursed')">
|
||||
<CursedCategory aria-hidden="true" />
|
||||
Cursed
|
||||
</p>
|
||||
<p v-if="categories.includes('worldgen')">
|
||||
<WorldGenCategory aria-hidden="true" />
|
||||
Worldgen
|
||||
</p>
|
||||
<p v-if="categories.includes('storage')">
|
||||
<StorageCategory aria-hidden="true" />
|
||||
Storage
|
||||
</p>
|
||||
<p v-if="categories.includes('food')">
|
||||
<FoodCategory aria-hidden="true" />
|
||||
Food
|
||||
</p>
|
||||
<p v-if="categories.includes('equipment')">
|
||||
<EquipmentCategory aria-hidden="true" />
|
||||
Equipment
|
||||
</p>
|
||||
<p v-if="categories.includes('misc')">
|
||||
<MiscCategory aria-hidden="true" />
|
||||
Misc
|
||||
</p>
|
||||
<span
|
||||
v-for="category in categoriesFiltered"
|
||||
:key="category.name"
|
||||
v-html="category.icon + category.name"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TechCategory from '~/assets/images/categories/tech.svg?inline'
|
||||
import AdventureCategory from '~/assets/images/categories/adventure.svg?inline'
|
||||
import CursedCategory from '~/assets/images/categories/cursed.svg?inline'
|
||||
import DecorationCategory from '~/assets/images/categories/decoration.svg?inline'
|
||||
import EquipmentCategory from '~/assets/images/categories/equipment.svg?inline'
|
||||
import FoodCategory from '~/assets/images/categories/food.svg?inline'
|
||||
import LibraryCategory from '~/assets/images/categories/library.svg?inline'
|
||||
import MagicCategory from '~/assets/images/categories/magic.svg?inline'
|
||||
import MiscCategory from '~/assets/images/categories/misc.svg?inline'
|
||||
import StorageCategory from '~/assets/images/categories/storage.svg?inline'
|
||||
import UtilityCategory from '~/assets/images/categories/utility.svg?inline'
|
||||
import WorldGenCategory from '~/assets/images/categories/worldgen.svg?inline'
|
||||
|
||||
import ForgeLoader from '~/assets/images/categories/forge.svg?inline'
|
||||
import FabricLoader from '~/assets/images/categories/fabric.svg?inline'
|
||||
|
||||
export default {
|
||||
name: 'Categories',
|
||||
components: {
|
||||
TechCategory,
|
||||
AdventureCategory,
|
||||
CursedCategory,
|
||||
DecorationCategory,
|
||||
EquipmentCategory,
|
||||
FoodCategory,
|
||||
LibraryCategory,
|
||||
MagicCategory,
|
||||
MiscCategory,
|
||||
StorageCategory,
|
||||
UtilityCategory,
|
||||
WorldGenCategory,
|
||||
ForgeLoader,
|
||||
FabricLoader,
|
||||
},
|
||||
props: {
|
||||
categories: {
|
||||
type: Array,
|
||||
@@ -102,30 +19,33 @@ export default {
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
categoriesFiltered() {
|
||||
return this.$tag.categories
|
||||
.concat(this.$tag.loaders)
|
||||
.filter((x) => this.categories.includes(x.name))
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.categories {
|
||||
@extend %row;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
p {
|
||||
span ::v-deep {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
background-color: var(--color-category-bg);
|
||||
border-radius: var(--size-rounded-max);
|
||||
color: var(--color-category-text);
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 0.25em;
|
||||
margin-right: 0.5em;
|
||||
padding: 0.4em 0.7em;
|
||||
font-size: var(--font-size-sm);
|
||||
height: 1em;
|
||||
color: var(--color-icon);
|
||||
margin-right: 1em;
|
||||
text-transform: capitalize;
|
||||
|
||||
svg {
|
||||
width: 15px;
|
||||
margin-right: 5px;
|
||||
width: 1rem;
|
||||
margin-right: 0.125rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<g transform="matrix(0.24,0,0,0.24,0,0)">
|
||||
<path
|
||||
d="M134.44,316.535C145.027,441.531 249.98,539.829 377.711,539.829C474.219,539.829 557.724,483.712 597.342,402.371L645.949,419.197C599.165,520.543 496.595,590.954 377.711,590.954C221.751,590.954 93.869,469.779 83.161,316.535L134.44,316.535ZM83.946,265.645C99.012,116.762 224.88,0.401 377.711,0.401C540.678,0.401 672.987,132.71 672.987,295.677C672.987,321.817 669.583,347.168 663.194,371.313L614.709,354.529C619.381,335.689 621.862,315.971 621.862,295.677C621.862,160.926 512.461,51.526 377.711,51.526C253.133,51.526 150.223,145.03 135.392,265.645L83.946,265.645Z"
|
||||
style="fill: rgb(94, 165, 69)"
|
||||
style="fill: var(--color-brand)"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
@@ -49,7 +49,7 @@
|
||||
<g transform="matrix(0.24,0,0,0.24,0,0)">
|
||||
<path
|
||||
d="M376.933,153.568C298.44,153.644 234.735,217.396 234.735,295.909C234.735,374.47 298.516,438.251 377.077,438.251C381.06,438.251 385.005,438.087 388.914,437.764L403.128,487.517C394.611,488.667 385.912,489.261 377.077,489.261C270.363,489.261 183.725,402.623 183.725,295.909C183.725,189.195 270.363,102.557 377.077,102.557C379.723,102.557 382.357,102.611 384.983,102.717L376.933,153.568ZM435.127,111.438C513.515,136.114 570.428,209.418 570.428,295.909C570.428,375.976 521.655,444.742 452.22,474.093L438.063,424.541C486.142,401.687 519.418,352.653 519.418,295.909C519.418,234.923 480.981,182.843 427.029,162.593L435.127,111.438Z"
|
||||
style="fill: rgb(94, 165, 69)"
|
||||
style="fill: var(--color-brand)"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
@@ -76,7 +76,7 @@
|
||||
<g transform="matrix(0.24,0,0,0.24,0,0)">
|
||||
<path
|
||||
d="M300.366,311.86L283.216,266.381L336.966,211.169L404.9,196.531L424.57,220.74L393.254,252.46L365.941,261.052L346.425,281.11L355.987,307.719L375.387,328.306L402.745,321.031L422.216,299.648L464.729,286.185L477.395,314.677L433.529,368.46L360.02,391.735L327.058,355.031L138.217,468.344C129.245,456.811 118.829,440.485 112.15,424.792L300.366,311.86Z"
|
||||
style="fill: rgb(94, 165, 69)"
|
||||
style="fill: var(--color-brand)"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
@@ -84,7 +84,7 @@
|
||||
<g transform="matrix(0.24,0,0,0.24,0,0)">
|
||||
<path
|
||||
d="M655.189,194.555L505.695,234.873C513.927,256.795 516.638,269.674 518.915,283.863L668.152,243.609C665.764,227.675 661.5,211.444 655.189,194.555Z"
|
||||
style="fill: rgb(94, 165, 69)"
|
||||
style="fill: var(--color-brand)"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
<template>
|
||||
<p
|
||||
<Checkbox
|
||||
class="filter"
|
||||
:class="{
|
||||
'filter-active': activeFilters.includes(facetName),
|
||||
cursed: displayName == 'FlameAnvil',
|
||||
}"
|
||||
@click="toggle"
|
||||
:value="activeFilters.includes(facetName)"
|
||||
@input="toggle()"
|
||||
>
|
||||
<slot></slot>
|
||||
{{ displayName }}
|
||||
</p>
|
||||
<div class="filter-text">
|
||||
<div v-if="icon" class="icon" v-html="icon"></div>
|
||||
<div v-else class="icon"><slot /></div>
|
||||
<span> {{ displayName }}</span>
|
||||
</div>
|
||||
</Checkbox>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Checkbox from '~/components/ui/Checkbox'
|
||||
|
||||
export default {
|
||||
name: 'SearchFilter',
|
||||
components: {
|
||||
Checkbox,
|
||||
},
|
||||
props: {
|
||||
facetName: {
|
||||
type: String,
|
||||
@@ -24,6 +29,10 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
activeFilters: {
|
||||
type: Array,
|
||||
default() {
|
||||
@@ -39,33 +48,28 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding: 0.4rem 0.3rem;
|
||||
margin: 3px 0 0 0.5rem;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 0.02rem;
|
||||
@extend %transparent-clickable;
|
||||
<style lang="scss" scoped>
|
||||
.filter ::v-deep {
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
padding: 0.2rem 0.3rem;
|
||||
.filter-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
height: 1rem;
|
||||
|
||||
svg {
|
||||
margin-right: 0.25rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
color: var(--color-icon);
|
||||
margin-right: 5px;
|
||||
height: 1rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-active {
|
||||
@extend %transparent-clickable, .selected;
|
||||
svg {
|
||||
color: var(--color-brand-light);
|
||||
span {
|
||||
text-transform: capitalize;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user