Modrinth/layouts/default.vue
2020-10-02 21:05:43 -07:00

546 lines
14 KiB
Vue

<template>
<div class="layout">
<aside>
<input
class="hamburger-button"
alt="Open navigation menu"
type="checkbox"
@click="toggleNavMenu()"
/>
<!-- TODO: Probably shouldn't be a Unicode symbol -->
<div class="hamburger-icon">☰</div>
<nuxt-link to="/" class="logo-wrapper">
<img class="logo" src="~/assets/images/logo.svg" />
<span class="name">modrinth</span>
</nuxt-link>
<nav>
<section class="links">
<h3>Projects</h3>
<section>
<nuxt-link to="/modpacks">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="16.5" y1="9.4" x2="7.5" y2="4.21" />
<path
d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"
/>
<polyline points="3.27 6.96 12 12.01 20.73 6.96" />
<line x1="12" y1="22.08" x2="12" y2="12" />
</svg>
<span> Modpacks </span>
</nuxt-link>
<nuxt-link to="/mods">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="16 18 22 12 16 6" />
<polyline points="8 6 2 12 8 18" />
</svg>
<span>Mods</span>
</nuxt-link>
</section>
<h3>Community</h3>
<section>
<nuxt-link to="/support">
<svg
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 cx="12" cy="12" r="4" />
<line x1="4.93" y1="4.93" x2="9.17" y2="9.17" />
<line x1="14.83" y1="14.83" x2="19.07" y2="19.07" />
<line x1="14.83" y1="9.17" x2="18.36" y2="5.64" />
<line x1="4.93" y1="19.07" x2="9.17" y2="14.83" />
</svg>
<span>Support</span>
</nuxt-link>
<nuxt-link to="/guides">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
</svg>
<span>Guides</span>
</nuxt-link>
</section>
<h3 v-if="this.$auth.loggedIn">Dashboard</h3>
<section v-if="this.$auth.loggedIn">
<nuxt-link to="/dashboard/projects">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"
/>
</svg>
<span>My projects</span>
</nuxt-link>
<nuxt-link to="/dashboard/analytics">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
</svg>
<span>Analytics</span>
</nuxt-link>
</section>
</section>
<div class="disclosure">
<span>
Modrinth is open source software. You may view the source code at
<a href="https://github.com/modrinth/knossos"
>our GitHub Repository</a
>.
</span>
</div>
<section class="user-actions">
<a
v-if="!this.$auth.loggedIn"
:href="
'https://api.modrinth.com/api/v1/auth/init?url=http://localhost:3000' +
this.$route.path
"
class="log-in-button"
>Log In</a
>
<div v-if="this.$auth.loggedIn" class="avatar">
<img :src="this.$auth.user.avatar_url" />
<span> {{ this.$auth.user.username }} </span>
</div>
<div v-if="this.$auth.loggedIn" class="notifications">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" />
<path d="M13.73 21a2 2 0 0 1-3.46 0" />
</svg>
</div>
<div class="theme">
<svg
v-if="theme === 'light'"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
@click="switchTheme"
>
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>
<svg
v-else
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
@click="switchTheme"
>
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</div>
</section>
</nav>
</aside>
<main>
<!-- <header>
<div class="search-wrapper">
<input type="search" name="search" id="search" placeholder="Search...">
<!/-- Icon follows and not precedes the input so we can target it with CSS' ~ selector --/>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round">
<circle cx="11" cy="11" r="8"/>
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
</div>
</header> -->
<nuxt />
</main>
</div>
</template>
<script>
export default {
data() {
return {
theme: 'light',
}
},
created() {
if (this.$route.query.code) {
this.$auth.setUserToken(this.$route.query.code)
console.log(this.$auth.user)
}
},
beforeMount() {
const theme = localStorage.getItem('data-theme')
? localStorage.getItem('data-theme')
: 'light'
this.theme = theme
document.documentElement.setAttribute('data-theme', theme)
},
methods: {
switchTheme() {
let theme = localStorage.getItem('data-theme')
if (theme === 'dark') {
theme = 'light'
} else {
theme = 'dark'
}
this.theme = theme
localStorage.setItem('data-theme', theme)
document.documentElement.setAttribute('data-theme', theme)
},
toggleNavMenu() {
document.body.style.overflow =
document.body.style.overflow !== 'hidden' ? 'hidden' : 'auto'
},
},
}
</script>
<style lang="scss">
.layout {
display: flex;
flex-flow: column;
min-height: 100vh;
width: 100%;
// Desktop
@media screen and (min-width: 1145px) {
flex-flow: row;
}
aside {
top: 0;
position: sticky;
border-right: 0;
display: flex; // Flex here to safely expand navigation height
flex-direction: column;
width: 100vw;
max-height: 100vh;
background: var(--color-bg);
z-index: 10;
.logo-wrapper {
align-items: center;
display: flex;
height: 3.5rem;
width: 100vw;
font-family: 'Montserrat', sans-serif;
.logo {
height: 2rem;
width: auto;
margin-left: 2.5rem;
}
.name {
font-family: 'Montserrat Alternates', serif;
margin-left: 0.4rem;
font-size: 1.3rem;
}
}
.hamburger-button {
position: absolute;
display: block;
left: 10px;
opacity: 0;
margin: 0;
top: 1.2rem;
width: 30px;
height: 30px;
cursor: pointer;
}
.hamburger-icon {
display: block;
position: absolute;
left: 15px;
top: 1.2rem;
pointer-events: none;
}
.hamburger-button:checked ~ nav {
left: 0;
}
nav {
display: flex;
flex-direction: column;
flex-grow: 1;
justify-content: space-between;
position: absolute;
height: calc(100vh - 3.5rem);
width: 100vw;
left: -100vw;
top: 3.5rem;
transition: left 150ms;
background: var(--color-bg);
overflow-y: auto;
z-index: 10;
// Larger screens that still need a collapsed menu
@media screen and (min-width: 900px) {
width: 300px;
left: -300px;
}
& > * {
padding: 0 0.75rem;
}
.links {
h3 {
color: #718096;
font-size: 0.8rem;
letter-spacing: 0.02rem;
margin-bottom: 0.5rem;
margin-top: 1.5rem;
text-transform: uppercase;
}
section {
border-left: 4px solid var(--color-grey-3);
a {
align-items: center;
border-radius: 0 0.25rem 0.25rem 0;
color: var(--color-grey-5);
display: flex;
margin-bottom: 0.25rem;
padding: 0.5rem 1rem;
&:hover,
&:focus,
&.nuxt-link-active {
background-color: var(--color-grey-1);
color: var(--color-text);
}
&.nuxt-link-active {
box-shadow: -4px 0 0 0 var(--color-brand);
}
svg {
height: 1rem;
width: 1rem;
flex-shrink: 0;
}
span {
margin-left: 0.5rem;
}
}
}
}
.user-actions {
align-items: center;
border-top: 2px solid var(--color-grey-2);
display: flex;
justify-content: space-between;
margin-top: 1rem;
padding-bottom: 1rem;
padding-top: 1rem;
& > * {
align-items: center;
display: flex;
}
svg {
color: var(--color-grey-5);
&:hover,
&:focus {
color: inherit;
}
}
.avatar {
img {
border-radius: 50%;
height: 2rem;
margin-right: 0.5rem;
width: 2rem;
}
}
.log-in-button {
text-align: center;
padding: 8px 40px;
border-radius: 5px;
color: var(--color-grey-5);
background-color: var(--color-grey-1);
margin-left: 2.5rem;
}
}
}
// Desktop
@media screen and (min-width: 1145px) {
border-right: 1px solid var(--color-grey-2);
min-width: 270px;
max-width: 270px;
nav {
height: 100%;
left: 0;
width: 100%;
transition: none;
position: static;
}
.logo-wrapper {
padding: 0 0 0 1.5rem;
width: 100%;
.logo {
margin: 0;
}
}
.hamburger-button,
.hamburger-icon {
display: none;
}
}
}
main {
background-color: var(--color-grey-0);
flex-grow: 1;
header {
align-items: center;
background-color: var(--color-bg);
box-shadow: 0 1px 1px 0 var(--color-grey-2);
display: flex;
height: 3.5rem;
justify-content: space-between;
padding: 0 3rem 0 1rem;
.search-wrapper {
align-items: center;
display: flex;
flex-direction: row-reverse;
width: 100%;
input {
border: none;
font-size: 1rem;
padding: 1rem;
width: 100%;
&::placeholder {
color: var(--color-grey-5);
}
&:hover,
&:focus {
& + svg {
color: inherit;
}
&::placeholder {
color: var(--color-grey-7);
}
}
}
svg {
color: var(--color-grey-5);
}
}
}
.content {
// Default is for small phone sizes (like iPhone 5/SE)
padding: 0.5rem 0.35rem 0.5rem 0.35rem;
// Larger phones
@media screen and (min-width: 500px) {
padding: 1rem 0.5rem 1rem 0.5rem;
}
// Desktop
@media screen and (min-width: 1145px) {
padding: 1rem;
}
}
}
}
.disclosure {
margin-top: auto;
max-width: 250px;
color: var(--color-grey-3);
a {
text-decoration: var(--color-grey-2) underline;
}
}
// Hack for very small (iPhone 5/SE) sized phones
// an x overflow existed and I was unable to figure out why
@media screen and (max-width: 360px) {
body {
overflow-x: hidden !important;
}
}
</style>