Files
www/components/ContextMenu.vue
T
2021-10-31 19:52:49 +07:00

271 lines
5.8 KiB
Vue

<template>
<client-only>
<vue-context ref="contextmenu" lazy>
<li v-for="(a, i) in additional" :key="i">
<a
role="button"
@click.prevent="a.handler"
@keypress.prevent="a.handler"
>
{{ a.name }}
</a>
</li>
<li v-if="additional.length" class="separator">
<a></a>
</li>
<li v-if="entries.length === 1">
<a
role="button"
@click.prevent="doSpeak()"
@keypress.prevent="doSpeak()"
>
Speak
</a>
</li>
<li v-if="type && quiz.db.length < entries.length">
<a
role="button"
@click.prevent="addToQuiz()"
@keypress.prevent="addToQuiz()"
>
Add to quiz
</a>
</li>
<li v-if="quiz.db.length">
<a
role="button"
@click.prevent="removeFromQuiz()"
@keypress.prevent="removeFromQuiz()"
>
Remove from quiz
</a>
</li>
<li v-if="entry" class="separator">
<a></a>
</li>
<li v-if="entry && type === 'vocabulary'">
<a
role="button"
@click="openInNewTab('Dictionary', { entry, type: 'vocabulary' })"
>
Show details
</a>
</li>
<li v-if="entry">
<a
role="button"
@click="openInNewTab('Dictionary', { entry, type: 'character' })"
>
{{ type === 'character' ? 'Show details' : 'Search for Kanji' }}
</a>
</li>
<li v-if="entry && type !== 'character'">
<a role="button" @click="openInNewTab('Dictionary', { entry, type })">
Search for vocabs
</a>
</li>
<li v-if="entry">
<a
role="button"
:href="`https://jisho.org/word/${encodeURIComponent(entry)}`"
target="_blank"
rel="noopener noreferrer"
>
Open in Jisho.org
</a>
</li>
<li v-if="entry && type !== 'sentence'">
<a
role="button"
:href="`https://dictionary.goo.ne.jp/srch/all/${encodeURIComponent(
entry
)}/m0u`"
target="_blank"
rel="noopener noreferrer"
>
Open in goo.ne.jp
</a>
</li>
<li v-if="id">
<a
role="button"
@click.prevent="doDelete()"
@keypress.prevent="doDelete()"
>
Delete
</a>
</li>
</vue-context>
</client-only>
</template>
<script lang="ts">
import { Vue, Component, Prop, Ref } from 'nuxt-property-decorator'
import { speak } from '~/assets/speak'
import { api, IType } from '~/assets/api'
@Component
export default class ContextMenu extends Vue {
@Prop() id?: string
@Prop() entry?: string
@Prop() audio?: {
mimetype?: string
url: string
}[]
@Prop() list?: string[]
@Prop() type?: IType
@Prop() source?: string
@Prop() direction?: string
@Prop() description?: string
@Prop({ default: () => [] }) additional!: {
name: string
handler: () => void
}[]
@Ref() contextmenu!: {
open: (evt: MouseEvent) => void
}
quiz: {
of?: {
entries: string[]
type: string
}
db: {
ids: string[]
}[]
} = {
db: [],
}
get entries() {
return this.list || (this.entry ? [this.entry] : [])
}
async setQuiz() {
if (this.entries.length && this.type) {
const { result } = await api.quiz_getManyByEntries({
entries: this.entries,
type: this.type,
direction: this.direction,
})
const entryMap = new Map<string, string[]>()
result.map(({ id, entry }) => {
const c = entryMap.get(entry!) || []
c.push(id!)
return entryMap.set(entry!, c)
})
this.quiz = {
of: {
entries: this.entries,
type: this.type,
},
db: Array.from(entryMap.values()).map((ids) => ({ ids })),
}
}
}
open(evt: MouseEvent) {
this.$nextTick(() => {
this.contextmenu.open(evt)
})
}
async doDelete() {
if (this.id) {
await api.extra_delete({
id: this.id,
})
}
this.$emit('deleted', this.id)
}
async doSpeak() {
if (this.entry) {
await speak(this.entry, { url: this.audio?.[0]?.url })
}
}
async addToQuiz() {
if (this.entries.length && this.type) {
const { result } = await api.quiz_create({
entries: this.entries,
type: this.type,
})
this.$buefy.snackbar.open(
`Added ${this.type}: ${this.entries.slice(0, 3).join(', ')}${
this.entries.length > 3 ? '...' : ''
} to quiz`
)
this.quiz = {
...this.quiz,
db: result,
}
this.$emit('quiz:added', {
entries: this.entries,
type: this.type,
db: result,
})
}
}
async removeFromQuiz() {
await api.quiz_deleteMany({
entries: this.entries,
type: this.type || 'vocabulary',
})
this.$buefy.snackbar.open(
`Removed ${this.type}: ${this.entries.slice(0, 3).join(', ')}${
this.entries.length > 3 ? '...' : ''
} from quiz`
)
const { db } = this.quiz
this.quiz = {
...this.quiz,
db: [],
}
this.$emit('quiz:removed', {
entries: this.entries,
type: this.type,
db,
})
}
openInNewTab(component: string, query: Record<string, string>) {
this.$accessor.ADD_TAB({
component,
query,
})
}
}
</script>
<style lang="scss" scoped>
.v-context {
li.separator {
display: flex;
flex-direction: row;
justify-content: center;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
a {
background-color: lightgray;
height: 1px;
width: calc(100% - 1rem);
padding: 0;
}
}
}
</style>