add app
This commit is contained in:
1 parent
cbc36d19f5
commit
2093433b96
8 files changed
+450
-211
No files matched your search
+342
@@ -0,0 +1,342 @@
|
||||
<template>
|
||||
<section class="AppPage">
|
||||
<b-loading v-if="!isReady" active is-full-page></b-loading>
|
||||
|
||||
<b-sidebar v-if="isReady" v-model="isDrawer" type="is-light" fullheight>
|
||||
<div class="p-2">
|
||||
<aside class="menu">
|
||||
<p class="menu-label">Menu</p>
|
||||
|
||||
<ul class="menu-list">
|
||||
<li v-for="(n, j) in navItems" :key="j">
|
||||
<a
|
||||
role="button"
|
||||
:class="{
|
||||
'is-active': tabs[activeTab].component === n.component,
|
||||
}"
|
||||
@click="setTab(activeTab, n.component)"
|
||||
>
|
||||
<b-icon v-if="n.icon" :icon="n.icon"></b-icon>
|
||||
<span v-if="n.text" :class="'icon ' + n.className">
|
||||
{{ n.text }}
|
||||
</span>
|
||||
{{ n.title || n.component }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/zhquiz/zhquiz/discussions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<b-icon pack="fab" icon="github"></b-icon>
|
||||
{{ 'Support & Contribute' }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div style="flex-grow: 1"></div>
|
||||
|
||||
<div class="p-4">
|
||||
<center>
|
||||
<a role="button" title="Click to logout" @click="doLogout">
|
||||
Logged in as {{ $store.state.identifier }}
|
||||
</a>
|
||||
</center>
|
||||
</div>
|
||||
</b-sidebar>
|
||||
|
||||
<nav v-if="isReady" class="tabs is-toggle">
|
||||
<ul>
|
||||
<li @click="isDrawer = true">
|
||||
<b-icon class="clickable" icon="bars" role="button"></b-icon>
|
||||
</li>
|
||||
<li
|
||||
v-for="(t, i) in tabs"
|
||||
:key="i"
|
||||
:class="{ 'is-active': activeTab === i }"
|
||||
>
|
||||
<a role="button">
|
||||
<b-dropdown aria-role="menu" append-to-body :value="t.component">
|
||||
<template #trigger>
|
||||
<a class="no-margin" role="button">
|
||||
<span class="text" @click.stop="setCurrentTab(i)">
|
||||
{{ t.title }}
|
||||
</span>
|
||||
<b-icon icon="caret-down"></b-icon>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<b-dropdown-item
|
||||
v-for="(n, j) in navItems"
|
||||
:key="j"
|
||||
:value="n.component"
|
||||
aria-role="menuitem"
|
||||
@click="setTab(i, n.component)"
|
||||
>
|
||||
<b-icon v-if="n.icon" :icon="n.icon"></b-icon>
|
||||
<span v-if="n.text" :class="'icon ' + n.className">
|
||||
{{ n.text }}
|
||||
</span>
|
||||
{{ n.title || n.component }}
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
|
||||
<button
|
||||
v-if="!t.permanent"
|
||||
class="delete is-small"
|
||||
@click="deleteTab(i)"
|
||||
></button>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a role="button" @click="addTab('Random')">+</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<main v-if="isReady">
|
||||
<component
|
||||
class="container"
|
||||
:is="t.component + 'Tab'"
|
||||
v-for="(t, i) in tabs"
|
||||
v-show="activeTab === i"
|
||||
:key="i"
|
||||
:query="t.query"
|
||||
@title="(t) => setTitle(i, t)"
|
||||
/>
|
||||
</main>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import BrowseTab from '@/components/tabs/BrowseTab.vue'
|
||||
import LookupTab from '@/components/tabs/LookupTab.vue'
|
||||
import LevelTab from '@/components/tabs/LevelTab.vue'
|
||||
import LibraryTab from '@/components/tabs/LibraryTab.vue'
|
||||
import { Component, Vue } from 'nuxt-property-decorator'
|
||||
import { api } from '~/assets/api'
|
||||
|
||||
@Component<AppPage>({
|
||||
components: {
|
||||
BrowseTab,
|
||||
LookupTab,
|
||||
LevelTab,
|
||||
LibraryTab,
|
||||
},
|
||||
async mounted() {
|
||||
await this.$accessor.updateSettings()
|
||||
|
||||
if (!this.$accessor.isApp) {
|
||||
this.$router.replace('/')
|
||||
} else {
|
||||
this.$accessor.ADD_TAB({
|
||||
component: 'Lookup',
|
||||
first: true,
|
||||
})
|
||||
this.isReady = true
|
||||
}
|
||||
},
|
||||
})
|
||||
export default class AppPage extends Vue {
|
||||
isReady = false
|
||||
isDrawer = false
|
||||
|
||||
get navItems(): {
|
||||
title?: string
|
||||
component: string
|
||||
icon?: string | string[]
|
||||
text?: string
|
||||
className?: string
|
||||
}[] {
|
||||
return [
|
||||
{
|
||||
title: 'Lookup',
|
||||
component: 'Character',
|
||||
text: '字',
|
||||
className: 'font-han',
|
||||
},
|
||||
{
|
||||
component: 'Level',
|
||||
text: this.level,
|
||||
},
|
||||
{
|
||||
title: 'User content',
|
||||
component: 'Browse',
|
||||
icon: 'list-ol',
|
||||
},
|
||||
{
|
||||
component: 'Library',
|
||||
icon: 'book-reader',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
get level() {
|
||||
const { level } = this.$store.state.settings
|
||||
return level ? level.toString() : ' '
|
||||
}
|
||||
|
||||
get tabs() {
|
||||
return this.$store.state.tabs
|
||||
}
|
||||
|
||||
get activeTab() {
|
||||
return this.$store.state.activeTab
|
||||
}
|
||||
|
||||
setTab(i: number, component: string) {
|
||||
this.$accessor.SET_TAB_COMPONENT({ i, component })
|
||||
}
|
||||
|
||||
setCurrentTab(i: number) {
|
||||
this.$accessor.SET_TAB_CURRENT({ i })
|
||||
}
|
||||
|
||||
addTab(component: string) {
|
||||
this.$accessor.ADD_TAB({ component })
|
||||
}
|
||||
|
||||
deleteTab(i: number) {
|
||||
this.$accessor.DELETE_TAB({ i })
|
||||
}
|
||||
|
||||
setTitle(i: number, title: string) {
|
||||
this.$accessor.SET_TAB_TITLE({ i, title })
|
||||
}
|
||||
|
||||
doLogout() {
|
||||
this.$buefy.dialog.confirm({
|
||||
message: 'Are you sure you want to logout?',
|
||||
onConfirm: async () => {
|
||||
await api.setWanikaniApiKey()
|
||||
this.$router.push('/')
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .sidebar-content {
|
||||
z-index: 60;
|
||||
}
|
||||
|
||||
.AppPage {
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr auto;
|
||||
}
|
||||
|
||||
$nav-height: 45px;
|
||||
|
||||
nav.tabs {
|
||||
z-index: 50;
|
||||
height: $nav-height;
|
||||
padding-top: 0.5em;
|
||||
padding-left: 0.5em;
|
||||
margin-bottom: 0 !important;
|
||||
background-color: rgba(230, 230, 230, 0.1);
|
||||
|
||||
li {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
li:not(.is-active) a {
|
||||
background-color: rgba(211, 211, 211, 0.5);
|
||||
border: none;
|
||||
}
|
||||
|
||||
li + li {
|
||||
margin-left: 0.5em !important;
|
||||
}
|
||||
|
||||
li a {
|
||||
border-bottom-left-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
|
||||
span.text {
|
||||
max-width: 10em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& :hover {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
overflow-y: scroll;
|
||||
position: absolute;
|
||||
margin-top: $nav-height;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 100px;
|
||||
height: 100%;
|
||||
background-color: rgba(211, 211, 211, 0.2);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.delete {
|
||||
border: none !important;
|
||||
transform: translateX(50%);
|
||||
background-color: rgba(143, 183, 221);
|
||||
|
||||
&:hover {
|
||||
background-color: lightgray;
|
||||
}
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: blue;
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
color: lightgray;
|
||||
|
||||
> span {
|
||||
$n: 3;
|
||||
$speed: 0.1s;
|
||||
|
||||
position: relative;
|
||||
animation: loading (($n + 1) * $speed) infinite;
|
||||
|
||||
@for $i from 1 through $n {
|
||||
&:nth-child(#{$i}) {
|
||||
animation-delay: $i * $speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
40% {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
100% {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+52
-43
@@ -1,53 +1,62 @@
|
||||
<template>
|
||||
<section class="section">
|
||||
<div class="columns is-mobile">
|
||||
<card
|
||||
title="Free"
|
||||
icon="github"
|
||||
>
|
||||
Open source on <a href="https://github.com/buefy/buefy">
|
||||
GitHub
|
||||
</a>
|
||||
</card>
|
||||
<main class="HomePage">
|
||||
<b-loading v-if="!isReady" active is-full-page></b-loading>
|
||||
<form v-else class="card login-card" @submit.prevent="login">
|
||||
<div class="card-content">
|
||||
<h2 class="title is-4">Login with WaniKani API key</h2>
|
||||
|
||||
<card
|
||||
title="Responsive"
|
||||
icon="cellphone-link"
|
||||
>
|
||||
<b class="has-text-grey">
|
||||
Every
|
||||
</b> component is responsive
|
||||
</card>
|
||||
<b-field>
|
||||
<b-input v-model="apiKey" placeholder="WaniKani API key"></b-input>
|
||||
</b-field>
|
||||
|
||||
<card
|
||||
title="Modern"
|
||||
icon="alert-decagram"
|
||||
>
|
||||
Built with <a href="https://vuejs.org/">
|
||||
Vue.js
|
||||
</a> and <a href="http://bulma.io/">
|
||||
Bulma
|
||||
</a>
|
||||
</card>
|
||||
|
||||
<card
|
||||
title="Lightweight"
|
||||
icon="arrange-bring-to-front"
|
||||
>
|
||||
No other internal dependency
|
||||
</card>
|
||||
</div>
|
||||
</section>
|
||||
<b-button class="is-primary is-fullwidth" @click="login">
|
||||
Login
|
||||
</b-button>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Card from '~/components/Card'
|
||||
<script lang="ts">
|
||||
import { Vue, Component } from 'nuxt-property-decorator'
|
||||
import { api } from '~/assets/api'
|
||||
|
||||
export default {
|
||||
name: 'HomePage',
|
||||
@Component<HomePage>({
|
||||
async mounted() {
|
||||
if (await api.isAuthorized()) {
|
||||
this.$router.replace('/app')
|
||||
} else {
|
||||
this.isReady = true
|
||||
}
|
||||
},
|
||||
})
|
||||
export default class HomePage extends Vue {
|
||||
apiKey = ''
|
||||
|
||||
components: {
|
||||
Card
|
||||
isReady = false
|
||||
|
||||
async login() {
|
||||
if (await api.setWanikaniApiKey(this.apiKey)) {
|
||||
this.$router.push('/app')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.HomePage {
|
||||
background-color: rgba(211, 211, 211, 0.3);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
position: absolute;
|
||||
max-width: 500px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
overflow: visible;
|
||||
}
|
||||
</style>
|
||||
@@ -1,15 +0,0 @@
|
||||
<template>
|
||||
<section class="section">
|
||||
<h2 class="title is-3 has-text-grey">
|
||||
"Just start <b-icon
|
||||
icon="rocket"
|
||||
size="is-large"
|
||||
/>"
|
||||
</h2>
|
||||
<h3 class="subtitle is-6 has-text-grey">
|
||||
Author: <a href="https://github.com/anteriovieira">
|
||||
Antério Vieira
|
||||
</a>
|
||||
</h3>
|
||||
</section>
|
||||
</template>
|
||||
Reference in new issue
Block a user