Versatile auth URLs & many UI fixes (#199)

* Make project cards right-align their last element

Spaces out elements in a `.project-card` using `justify-content: space-between;`.

Fixes modrinth/knossos#170

* Automatically set URL for auth redirect

* Make login button use base url or current origin

Allows the login button to work in dev environment

* Remove Axios base URL trailing slash

* Update authUrl() on dashboard to match default

* Remove 'code' query from URL on page load
Allow non-exact paths to highlight mod & dashboard tabs

Fixes modrinth/knossos#200

* Make page 5 button visible on page 4 (pagination)

Fixes modrinth/knossos#184

* Color links on legal pages

Fixes modrinth/knossos#166

* Set max notifications to 5 and ignore duplicates

Fixes modrinth/knossos#175

* Add space above report button when no user desc

Fixes modrinth/knossos#143

* Better text spacing from edge of mobile screen

Fixes modrinth/knossos#179

* Fix slanted bars in modrinth/knossos#57

* Fix checkbox grid and role label

Fixes modrinth/knossos#191

* Move mod 'settings' button to the far right

Fixes modrinth/knossos#138

* Abbreviate minutes to min. when time is too long

Not a perfect solution imo, but works for now

Fixes modrinth/knossos#193

* Fix mobile header margins & add breakpoints

Fixes modrinth/knossos#203

* Clean up nuxt config
Silence babel warning & styleResources

* Upgrade sass-loader to 10.1.1 and remove warning

* Remove added horizontal footer padding

https://github.com/modrinth/knossos/pull/199#discussion_r629011624

* Improve mobile header fix

* Fix up minor inconsistencies in mod header

* Remove hard coded date

* Cleans up pagination to be more intuitive

* Fixes member invite input on moble

* Fix login button when searching mods

* Improved mobile mod search

Consistently sized pagination buttons

Breakpoint for sort buttons on smaller screens

* Consistent link style on text-only pages

* Better 4k support

* Slightly better mobile project-card support

Shuffles categories under mod icon when there is room

* Animate homepage typewriter effect backwards

* Tiny commit to align mod icons in mod headers

* Make processing status include 'Under Review'

This can be later updated once the backend has a separate status

* Create vercel.json

* Update domain auto detection

* Test vercel NODE_ENV

* Remove console.log for debugging hosting services

* Make mobile first + fix shrinked text circle size

* Optimize SVG

* Change media queries to be more mobile first

* Remove `|| window.location.origin`

* re-deploy vercel

* Change "Processing" message to "Under review"
This commit is contained in:
venashial
2021-05-27 09:27:13 -07:00
committed by GitHub
parent b224f1d78d
commit 4d64df37f5
20 changed files with 397 additions and 100 deletions

View File

@@ -0,0 +1,6 @@
<svg width="24" height="24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="3" />
<path
d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z" />
</svg>

After

Width:  |  Height:  |  Size: 888 B

View File

@@ -82,7 +82,8 @@
word-wrap: break-word; word-wrap: break-word;
} }
h1, h2 { h1,
h2 {
padding: 10px 0 5px; padding: 10px 0 5px;
border-bottom: 1px solid var(--color-header-underline); border-bottom: 1px solid var(--color-header-underline);
} }
@@ -91,7 +92,7 @@
margin: 15px 0; margin: 15px 0;
padding: 0 1em; padding: 0 1em;
color: var(--color-text); color: var(--color-text);
border-left: .25em solid var(--color-block-quote); border-left: 0.25em solid var(--color-block-quote);
} }
a { a {
@@ -152,7 +153,7 @@
z-index: 1; z-index: 1;
} }
&[x-placement^="top"] { &[x-placement^='top'] {
margin-bottom: 5px; margin-bottom: 5px;
.tooltip-arrow { .tooltip-arrow {
@@ -167,7 +168,7 @@
} }
} }
&[x-placement^="bottom"] { &[x-placement^='bottom'] {
margin-top: 5px; margin-top: 5px;
.tooltip-arrow { .tooltip-arrow {
@@ -182,7 +183,7 @@
} }
} }
&[x-placement^="right"] { &[x-placement^='right'] {
margin-left: 5px; margin-left: 5px;
.tooltip-arrow { .tooltip-arrow {
@@ -197,7 +198,7 @@
} }
} }
&[x-placement^="left"] { &[x-placement^='left'] {
margin-right: 5px; margin-right: 5px;
.tooltip-arrow { .tooltip-arrow {
@@ -215,13 +216,13 @@
&[aria-hidden='true'] { &[aria-hidden='true'] {
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
transition: opacity .15s, visibility .15s; transition: opacity 0.15s, visibility 0.15s;
} }
&[aria-hidden='false'] { &[aria-hidden='false'] {
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
transition: opacity .15s; transition: opacity 0.15s;
} }
} }
@@ -247,7 +248,7 @@
svg { svg {
width: 1rem; width: 1rem;
height: 1rem; height: 1rem;
margin-right: 0.3rem; margin-right: 0.4rem;
} }
&:hover, &:hover,
@@ -259,7 +260,8 @@
} }
} }
&.nuxt-link-exact-active { &.nuxt-link-exact-active,
&.active-path {
color: var(--color-text-dark); color: var(--color-text-dark);
span { span {
@@ -293,7 +295,8 @@
flex-shrink: 0; flex-shrink: 0;
} }
&.nuxt-link-exact-active { &.nuxt-link-exact-active,
&.active-path {
svg { svg {
color: var(--color-brand-light); color: var(--color-brand-light);
} }
@@ -418,7 +421,6 @@
background: var(--color-button-bg-hover); background: var(--color-button-bg-hover);
} }
} }
} }
.section-header { .section-header {
@@ -488,11 +490,8 @@ label {
&:focus { &:focus {
outline: 0; outline: 0;
} }
} }
.stylized-toggle { .stylized-toggle {
height: 32px; height: 32px;
width: 52px; width: 52px;
@@ -501,7 +500,7 @@ label {
position: relative; position: relative;
margin: 0; margin: 0;
border: 2px solid var(--color-button-bg); border: 2px solid var(--color-button-bg);
transition: all .2s ease; transition: all 0.2s ease;
background: var(--color-button-bg); background: var(--color-button-bg);
&:after { &:after {
@@ -513,8 +512,8 @@ label {
height: 24px; height: 24px;
border-radius: 50%; border-radius: 50%;
background: white; background: white;
box-shadow: 0 1px 2px rgba(44,44,44,.2); box-shadow: 0 1px 2px rgba(44, 44, 44, 0.2);
transition: all .2s cubic-bezier(.5,.1,.75,1.35); transition: all 0.2s cubic-bezier(0.5, 0.1, 0.75, 1.35);
} }
&:checked { &:checked {
background: var(--color-brand); background: var(--color-brand);
@@ -527,7 +526,7 @@ label {
background: var(--color-button-bg); background: var(--color-button-bg);
border: 2px solid var(--color-button-bg); border: 2px solid var(--color-button-bg);
} }
&:hover:checked &:focus:checked { &:hover:checked &:focus:checked {
background: var(--color-brand); background: var(--color-brand);
border: 2px solid var(--color-brand); border: 2px solid var(--color-brand);
} }

View File

@@ -25,7 +25,8 @@
&:focus, &:focus,
&:hover, &:hover,
&.selected, &.selected,
&.nuxt-link-exact-active { &.nuxt-link-exact-active,
&.active-path {
color: var(--color-transparent-button-text-hover); color: var(--color-transparent-button-text-hover);
background-color: var(--color-transparent-button-bg-hover); background-color: var(--color-transparent-button-bg-hover);
} }

View File

@@ -1,5 +1,5 @@
<template> <template>
<footer :class="{ centered, hideBig, hideSmall }"> <footer :class="{ centered, padded, hideBig, hideSmall }">
<span> <span>
Modrinth is open source software. You may view the source code at Modrinth is open source software. You may view the source code at
<a target="_blank" href="https://github.com/modrinth">our GitHub page</a>. <a target="_blank" href="https://github.com/modrinth">our GitHub page</a>.
@@ -55,6 +55,10 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
padded: {
type: Boolean,
default: false,
},
hideSmall: { hideSmall: {
type: Boolean, type: Boolean,
default: false, default: false,
@@ -78,8 +82,12 @@ export default {
align-items: center; align-items: center;
} }
.padded {
padding: 2rem 1rem;
}
footer { footer {
padding: 2rem 0 2rem 0; padding: 2rem 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -10,17 +10,22 @@
</button> </button>
<div <div
v-for="(item, index) in pages" v-for="(item, index) in pages"
:key="'page-' + item" :key="'page-' + item + '-' + index"
:class="{ :class="{
'page-number': currentPage !== item, 'page-number': currentPage !== item,
shrink: item > 99,
}" }"
class="page-number-container" class="page-number-container"
> >
<div v-if="pages[index - 1] + 1 !== item && item !== 1" class="has-icon"> <div v-if="item == '-'" class="has-icon">
<GapIcon /> <GapIcon />
</div> </div>
<button <button
:class="{ 'page-number current': currentPage === item }" v-else
:class="{
'page-number current': currentPage === item,
shrink: item > 99,
}"
@click="currentPage !== item ? switchPage(item) : null" @click="currentPage !== item ? switchPage(item) : null"
> >
{{ item }} {{ item }}
@@ -28,7 +33,9 @@
</div> </div>
<button <button
:class="{ disabled: currentPage === pages[pages.length - 1] }" :class="{
disabled: currentPage === pages[pages.length - 1],
}"
class="paginate has-icon" class="paginate has-icon"
aria-label="Next Page" aria-label="Next Page"
@click=" @click="
@@ -76,10 +83,11 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
button { button {
min-width: 2rem; padding: 0;
padding: 0 0.5rem; margin: 0;
height: 2rem; width: 2em;
border-radius: 2rem; height: 2em;
border-radius: 2em;
background: transparent; background: transparent;
&.page-number.current { &.page-number.current {
background: var(--color-button-bg-hover); background: var(--color-button-bg-hover);
@@ -100,15 +108,37 @@ button {
.has-icon { .has-icon {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0 0.5rem; height: 2em;
height: 2rem;
svg { svg {
width: 1rem; width: 1em;
} }
} }
.page-number-container { .page-number-container,
button,
.has-icon {
display: flex; display: flex;
max-height: 2rem; justify-content: center;
align-items: center;
height: 2em;
width: 2em;
}
.paginates {
height: 2em;
margin: 0.5rem 0;
> div {
margin: 0 0.1em;
}
font-size: 80%;
@media screen and (min-width: 350px) {
font-size: 100%;
}
}
.shrink {
font-size: 0.9rem;
height: 2.225em;
width: 2.225em;
} }
</style> </style>

View File

@@ -9,6 +9,7 @@
loading="lazy" loading="lazy"
/> />
</nuxt-link> </nuxt-link>
<Categories :categories="categories" class="left-categories" />
</div> </div>
<div class="info"> <div class="info">
<div class="top"> <div class="top">
@@ -40,7 +41,7 @@
>Draft</span >Draft</span
> >
<span v-if="status === 'processing'" class="badge yellow"> <span v-if="status === 'processing'" class="badge yellow">
Processing Under review
</span> </span>
<span v-if="status === 'unlisted'" class="badge gray"> <span v-if="status === 'unlisted'" class="badge gray">
Unlisted Unlisted
@@ -99,7 +100,7 @@
</div> </div>
</div> </div>
</div> </div>
<Categories :categories="categories" /> <Categories :categories="categories" class="right-categories" />
</div> </div>
</div> </div>
</div> </div>
@@ -207,6 +208,7 @@ export default {
@media screen and (min-width: 1024px) { @media screen and (min-width: 1024px) {
flex-direction: row; flex-direction: row;
justify-content: space-between;
} }
.icon { .icon {
@@ -280,6 +282,19 @@ export default {
} }
} }
} }
.left-categories {
display: none;
}
@media screen and (max-width: 560px) {
.left-categories {
display: flex;
margin: 0 0 0.75rem 0.75rem;
width: 7rem;
}
.right-categories {
display: none;
}
}
.buttons { .buttons {
@extend %column; @extend %column;
margin-bottom: 1rem; margin-bottom: 1rem;

View File

@@ -4,16 +4,18 @@
<section class="navbar columns"> <section class="navbar columns">
<section class="logo column"> <section class="logo column">
<NuxtLink to="/"> <NuxtLink to="/">
<ModrinthLogoSmall aria-label="modrinth" class="small-logo" />
<ModrinthLogo <ModrinthLogo
v-if="$colorMode.value === 'light'" v-if="$colorMode.value === 'light'"
aria-label="modrinth" aria-label="modrinth"
class="text-logo"
/> />
<ModrinthLogoWhite v-else aria-label="modrinth" /> <ModrinthLogoWhite v-else aria-label="modrinth" class="text-logo" />
</NuxtLink> </NuxtLink>
<span class="badge yellow">Beta</span> <span class="badge yellow">Beta</span>
</section> </section>
<section class="menu-icon"> <section class="menu-icon">
<button @click="changeTheme"> <button class="theme-toggle" @click="changeTheme">
<MoonIcon v-if="$colorMode.value === 'light'" /> <MoonIcon v-if="$colorMode.value === 'light'" />
<SunIcon v-else /> <SunIcon v-else />
</button> </button>
@@ -24,11 +26,23 @@
<section ref="nav" class="right-group columns"> <section ref="nav" class="right-group columns">
<section class="column-grow-5 nav"> <section class="column-grow-5 nav">
<div class="tabs"> <div class="tabs">
<NuxtLink to="/mods" class="tab"> <NuxtLink
to="/mods"
class="tab"
:class="{
'active-path': this.$route.path.startsWith('/mod'),
}"
>
<span>Mods</span> <span>Mods</span>
</NuxtLink> </NuxtLink>
<div v-if="this.$auth.user" class="section"> <div v-if="this.$auth.user" class="section">
<NuxtLink to="/dashboard/projects" class="tab"> <NuxtLink
to="/dashboard/projects"
class="tab"
:class="{
'active-path': this.$route.path.startsWith('/dashboard'),
}"
>
<span>Dashboard</span> <span>Dashboard</span>
</NuxtLink> </NuxtLink>
</div> </div>
@@ -108,7 +122,12 @@
</header> </header>
<main> <main>
<CookieConsent /> <CookieConsent />
<notifications group="main" position="bottom right" /> <notifications
group="main"
position="bottom right"
:max="5"
:ignore-duplicates="true"
/>
<!--<notifications <!--<notifications
group="ads" group="ads"
position="bottom right" position="bottom right"
@@ -125,6 +144,7 @@ import ClickOutside from 'vue-click-outside'
import ModrinthLogo from '~/assets/images/text-logo.svg?inline' import ModrinthLogo from '~/assets/images/text-logo.svg?inline'
import ModrinthLogoWhite from '~/assets/images/text-logo-white.svg?inline' import ModrinthLogoWhite from '~/assets/images/text-logo-white.svg?inline'
import ModrinthLogoSmall from '~/assets/images/logo.svg?inline'
import HamburgerIcon from '~/assets/images/utils/hamburger.svg?inline' import HamburgerIcon from '~/assets/images/utils/hamburger.svg?inline'
@@ -144,6 +164,7 @@ export default {
components: { components: {
ModrinthLogo, ModrinthLogo,
ModrinthLogoWhite, ModrinthLogoWhite,
ModrinthLogoSmall,
DropdownIcon, DropdownIcon,
MoonIcon, MoonIcon,
SunIcon, SunIcon,
@@ -164,7 +185,7 @@ export default {
}, },
computed: { computed: {
authUrl() { authUrl() {
return `https://api.modrinth.com/api/v1/auth/init?url=https://modrinth.com${this.$route.fullPath}` return `https://api.modrinth.com/api/v1/auth/init?url=${process.env.domain}${this.$route.fullPath}`
}, },
userUrl() { userUrl() {
return `/user/${this.$auth.user.id}` return `/user/${this.$auth.user.id}`
@@ -179,6 +200,11 @@ export default {
document.body.style.overflow = 'auto' document.body.style.overflow = 'auto'
}, },
}, },
beforeCreate() {
if (this.$route.query.code) {
this.$router.push(this.$route.path)
}
},
methods: { methods: {
toggleNavBar() { toggleNavBar() {
window.scrollTo(0, 0) window.scrollTo(0, 0)
@@ -198,18 +224,19 @@ export default {
}, },
async logout() { async logout() {
this.$cookies.set('auth-token-reset', true) this.$cookies.set('auth-token-reset', true)
// If users logs out on dashboard, redirect on the home page // If users logs out on dashboard, force redirect on the home page to clear cookies
if (this.$route.path.startsWith('/dashboard')) { if (this.$route.path.startsWith('/dashboard')) {
await this.$router.push('/') window.location.href = '/'
} else { } else {
await this.$router.go(null) await this.$router.go(null)
this.$notify({
group: 'main',
title: 'Logged Out',
text: 'You have logged out successfully!',
type: 'success',
})
} }
this.$notify({
group: 'main',
title: 'Logged Out',
text: 'You have logged out successfully!',
type: 'success',
})
}, },
changeTheme() { changeTheme() {
this.$colorMode.preference = this.$colorMode.preference =
@@ -229,7 +256,10 @@ export default {
background-color: var(--color-raised-bg); background-color: var(--color-raised-bg);
max-width: 100vw; max-width: 100vw;
.navbar { .navbar {
margin: 0 var(--spacing-card-lg); margin: 0 0.5rem;
@media screen and (min-width: 450px) {
margin: 0 var(--spacing-card-lg);
}
section.logo { section.logo {
align-items: center; align-items: center;
display: flex; display: flex;
@@ -237,12 +267,28 @@ export default {
padding: 1rem 0; padding: 1rem 0;
margin-left: 1rem; margin-left: 1rem;
color: var(--color-text-dark); color: var(--color-text-dark);
.small-logo {
display: block;
}
svg { svg {
display: none;
height: 1.75rem; height: 1.75rem;
width: auto; width: auto;
} }
@media screen and (min-width: 350px) {
.small-logo {
display: none;
}
svg {
display: unset;
}
}
.badge { .badge {
margin-left: 0.25rem; margin-left: 0.25rem;
display: none;
@media screen and (min-width: 430px) {
display: unset;
}
} }
button { button {
background: none; background: none;

View File

@@ -37,7 +37,7 @@ export default async function (context) {
if (!context.$auth.user) { if (!context.$auth.user) {
return context.redirect( return context.redirect(
`https://api.modrinth.com/api/v1/auth/init?url=https://modrinth.com${context.route.fullPath}` `https://api.modrinth.com/api/v1/auth/init?url=${process.env.domain}${context.route.fullPath}`
) )
} }
} }

View File

@@ -150,15 +150,18 @@ export default {
*/ */
build: { build: {
transpile: ['vue-tooltip', 'vue-notification'], transpile: ['vue-tooltip', 'vue-notification'],
styleResources: {
scss: './assets/styles/injected.scss',
},
html: { html: {
minify: { minify: {
collapseWhitespace: true, // as @dario30186 mentioned collapseWhitespace: true, // as @dario30186 mentioned
removeComments: true, // 👈 add this line removeComments: true, // 👈 add this line
}, },
}, },
babel: {
plugins: [['@babel/plugin-proposal-private-methods', { loose: true }]],
},
},
styleResources: {
scss: './assets/styles/injected.scss',
}, },
loading: { loading: {
color: 'green', color: 'green',
@@ -166,6 +169,7 @@ export default {
}, },
env: { env: {
version: process.env.VERSION_ID || 'unknown', version: process.env.VERSION_ID || 'unknown',
domain: getDomain(),
}, },
publicRuntimeConfig: { publicRuntimeConfig: {
ads: { ads: {
@@ -179,3 +183,17 @@ export default {
}, },
}, },
} }
function getDomain() {
if (process.env.NODE_ENV === 'production') {
if (process.env.HEROKU_APP_NAME) {
return `https://${process.env.HEROKU_APP_NAME}.herokuapp.com`
} else if (process.env.VERCEL_URL) {
return `https://${process.env.VERCEL_URL}`
} else {
return 'https://modrinth.com'
}
} else {
return 'http://localhost:3000'
}
}

View File

@@ -110,5 +110,6 @@ export default {
a { a {
text-decoration: underline; text-decoration: underline;
color: var(--color-link);
} }
</style> </style>

View File

@@ -78,7 +78,7 @@ export default {
}, },
computed: { computed: {
authUrl() { authUrl() {
return `https://api.modrinth.com/api/v1/auth/init?url=https://modrinth.com${this.$route.fullPath}` return `https://api.modrinth.com/api/v1/auth/init?url=${process.env.domain}${this.$route.fullPath}`
}, },
}, },
} }

View File

@@ -54,7 +54,7 @@ export default {
async logout() { async logout() {
this.$cookies.set('auth-token-reset', true) this.$cookies.set('auth-token-reset', true)
await this.$router.replace( await this.$router.replace(
'https://api.modrinth.com/api/v1/auth/init?url=https://modrinth.com/' `https://api.modrinth.com/api/v1/auth/init?url=${process.env.domain}${this.$route.fullPath}`
) )
}, },
}, },

View File

@@ -171,7 +171,7 @@ fetch('https://api.modrinth.com/api/v1/mod').then(res => res.json()).then(data =
</pre> </pre>
</div> </div>
</div> </div>
<m-footer class="footer" centered /> <m-footer class="footer" centered padded />
</div> </div>
</template> </template>
@@ -188,6 +188,7 @@ export default {
data() { data() {
return { return {
currentText: 'Open source', currentText: 'Open source',
increasing: true,
texts: ['Open source', 'Easy to use', 'Developer focused', 'API Based'], texts: ['Open source', 'Easy to use', 'Developer focused', 'API Based'],
} }
}, },
@@ -205,16 +206,29 @@ export default {
}) })
}, },
typeWriter(text, i, callback) { typeWriter(text, i, callback) {
if (!text || i >= text.length) { if (!this.increasing && i <= 0) {
setTimeout(callback, 1000 + Math.random() * 500) this.increasing = true
setTimeout(callback, 300 + Math.random() * 50)
return return
} }
this.currentText = text.substring(0, i + 1) const step = this.increasing ? 1 : -1
setTimeout(
() => this.typeWriter(text, i + 1, callback), if (i >= text.length && this.increasing) {
150 + Math.random() * 50 this.increasing = false
)
setTimeout(
() => this.typeWriter(text, i + step, callback),
1300 + Math.random() * 500
)
} else {
this.currentText = text.substring(0, i + step)
const speed = this.increasing ? 140 : 50
setTimeout(
() => this.typeWriter(text, i + step, callback),
speed + Math.random() * 20
)
}
}, },
}, },
} }
@@ -233,6 +247,9 @@ export default {
.left { .left {
padding-top: 75px; padding-top: 75px;
padding-left: 100px; padding-left: 100px;
@media screen and (min-width: 1500px) {
padding-left: 15%;
}
.typewriter { .typewriter {
display: inline-block; display: inline-block;
@@ -287,6 +304,11 @@ export default {
font-weight: bold; font-weight: bold;
} }
} }
&.left {
@media screen and (min-width: 2048px) {
max-width: 15vw;
}
}
} }
.hero-image { .hero-image {
@@ -311,7 +333,7 @@ export default {
background: inherit; background: inherit;
content: ''; content: '';
display: block; display: block;
height: 50%; height: 100%;
left: 0; left: 0;
position: absolute; position: absolute;
right: 0; right: 0;
@@ -323,12 +345,18 @@ export default {
&:before { &:before {
top: 0; top: 0;
transform: skewY(5deg); transform: skewY(5deg);
@media screen and (min-width: 2048px) {
transform: skewY(2deg);
}
transform-origin: 100% 0; transform-origin: 100% 0;
} }
&:after { &:after {
bottom: 0; bottom: 0;
transform: skewY(-5deg); transform: skewY(-5deg);
@media screen and (min-width: 2048px) {
transform: skewY(-2deg);
}
transform-origin: 100%; transform-origin: 100%;
} }
} }
@@ -388,6 +416,12 @@ export default {
padding-left: 0 !important; padding-left: 0 !important;
text-align: center; text-align: center;
width: 100%; width: 100%;
h1,
h3,
p {
padding: 0 5vw;
}
} }
.hero { .hero {

View File

@@ -228,4 +228,9 @@ export default {
@extend %card; @extend %card;
padding: var(--spacing-card-sm) var(--spacing-card-lg); padding: var(--spacing-card-sm) var(--spacing-card-lg);
} }
a {
text-decoration: underline;
color: var(--color-link);
}
</style> </style>

View File

@@ -201,4 +201,9 @@ export default {
@extend %card; @extend %card;
padding: var(--spacing-card-sm) var(--spacing-card-lg); padding: var(--spacing-card-sm) var(--spacing-card-lg);
} }
a {
text-decoration: underline;
color: var(--color-link);
}
</style> </style>

View File

@@ -84,13 +84,6 @@
> >
<span>Versions</span> <span>Versions</span>
</nuxt-link> </nuxt-link>
<nuxt-link
v-if="currentMember"
:to="'/mod/' + (mod.slug ? mod.slug : mod.id) + '/settings'"
class="tab"
>
<span>Settings</span>
</nuxt-link>
<a <a
v-if="mod.wiki_url" v-if="mod.wiki_url"
:href="mod.wiki_url" :href="mod.wiki_url"
@@ -127,7 +120,14 @@
<ExternalIcon /> <ExternalIcon />
<span>Discord</span> <span>Discord</span>
</a> </a>
<div class="filler" /> <nuxt-link
v-if="currentMember"
:to="'/mod/' + mod.id + '/settings'"
class="tab settings-tab"
>
<SettingsIcon />
<span>Settings</span>
</nuxt-link>
</div> </div>
</div> </div>
<div class="mod-content"> <div class="mod-content">
@@ -162,7 +162,7 @@
" "
class="value" class="value"
> >
{{ $dayjs(mod.published).fromNow() }} {{ formatTime(mod.published) }}
</p> </p>
</div> </div>
</div> </div>
@@ -195,7 +195,7 @@
" "
class="value" class="value"
> >
{{ $dayjs(mod.updated).fromNow() }} {{ formatTime(mod.updated) }}
</p> </p>
</div> </div>
</div> </div>
@@ -368,6 +368,7 @@ import ReportIcon from '~/assets/images/utils/report.svg?inline'
import FollowIcon from '~/assets/images/utils/heart.svg?inline' import FollowIcon from '~/assets/images/utils/heart.svg?inline'
import ExternalIcon from '~/assets/images/utils/external.svg?inline' import ExternalIcon from '~/assets/images/utils/external.svg?inline'
import SettingsIcon from '~/assets/images/utils/settings.svg?inline'
import ForgeIcon from '~/assets/images/categories/forge.svg?inline' import ForgeIcon from '~/assets/images/categories/forge.svg?inline'
import FabricIcon from '~/assets/images/categories/fabric.svg?inline' import FabricIcon from '~/assets/images/categories/fabric.svg?inline'
@@ -380,6 +381,7 @@ export default {
MFooter, MFooter,
Categories, Categories,
ExternalIcon, ExternalIcon,
SettingsIcon,
ForgeIcon, ForgeIcon,
FabricIcon, FabricIcon,
DownloadIcon, DownloadIcon,
@@ -501,6 +503,13 @@ export default {
this.userFollows.splice(this.userFollows.indexOf(this.mod.id), 1) this.userFollows.splice(this.userFollows.indexOf(this.mod.id), 1)
}, },
formatTime(date) {
let defaultMessage = this.$dayjs(date).fromNow()
if (defaultMessage.length > 13) {
defaultMessage = defaultMessage.replace('minutes', 'min.')
}
return defaultMessage
},
}, },
head() { head() {
return { return {
@@ -557,32 +566,42 @@ export default {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
text-align: center; text-align: center;
padding: 1rem;
@extend %card-spaced-b; @extend %card-spaced-b;
width: 100%;
.icon { .icon {
margin: auto 0; margin: unset 0;
height: 6.08rem;
@media screen and (min-width: 1024px) {
align-self: flex-start;
}
img { img {
width: 6rem; height: 100%;
height: 6rem; width: auto;
margin: var(--spacing-card-md); margin: 0;
border-radius: var(--size-rounded-icon); border-radius: var(--size-rounded-icon);
object-fit: contain;
} }
} }
.info { .info {
@extend %column; @extend %column;
display: flex;
justify-content: flex-start;
height: calc(100% - 0.2rem);
p {
margin: 0;
}
.title { .title {
margin: var(--spacing-card-md) var(--spacing-card-md) 0 0; margin: 0;
color: var(--color-text-dark); color: var(--color-text-dark);
font-size: var(--font-size-lg); font-size: var(--font-size-lg);
} }
.description { .description {
margin: var(--spacing-card-sm) var(--spacing-card-md) 0 0; margin-top: var(--spacing-card-sm);
margin-bottom: 0.5rem;
color: var(--color-text-dark); color: var(--color-text-dark);
} }
.alt-nav { .alt-nav {
margin: var(--spacing-card-sm) var(--spacing-card-md) 0 0; margin-top: auto;
p { p {
margin: 0; margin: 0;
} }
@@ -590,30 +609,73 @@ export default {
} }
.buttons { .buttons {
@extend %row; @extend %row;
margin: var(--spacing-card-md) var(--spacing-card-md) var(--spacing-card-md)
0;
button, button,
a { a {
margin: 0.2rem 0 0 0.2rem; margin: 0;
padding: 0.2rem 0.5rem;
display: flex;
&:not(:last-child) {
margin-right: 0.5rem;
@media screen and (min-width: 1024px) {
margin-right: 0;
margin-bottom: 0.5rem;
}
}
}
}
> div {
margin-bottom: 1rem;
&:last-child {
margin-bottom: 0;
} }
} }
@media screen and (min-width: 1024px) { @media screen and (min-width: 1024px) {
align-items: unset; display: grid;
text-align: unset; grid-template-columns: auto 1fr auto;
flex-direction: row; padding: 1rem;
grid-gap: 1rem;
text-align: left;
.buttons { .buttons {
align-self: flex-start;
flex-direction: column; flex-direction: column;
margin-left: auto; }
> div {
margin-bottom: 0;
} }
} }
} }
.mod-navigation { .mod-navigation {
@extend %card-spaced-b; @extend %card-spaced-b;
padding-bottom: 0.2rem; padding: 0 1rem;
.tabs {
overflow-x: auto;
padding: 0;
.tab {
padding: 0;
margin: 0.9rem 0.5rem 0.8rem 0.5rem;
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
}
}
.settings-tab {
@media screen and (min-width: 1024px) {
margin-left: auto !important;
}
}
} }
.mod-info { .mod-info {

View File

@@ -42,7 +42,7 @@
</div> </div>
</label> </label>
</section> </section>
<div class="section-header columns"> <div class="section-header columns team-invite">
<h3 class="column-grow-1">Team members</h3> <h3 class="column-grow-1">Team members</h3>
<div class="column"> <div class="column">
<input <input
@@ -417,12 +417,30 @@ export default {
.main-info { .main-info {
margin-bottom: var(--spacing-card-lg); margin-bottom: var(--spacing-card-lg);
@media screen and (min-width: 1024px) {
label {
align-items: center;
input {
margin-left: 1rem;
}
}
}
} }
.permissions { .permissions {
margin: 1rem 0; margin: 1rem 0;
max-width: 45rem;
display: grid; display: grid;
grid-template-columns: 10rem 10rem 10rem; grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
grid-template-rows: 1.5rem 1.5rem 1.5rem; grid-gap: 0.5rem;
label {
flex-direction: row;
input {
flex: none;
margin-right: 0.5rem;
}
}
} }
} }
@@ -469,10 +487,37 @@ section {
text-align: center; text-align: center;
height: fit-content; height: fit-content;
flex: 1; flex: 1;
@media screen and (max-width: 1024px) {
margin: 0.5rem 0 1rem 0;
}
} }
div:hover { div:hover {
cursor: pointer; cursor: pointer;
} }
} }
} }
.team-invite {
@media screen and (max-width: 1024px) {
flex-direction: column;
h3 {
margin-bottom: 0.5rem;
}
}
> div {
input {
margin-right: 1rem;
}
@media screen and (max-width: 500px) {
display: flex;
flex-direction: column;
input {
margin: 0;
}
button {
margin-top: 0.5rem;
}
}
}
}
</style> </style>

View File

@@ -613,9 +613,11 @@ export default {
this.currentPage = newPageNumber this.currentPage = newPageNumber
if (pageAmount > 4) { if (pageAmount > 4) {
if (this.currentPage + 1 >= pageAmount) { if (this.currentPage + 3 >= pageAmount) {
this.pages = [ this.pages = [
1, 1,
'-',
pageAmount - 4,
pageAmount - 3, pageAmount - 3,
pageAmount - 2, pageAmount - 2,
pageAmount - 1, pageAmount - 1,
@@ -624,13 +626,15 @@ export default {
} else if (this.currentPage > 4) { } else if (this.currentPage > 4) {
this.pages = [ this.pages = [
1, 1,
'-',
this.currentPage - 1, this.currentPage - 1,
this.currentPage, this.currentPage,
this.currentPage + 1, this.currentPage + 1,
'-',
pageAmount, pageAmount,
] ]
} else { } else {
this.pages = [1, 2, 3, 4, pageAmount] this.pages = [1, 2, 3, 4, 5, '-', pageAmount]
} }
} else { } else {
this.pages = Array.from({ length: pageAmount }, (_, i) => i + 1) this.pages = Array.from({ length: pageAmount }, (_, i) => i + 1)
@@ -716,6 +720,12 @@ export default {
margin-right: 0.5rem; margin-right: 0.5rem;
display: flex; display: flex;
width: auto; width: auto;
@media screen and (max-width: 350px) {
flex-direction: column;
.mobile-filters-button {
margin: 0.5rem 0 0 0;
}
}
.per-page { .per-page {
margin-left: 0.5rem; margin-left: 0.5rem;
display: none; display: none;
@@ -739,7 +749,7 @@ export default {
.search-bottom { .search-bottom {
align-items: center; align-items: center;
display: flex; display: flex;
justify-content: flex-end; justify-content: center;
background: var(--color-raised-bg); background: var(--color-raised-bg);
border-radius: var(--size-rounded-card); border-radius: var(--size-rounded-card);
padding: 0 1rem; padding: 0 1rem;
@@ -752,6 +762,7 @@ export default {
} }
@media screen and (min-width: 550px) { @media screen and (min-width: 550px) {
padding: 0.25rem 1rem 0.25rem 1rem; padding: 0.25rem 1rem 0.25rem 1rem;
justify-content: flex-end;
.per-page { .per-page {
display: unset; display: unset;
} }

View File

@@ -227,6 +227,8 @@ export default {
.buttons { .buttons {
@extend %column; @extend %column;
margin-top: 16px;
.iconified-button { .iconified-button {
max-width: 4.5rem; max-width: 4.5rem;
} }

9
vercel.json Normal file
View File

@@ -0,0 +1,9 @@
{
"builds": [
{
"src": "nuxt.config.js",
"use": "@nuxtjs/vercel-builder",
"config": {}
}
]
}