From f0bd330d044be703d8f7f38d9673b2b628011c98 Mon Sep 17 00:00:00 2001 From: Pacharapol Withayasakpunt Date: Sun, 24 Oct 2021 18:16:46 +0700 Subject: [PATCH] add contextmenu --- .gitignore | 2 + .vscode/settings.json | 11 ++ Pipfile | 12 ++ Pipfile.lock | 84 +++++++++++ assets/api.ts | 114 +++++++++++++++ assets/buefy.png | Bin 10999 -> 0 bytes assets/speak.ts | 82 +++++++++++ assets/util.ts | 76 ++++++++++ components/Card.vue | 42 ------ components/ContextMenu.vue | 282 +++++++++++++++++++++++++++++++++++++ components/NuxtLogo.vue | 11 -- components/Tutorial.vue | 46 ------ package.json | 14 +- store/README.md | 10 -- store/index.ts | 129 +++++++++++++++++ tsconfig.json | 3 +- vue-inject.d.ts | 8 ++ yarn.lock | 117 ++++++++++++--- 18 files changed, 912 insertions(+), 131 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 assets/api.ts delete mode 100644 assets/buefy.png create mode 100644 assets/speak.ts create mode 100644 assets/util.ts delete mode 100644 components/Card.vue create mode 100644 components/ContextMenu.vue delete mode 100644 components/NuxtLogo.vue delete mode 100644 components/Tutorial.vue delete mode 100644 store/README.md create mode 100644 store/index.ts create mode 100644 vue-inject.d.ts diff --git a/.gitignore b/.gitignore index e8f682b..91199b9 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,5 @@ sw.* # Vim swap files *.swp + +/.venv/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3a77f1d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "editor.tabSize": 2, + "[vue]": { + "editor.defaultFormatter": "octref.vetur", + "editor.formatOnSave": true + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + } +} diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..d02f57c --- /dev/null +++ b/Pipfile @@ -0,0 +1,12 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +gtts = ">=2.2.3" + +[dev-packages] + +[requires] +python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..ff42739 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,84 @@ +{ + "_meta": { + "hash": { + "sha256": "ff889e495c55c0c92b329fe9caf8fad69d314e15db1c62b97c7abbc709a211ab" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "certifi": { + "hashes": [ + "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872", + "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569" + ], + "version": "==2021.10.8" + }, + "charset-normalizer": { + "hashes": [ + "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0", + "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b" + ], + "markers": "python_version >= '3'", + "version": "==2.0.7" + }, + "click": { + "hashes": [ + "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3", + "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b" + ], + "markers": "python_version >= '3.6'", + "version": "==8.0.3" + }, + "gtts": { + "hashes": [ + "sha256:8376d9bde85ac8cda95ea92dcd12bf31a3efe9e810edcb04437a4151cbec30d6", + "sha256:88cfaa112471867009a0450a696e5bc69cf1671fa0fa683fe17d0650023c863c" + ], + "index": "pypi", + "version": "==2.2.3" + }, + "idna": { + "hashes": [ + "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", + "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" + ], + "markers": "python_version >= '3'", + "version": "==3.3" + }, + "requests": { + "hashes": [ + "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", + "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==2.26.0" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "urllib3": { + "hashes": [ + "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece", + "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "version": "==1.26.7" + } + }, + "develop": {} +} diff --git a/assets/api.ts b/assets/api.ts new file mode 100644 index 0000000..d54cd1a --- /dev/null +++ b/assets/api.ts @@ -0,0 +1,114 @@ +import _axios, { AxiosInstance } from 'axios' + +class Api { + axios: AxiosInstance + _isAuthorized = true + _user = '' + + constructor() { + this.axios = _axios.create() + } + + async setWanikaniApiKey(apiKey?: string) { + const setApiKey = () => { + if (apiKey) { + this.axios.defaults.headers.common.Authorization = `Bearer ${apiKey}` + } else { + delete this.axios.defaults.headers.common.Authorization + } + } + + setApiKey() + const r = await this.user_getSettings() + + if (r) return r + apiKey = '' + setApiKey() + + return null + } + + async isAuthorized(): Promise { + if (this._isAuthorized && !this._user) { + const r = await this.user_getSettings() + if (r) { + this._user = r.identifier + this._isAuthorized = true + } + } + + return this._isAuthorized + } + + async user_getSettings() { + const r = await this.axios + .get('/api/user', { + params: { + select: 'level,levelMin,identifier', + }, + }) + .then( + (r) => + r.data as { + level: number + levelMin?: number + identifier: string + } + ) + .then((r) => ({ + ...r, + levelMin: r.levelMin || 1, + })) + .catch(() => null) + + if (!r) { + return + } + + return r + } + + async user_updateSettings(u: { levelMin: number }) { + return await this.axios.patch('/api/user', u) + } + + async quiz_getMany(params: { + entries: string[] + select: string[] + type?: string + direction?: string + }) { + return await this.axios.post('/api/quiz/getMany', params).then( + (r) => + r.data as { + result: { + id: string + entry: string + }[] + } + ) + } + + async quiz_create(params: { entries: string[]; type: string }) { + return await this.axios.put('/api/quiz', params).then( + (r) => + r.data as { + result: { + ids: string[] + }[] + } + ) + } + + async quiz_deleteMany(params: { entries: string[]; type: string }) { + return await this.axios.post('/api/quiz/deleteMany', params) + } + + async extra_delete(params: { id: string }) { + return await this.axios.delete('/extra/delete', { + params, + }) + } +} + +export const api = new Api() diff --git a/assets/buefy.png b/assets/buefy.png deleted file mode 100644 index 0526173988f3fae84153f1401a1e013dc56c4df3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10999 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 a7BDN|{r;)joCNwHKub+uwMNA@^8Ww`_6cJE diff --git a/assets/speak.ts b/assets/speak.ts new file mode 100644 index 0000000..df6b9ca --- /dev/null +++ b/assets/speak.ts @@ -0,0 +1,82 @@ +const allVoices: Record = {} + +// eslint-disable-next-line array-callback-return +speechSynthesis.getVoices().map((v) => { + allVoices[v.lang] = v.lang +}) + +speechSynthesis.onvoiceschanged = () => { + // eslint-disable-next-line array-callback-return + speechSynthesis.getVoices().map((v) => { + allVoices[v.lang] = v.lang + }) +} + +export async function speak( + s: string, + opts: { + lang?: string + forceOffline?: boolean + } = {} +) { + if (!opts.forceOffline) { + const audio = new Audio( + `/api/tts?q=${encodeURIComponent(s)}&lang=${encodeURIComponent( + opts.lang || 'ja' + )}` + ) + await audio.play().catch(() => + speak(s, { + ...opts, + forceOffline: true, + }) + ) + return + } + + let lang = 'zh' + + const voices = Object.keys(allVoices) + const stage1 = () => voices.filter((v) => v === lang)[0] + const stage2 = () => { + const m1 = lang.substr(0, 2) + const m2 = lang.substr(3, 2) + const r1 = new RegExp(`^${m1}[-_]${m2}`, 'i') + return voices.filter((v) => r1.test(v))[0] + } + const stage3 = () => { + const m1 = lang.substr(0, 2).toLocaleLowerCase() + return voices.filter((v) => v.toLocaleLowerCase().startsWith(m1))[0] + } + + lang = stage1() || stage2() || stage3() || '' + + if (lang) { + const utterance = new SpeechSynthesisUtterance(s) + utterance.lang = lang + speechSynthesis.speak(utterance) + + return new Promise((resolve) => { + utterance.onend = () => { + resolve() + } + }) + } +} + +window.addEventListener('keydown', (ev) => { + if ( + ev.target instanceof HTMLElement && + ['INPUT', 'TEXTAREA'].includes(ev.target.tagName.toLocaleUpperCase()) + ) { + return + } + + if (ev.key === 's') { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const s = window.getSelection()!.toString() + if (s) { + speak(s) + } + } +}) diff --git a/assets/util.ts b/assets/util.ts new file mode 100644 index 0000000..bc6797a --- /dev/null +++ b/assets/util.ts @@ -0,0 +1,76 @@ +export const doClick = async (el?: Element | null, timeout?: number) => { + if (!el) return + + if (el.classList?.add) { + el.classList.add('active') + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { click } = el as any + if (typeof click === 'function') { + const r = click() + if (r instanceof Promise) { + await r + } else if (timeout) { + await new Promise((resolve) => setTimeout(resolve, timeout)) + } else { + return + } + } + + if (el.classList?.remove) { + el.classList.remove('active') + } +} + +export const doMapKeypress = ( + evt: KeyboardEvent, + map: Record void) | string | HTMLElement> +) => { + let action = map[evt.key] + while (typeof action === 'string') { + action = map[action] + } + if (typeof action === 'function') { + action() + } else if (action instanceof HTMLElement) { + doClick(action) + } +} + +/** + * Shuffles array in place. ES6 version + * @param {Array} a items An array containing the items. + */ +export function shuffle(a: T[]) { + for (let i = a.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)) + ;[a[i], a[j]] = [a[j], a[i]] + } + return a +} + +export function sample(arr: T[], size: number): T[] { + const allN = Array(arr.length) + .fill(null) + .map((_, i) => i) + const outN: number[] = [] + + while (allN.length && outN.length < size) { + outN.push(...allN.splice(Math.floor(Math.random() * allN.length), 1)) + } + + return outN.map((n) => arr[n]) +} + +export function capitalize(s: string): string { + if (s.includes(' ')) { + return s + .split(' ') + .map((s0) => capitalize(s0)) + .join(' ') + } + + if (!s) return s + return s[0].toLocaleUpperCase() + s.substr(1) +} diff --git a/components/Card.vue b/components/Card.vue deleted file mode 100644 index 9c379b1..0000000 --- a/components/Card.vue +++ /dev/null @@ -1,42 +0,0 @@ - - - diff --git a/components/ContextMenu.vue b/components/ContextMenu.vue new file mode 100644 index 0000000..e4a08f6 --- /dev/null +++ b/components/ContextMenu.vue @@ -0,0 +1,282 @@ + + + + + diff --git a/components/NuxtLogo.vue b/components/NuxtLogo.vue deleted file mode 100644 index 970eba0..0000000 --- a/components/NuxtLogo.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/components/Tutorial.vue b/components/Tutorial.vue deleted file mode 100644 index 6440b8f..0000000 --- a/components/Tutorial.vue +++ /dev/null @@ -1,46 +0,0 @@ - - diff --git a/package.json b/package.json index f39bf82..52e0f50 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "jaquiz", + "name": "@jaquiz/www", "version": "1.0.0", "private": true, "scripts": { @@ -10,14 +10,20 @@ }, "dependencies": { "@nuxtjs/pwa": "^3.3.5", + "axios": "^0.23.0", "core-js": "^3.15.1", "nuxt": "^2.15.7", - "nuxt-buefy": "^0.4.8" + "nuxt-buefy": "^0.4.8", + "nuxt-property-decorator": "^2.9.1", + "nuxt-typed-vuex": "^0.2.0", + "vue-context": "^6.0.0" }, "devDependencies": { "@nuxt/types": "^2.15.7", "@nuxt/typescript-build": "^2.1.0", "eslint-config-prettier": "^8.3.0", - "prettier": "^2.3.2" + "prettier": "^2.3.2", + "sass": "^1.43.3", + "sass-loader": "~10.1.1" } -} \ No newline at end of file +} diff --git a/store/README.md b/store/README.md deleted file mode 100644 index 1972d27..0000000 --- a/store/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# STORE - -**This directory is not required, you can delete it if you don't want to use it.** - -This directory contains your Vuex Store files. -Vuex Store option is implemented in the Nuxt.js framework. - -Creating a file in this directory automatically activates the option in the framework. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). diff --git a/store/index.ts b/store/index.ts new file mode 100644 index 0000000..922499f --- /dev/null +++ b/store/index.ts @@ -0,0 +1,129 @@ +import { actionTree, getAccessorType, mutationTree } from 'typed-vuex' +import { api } from '~/assets/api' + +interface ISettings { + level: number | null + levelMin: number | null + sentenceMin: number | null + sentenceMax: number | null +} + +export const state = () => ({ + settings: { + level: null, + levelMin: null, + sentenceMin: null, + sentenceMax: null, + } as ISettings, + isApp: false, + tabs: [] as { + title: string + component: string + permanent?: boolean + query: Record + }[], + activeTab: 0, + identifier: '', +}) + +export const mutations = mutationTree(state, { + SET_SETTINGS(state, settings: ISettings) { + state.settings = settings + }, + SET_IS_APP(state, isApp: boolean) { + state.isApp = isApp + }, + SET_TAB_TITLE(state, { i, title }: { i: number; title: string }) { + state.tabs[i].title = title + state.tabs = JSON.parse(JSON.stringify(state.tabs)) + }, + ADD_TAB( + state, + { + component, + query = {}, + first, + }: { + component: string + query?: Record + first?: boolean + } + ) { + if (first) { + state.tabs = [ + { + title: component, + component, + permanent: true, + query, + }, + ] + } else { + state.tabs = [ + ...state.tabs, + { + title: component, + component, + query, + }, + ] + } + + state.activeTab = state.tabs.length - 1 + }, + DELETE_TAB(state, { i }: { i: number }) { + state.tabs.splice(i, 1) + + if (state.activeTab >= state.tabs.length) { + state.activeTab = state.tabs.length - 1 + } + + state.tabs = JSON.parse(JSON.stringify(state.tabs)) + }, + SET_TAB_COMPONENT(state, { i, component }: { i: number; component: string }) { + state.tabs[i].component = component + + state.tabs = JSON.parse(JSON.stringify(state.tabs)) + state.activeTab = i + }, + SET_TAB_CURRENT(state, { i }: { i: number }) { + state.activeTab = i + }, + SET_IDENTIFIER(state, name: string) { + state.identifier = name + }, +}) + +export const actions = actionTree( + { state, mutations }, + { + async updateSettings({ commit }) { + const r = await api.user_getSettings() + commit('SET_IS_APP', !!r) + commit('SET_IDENTIFIER', r?.identifier || '') + + if (r) { + if (!r.levelMin) { + r.levelMin = 1 + + await api.user_updateSettings({ + levelMin: r.levelMin, + }) + } + } + + commit('SET_SETTINGS', { + level: r?.level || null, + levelMin: r?.levelMin || null, + sentenceMin: null, + sentenceMax: null, + }) + }, + } +) + +export const accessorType = getAccessorType({ + state, + mutations, + actions, +}) diff --git a/tsconfig.json b/tsconfig.json index 4b17280..86bb182 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,7 +25,8 @@ }, "types": [ "@nuxt/types", - "@types/node" + "@types/node", + "buefy/types" ] }, "exclude": [ diff --git a/vue-inject.d.ts b/vue-inject.d.ts new file mode 100644 index 0000000..504f8e0 --- /dev/null +++ b/vue-inject.d.ts @@ -0,0 +1,8 @@ +import { accessorType } from '~/store' + +declare module 'vue/types/vue' { + interface Vue { + $accessor: typeof accessorType + $axios: never + } +} diff --git a/yarn.lock b/yarn.lock index ce489a1..afd0946 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2116,6 +2116,13 @@ autoprefixer@^9.6.1: postcss "^7.0.32" postcss-value-parser "^4.1.0" +axios@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.23.0.tgz#b0fa5d0948a8d1d75e3d5635238b6c4625b05149" + integrity sha512-NmvAE4i0YAv5cKq8zlDoPd1VLKAqX5oLuZKs8xkJa4qi6RGn0uhCYFjWtHHC9EM/MwOwYWOs53W+V0aqEXq1sg== + dependencies: + follow-redirects "^1.14.4" + babel-loader@^8.2.2: version "8.2.3" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.3.tgz#8986b40f1a64cacfcb4b8429320085ef68b1342d" @@ -2576,6 +2583,21 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.1, chokidar@^3.4.2, chokidar@^3.5.1, chokidar@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -2595,21 +2617,6 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.4.1, chokidar@^3.4.2, chokidar@^3.5.1, chokidar@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -3825,6 +3832,11 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +follow-redirects@^1.14.4: + version "1.14.4" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" + integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -4750,7 +4762,7 @@ jiti@^1.9.2: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.12.9.tgz#2ce45b265cfc8dc91ebd70a5204807cf915291bc" integrity sha512-TdcJywkQtcwLxogc4rSMAi479G2eDPzfW0fLySks7TPhgZZ4s/tM6stnzayIh3gS/db3zExWJyUx4cNWrwAmoQ== -js-tokens@^4.0.0: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -4963,6 +4975,13 @@ lodash@^4.15.0, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17. resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +loose-envify@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + lower-case@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" @@ -5490,6 +5509,24 @@ nuxt-buefy@^0.4.8: dependencies: buefy "^0.9.10" +nuxt-property-decorator@^2.9.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/nuxt-property-decorator/-/nuxt-property-decorator-2.9.1.tgz#60fd87b64d85519b091374c7ea4c0ce1979f6afa" + integrity sha512-dE2GrrGKZMhv0dHAr+Lmj+JOQfjIouINgF58QNRDFNOZXMJrXxKO5zGqvCRwmx3hxqqwht7TXHdz9w1AnvL2IA== + dependencies: + vue-class-component "^7.2.6" + vue-property-decorator "^9.0.0" + vuex-class "^0.3.2" + vuex-module-decorators "^1.0.1" + +nuxt-typed-vuex@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/nuxt-typed-vuex/-/nuxt-typed-vuex-0.2.0.tgz#989ff05bf30aa10bc82456f03cf5645bc9621e3a" + integrity sha512-c+6LRuwv4aox0aarAccd5zdd3tWOBDxbc32VRLeVpAt//ZG8Lcutt4Xc067iyRaquOashkJvQ8//Ko18EaySTw== + dependencies: + typed-vuex "0.2.0" + upath "^2.0.1" + nuxt@^2.15.7: version "2.15.8" resolved "https://registry.yarnpkg.com/nuxt/-/nuxt-2.15.8.tgz#946cba46bdaaf0e3918aa27fd9ea0fed8ed303b0" @@ -6999,7 +7036,7 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sass-loader@10.1.1: +sass-loader@10.1.1, sass-loader@~10.1.1: version "10.1.1" resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.1.tgz#4ddd5a3d7638e7949065dd6e9c7c04037f7e663d" integrity sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw== @@ -7010,6 +7047,13 @@ sass-loader@10.1.1: schema-utils "^3.0.0" semver "^7.3.2" +sass@^1.43.3: + version "1.43.3" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.43.3.tgz#aa16a69131b84f0cd23189a242571e8905f1ce43" + integrity sha512-BJnLngqWpMeS65UvlYYEuCb3/fLxDxhHtOB/gWPxs6NKrslTxGt3ZxwIvOe/0Jm4tWwM/+tIpE3wj4dLEhPDeQ== + dependencies: + chokidar ">=3.0.0 <4.0.0" + sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -7768,6 +7812,11 @@ type-fest@^0.8.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +typed-vuex@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/typed-vuex/-/typed-vuex-0.2.0.tgz#1c1b60150926a764c0a5a2d5eb6620ef8e9096aa" + integrity sha512-0JZJBcQiXJq4TnQh0W3sdZzTnwUiHO0AygttInsXO8BZ4eRTbNRmldKDIzsgA7WRlVi+pJ8oAKDlDDpkx0Ilpg== + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -8004,11 +8053,30 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +vue-class-component@^7.2.6: + version "7.2.6" + resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-7.2.6.tgz#8471e037b8e4762f5a464686e19e5afc708502e4" + integrity sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w== + +vue-clickaway@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/vue-clickaway/-/vue-clickaway-2.2.2.tgz#cecf6839575e8b2afc5d3edb3efb616d293dbb44" + integrity sha512-25SpjXKetL06GLYoLoC8pqAV6Cur9cQ//2g35GRFBV4FgoljbZZjTINR8g2NuVXXDMLSUXaKx5dutgO4PaDE7A== + dependencies: + loose-envify "^1.2.0" + vue-client-only@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/vue-client-only/-/vue-client-only-2.1.0.tgz#1a67a47b8ecacfa86d75830173fffee3bf8a4ee3" integrity sha512-vKl1skEKn8EK9f8P2ZzhRnuaRHLHrlt1sbRmazlvsx6EiC3A8oWF8YCBrMJzoN+W3OnElwIGbVjsx6/xelY1AA== +vue-context@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/vue-context/-/vue-context-6.0.0.tgz#97a1a423fc86fb7a54f46d1567670aba281a5970" + integrity sha512-x8gO6xgj0MtTCWcYbDjO/7VJ/gT+nV+unqICvsN0hwqqBzft31eYyExYqrSvfDCRI7ixxObu5tbl7BQAKge7eg== + dependencies: + vue-clickaway "^2.2.2" + vue-hot-reload-api@^2.3.0: version "2.3.4" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" @@ -8037,6 +8105,11 @@ vue-no-ssr@^1.1.1: resolved "https://registry.yarnpkg.com/vue-no-ssr/-/vue-no-ssr-1.1.1.tgz#875f3be6fb0ae41568a837f3ac1a80eaa137b998" integrity sha512-ZMjqRpWabMPqPc7gIrG0Nw6vRf1+itwf0Itft7LbMXs2g3Zs/NFmevjZGN1x7K3Q95GmIjWbQZTVerxiBxI+0g== +vue-property-decorator@^9.0.0: + version "9.1.2" + resolved "https://registry.yarnpkg.com/vue-property-decorator/-/vue-property-decorator-9.1.2.tgz#266a2eac61ba6527e2e68a6933cfb98fddab5457" + integrity sha512-xYA8MkZynPBGd/w5QFJ2d/NM0z/YeegMqYTphy7NJQXbZcuU6FC6AOdUAcy4SXP+YnkerC6AfH+ldg7PDk9ESQ== + vue-router@^3.5.1: version "3.5.2" resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.5.2.tgz#5f55e3f251970e36c3e8d88a7cd2d67a350ade5c" @@ -8082,6 +8155,16 @@ vue@^2.6.12: resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235" integrity sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ== +vuex-class@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/vuex-class/-/vuex-class-0.3.2.tgz#c7e96a076c1682137d4d23a8dcfdc63f220e17a8" + integrity sha512-m0w7/FMsNcwJgunJeM+wcNaHzK2KX1K1rw2WUQf7Q16ndXHo7pflRyOV/E8795JO/7fstyjH3EgqBI4h4n4qXQ== + +vuex-module-decorators@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vuex-module-decorators/-/vuex-module-decorators-1.0.1.tgz#d34dafb5428a3636f1c26d3d014c15fc9659ccd0" + integrity sha512-FLWZsXV5XAtl/bcKUyQFpnSBtpc3wK/7zSdy9oKbyp71mZd4ut5y2zSd219wWW9OG7WUOlVwac4rXFFDVnq7ug== + vuex@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71"