add contextmenu

This commit is contained in:
polv committed 2021-10-24 18:16:46 +07:00
1 parent 1c53acbe2c
commit f0bd330d04
18 files changed
+912 -131

No files matched your search

+282
View File
@@ -0,0 +1,282 @@
<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="entries.length === 1" class="separator">
<a></a>
</li>
<li v-if="entries.length === 1 && type === 'vocabulary'">
<a
role="button"
@click="openInNewTab('Vocabulary', { entry: entries[0] })"
>
Show details
</a>
</li>
<li v-if="entries.length === 1">
<a role="button" @click="openInNewTab('Character', { q: entries[0] })">
{{ type === 'character' ? 'Show details' : 'Search for Hanzi' }}
</a>
</li>
<li v-if="entries.length === 1 && type !== 'character'">
<a role="button" @click="openInNewTab('Vocabulary', { q: entries[0] })">
Search for vocabs
</a>
</li>
<li v-if="entries.length === 1">
<a
role="button"
:href="`https://jisho.org/word/${encodeURIComponent(entries[0])}`"
target="_blank"
rel="noopener noreferrer"
>
Open in Jisho.org
</a>
</li>
<li v-if="entries.length === 1 && type !== 'sentence'">
<a
role="button"
:href="`https://dictionary.goo.ne.jp/srch/all/${encodeURIComponent(
entries[0]
)}/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 } from '~/assets/api'
@Component
export default class ContextMenu extends Vue {
@Prop() id?: string
@Prop() entry?: string | string[]
@Prop() type?: string
@Prop() source?: string
@Prop() direction?: string
@Prop() description?: string
@Prop({ default: () => ({}) }) pinyin!: Record<string, string>
@Prop({ default: () => ({}) }) english!: Record<string, 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 Array.isArray(this.entry)
? this.entry
: this.entry
? [this.entry]
: []
}
async setQuiz() {
if (this.entry && this.type) {
const { result } = await api.quiz_getMany({
entries: this.entries,
select: ['id', 'entry'],
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(async () => {
if (this.entry && this.type) {
if (
!(
this.quiz.of &&
this.quiz.of.type !== this.type &&
this.quiz.of.entries.length === this.entries.length &&
this.quiz.of.entries.every((el, i) => this.entries[i] === el)
)
) {
await this.setQuiz()
}
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.entries.length === 1) {
await speak(this.entries[0])
}
}
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>