mirror of
https://github.com/tachiyomiorg/website.git
synced 2024-10-31 23:15:05 +01:00
Rewrite extensions components to setup syntax (#21)
* Rewrite extensions components to setup syntax. * Format md.use. * Import ElementPlus components directly for three-shaking. * Fix SSR setting. * Fix missing loading indicator in extensions page.
This commit is contained in:
parent
359833502a
commit
1d1bf5fba9
@ -57,6 +57,7 @@
|
||||
"stylelint": "^15.10.3",
|
||||
"stylelint-stylus": "^0.18.0",
|
||||
"stylus": "^0.60.0",
|
||||
"unplugin-element-plus": "^0.8.0",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vitepress": "1.0.0-rc.10",
|
||||
"vitepress-plugin-tabs": "^0.3.0",
|
||||
|
@ -1,9 +1,5 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@iconify-prerendered/vue-mdi':
|
||||
specifier: ^0.23.1689058119
|
||||
@ -109,6 +105,9 @@ devDependencies:
|
||||
stylus:
|
||||
specifier: ^0.60.0
|
||||
version: 0.60.0
|
||||
unplugin-element-plus:
|
||||
specifier: ^0.8.0
|
||||
version: 0.8.0
|
||||
vite-plugin-eslint:
|
||||
specifier: ^1.8.1
|
||||
version: 1.8.1(eslint@8.48.0)(vite@4.4.9)
|
||||
@ -1013,6 +1012,20 @@ packages:
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/@rollup/pluginutils@5.0.4:
|
||||
resolution: {integrity: sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
rollup: ^1.20.0||^2.0.0||^3.0.0
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/estree': 1.0.1
|
||||
estree-walker: 2.0.2
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/@shuding/opentype.js@1.4.0-beta.0:
|
||||
resolution: {integrity: sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==}
|
||||
engines: {node: '>= 8.0.0'}
|
||||
@ -1540,6 +1553,14 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/argparse@1.0.10:
|
||||
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
|
||||
dependencies:
|
||||
@ -1673,6 +1694,11 @@ packages:
|
||||
resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
|
||||
dev: false
|
||||
|
||||
/binary-extensions@2.2.0:
|
||||
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/body-parser@1.20.1:
|
||||
resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
@ -1781,6 +1807,21 @@ packages:
|
||||
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/chokidar@3.5.3:
|
||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
braces: 3.0.2
|
||||
glob-parent: 5.1.2
|
||||
is-binary-path: 2.1.0
|
||||
is-glob: 4.0.3
|
||||
normalize-path: 3.0.0
|
||||
readdirp: 3.6.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/cli-cursor@4.0.0:
|
||||
resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
@ -2189,6 +2230,10 @@ packages:
|
||||
which-typed-array: 1.1.11
|
||||
dev: true
|
||||
|
||||
/es-module-lexer@1.3.0:
|
||||
resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==}
|
||||
dev: true
|
||||
|
||||
/es-set-tostringtag@2.0.1:
|
||||
resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -3032,6 +3077,13 @@ packages:
|
||||
has-bigints: 1.0.2
|
||||
dev: true
|
||||
|
||||
/is-binary-path@2.1.0:
|
||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
binary-extensions: 2.2.0
|
||||
dev: true
|
||||
|
||||
/is-boolean-object@1.1.2:
|
||||
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -3986,6 +4038,13 @@ packages:
|
||||
type-fest: 1.4.0
|
||||
dev: true
|
||||
|
||||
/readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/redent@4.0.0:
|
||||
resolution: {integrity: sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==}
|
||||
engines: {node: '>=12'}
|
||||
@ -4711,6 +4770,27 @@ packages:
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: true
|
||||
|
||||
/unplugin-element-plus@0.8.0:
|
||||
resolution: {integrity: sha512-jByUGY3FG2B8RJKFryqxx4eNtSTj+Hjlo8edcOdJymewndDQjThZ1pRUQHRjQsbKhTV2jEctJV7t7RJ405UL4g==}
|
||||
engines: {node: '>=14.19.0'}
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.0.4
|
||||
es-module-lexer: 1.3.0
|
||||
magic-string: 0.30.3
|
||||
unplugin: 1.4.0
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
dev: true
|
||||
|
||||
/unplugin@1.4.0:
|
||||
resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==}
|
||||
dependencies:
|
||||
acorn: 8.10.0
|
||||
chokidar: 3.5.3
|
||||
webpack-sources: 3.2.3
|
||||
webpack-virtual-modules: 0.5.0
|
||||
dev: true
|
||||
|
||||
/upath@2.0.1:
|
||||
resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==}
|
||||
engines: {node: '>=4'}
|
||||
@ -4909,6 +4989,15 @@ packages:
|
||||
'@vue/server-renderer': 3.3.4(vue@3.3.4)
|
||||
'@vue/shared': 3.3.4
|
||||
|
||||
/webpack-sources@3.2.3:
|
||||
resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
dev: true
|
||||
|
||||
/webpack-virtual-modules@0.5.0:
|
||||
resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
|
||||
dev: true
|
||||
|
||||
/which-boxed-primitive@1.0.2:
|
||||
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
|
||||
dependencies:
|
||||
@ -5022,3 +5111,7 @@ packages:
|
||||
/yoga-wasm-web@0.3.3:
|
||||
resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==}
|
||||
dev: true
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { defineConfig, loadEnv } from "vitepress";
|
||||
import ElementPlus from "unplugin-element-plus/vite";
|
||||
|
||||
import markdownConfig from "./config/markdownConfig"; // For use with loading Markdown plugins
|
||||
import themeConfig from "./config/themeConfig"; // Theme related config
|
||||
@ -30,4 +31,10 @@ export default defineConfig({
|
||||
generateFeed(context, hostname);
|
||||
generateOgImages(context);
|
||||
},
|
||||
vite: {
|
||||
plugins: [ElementPlus({})],
|
||||
ssr: {
|
||||
noExternal: ["element-plus"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -12,17 +12,17 @@ import shortcodes from "./shortcodes";
|
||||
|
||||
const markdownConfig: MarkdownOptions = {
|
||||
config: (md) => {
|
||||
md.use(attrs),
|
||||
md.use(figure),
|
||||
md.use(imgLazyload),
|
||||
md.use(imgMark),
|
||||
md.use(imgSize),
|
||||
md.use(include, {
|
||||
md
|
||||
.use(attrs)
|
||||
.use(figure)
|
||||
.use(imgLazyload)
|
||||
.use(imgMark)
|
||||
.use(imgSize)
|
||||
.use(include, {
|
||||
currentPath: (env) => env.filePath,
|
||||
}),
|
||||
md.use(tabsMarkdownPlugin);
|
||||
|
||||
md.use(shortcode_plugin, shortcodes);
|
||||
})
|
||||
.use(tabsMarkdownPlugin)
|
||||
.use(shortcode_plugin, shortcodes);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1,41 +1,58 @@
|
||||
<script>
|
||||
<script setup lang="ts">
|
||||
import { toRefs } from "vue";
|
||||
import { langName, simpleLangName } from "../../../config/scripts/languages";
|
||||
import type { Extension } from "../../queries/useExtensionsRepositoryQuery";
|
||||
|
||||
export default {
|
||||
props: ["extensions"],
|
||||
emits: ["filters"],
|
||||
data() {
|
||||
return {
|
||||
filters: {
|
||||
search: "",
|
||||
lang: [],
|
||||
nsfw: "Show all",
|
||||
sort: "Ascending",
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
filters: {
|
||||
handler(value) {
|
||||
this.$emit("filters", this.filters);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
simpleLangName,
|
||||
langName,
|
||||
},
|
||||
};
|
||||
import {
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElRadioGroup,
|
||||
ElRadio,
|
||||
} from "element-plus";
|
||||
|
||||
export type Nsfw = "Show all" | "NSFW" | "SFW";
|
||||
export type Sort = "Ascending" | "Descending";
|
||||
|
||||
const props = defineProps<{
|
||||
extensions: Extension[][];
|
||||
search: string;
|
||||
lang: string[];
|
||||
nsfw: Nsfw;
|
||||
sort: Sort;
|
||||
}>();
|
||||
|
||||
const { extensions } = toRefs(props);
|
||||
|
||||
defineEmits<{
|
||||
(e: 'update:search', search: string): void;
|
||||
(e: 'update:lang', lang: string[]): void,
|
||||
(e: 'update:nsfw', nsfw: Nsfw): void,
|
||||
(e: 'update:sort', sort: Sort): void,
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="filters-list">
|
||||
<el-form :model="filters" label-width="120px">
|
||||
<el-form label-width="120px">
|
||||
<el-form-item label="Search:">
|
||||
<el-input v-model="filters.search" placeholder="Search extensions by name or ID..." clearable />
|
||||
<el-input
|
||||
:model-value="search"
|
||||
placeholder="Search extensions by name or ID..."
|
||||
clearable
|
||||
@update:model-value="$emit('update:search', $event)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="Languages:">
|
||||
<el-select v-model="filters.lang" placeholder="Show specific languages..." multiple clearable>
|
||||
<el-select
|
||||
:model-value="lang"
|
||||
placeholder="Show specific languages..."
|
||||
multiple
|
||||
clearable
|
||||
@update:model-value="$emit('update:lang', $event)"
|
||||
>
|
||||
<el-option
|
||||
v-for="[group] in extensions"
|
||||
:key="group.lang"
|
||||
@ -45,13 +62,19 @@ export default {
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="Sort by:">
|
||||
<el-radio-group v-model="filters.sort">
|
||||
<el-radio-group
|
||||
:model-value="sort"
|
||||
@update:model-value="$emit('update:sort', $event)"
|
||||
>
|
||||
<el-radio label="Ascending"></el-radio>
|
||||
<el-radio label="Descending"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="Display mode:">
|
||||
<el-radio-group v-model="filters.nsfw">
|
||||
<el-radio-group
|
||||
:model-value="nsfw"
|
||||
@update:model-value="$emit('update:nsfw', $event)"
|
||||
>
|
||||
<el-radio label="NSFW"></el-radio>
|
||||
<el-radio label="SFW"></el-radio>
|
||||
<el-radio label="Show all"></el-radio>
|
||||
@ -67,4 +90,8 @@ export default {
|
||||
flex-direction: column
|
||||
row-gap: 1rem
|
||||
}
|
||||
|
||||
.el-select {
|
||||
width: 100%
|
||||
}
|
||||
</style>
|
||||
|
@ -1,27 +1,25 @@
|
||||
<script>
|
||||
<script setup lang="ts">
|
||||
import { computed, toRefs } from "vue";
|
||||
import { langName, simpleLangName } from "../../../config/scripts/languages";
|
||||
import ExtensionItem from "./ExtensionItem.vue";
|
||||
import type { Extension } from "../../queries/useExtensionsRepositoryQuery";
|
||||
|
||||
export default {
|
||||
components: { ExtensionItem },
|
||||
props: ["list", "totalCount"],
|
||||
computed: {
|
||||
groupName: function () {
|
||||
const firstItem = this.list[0];
|
||||
return firstItem.lang === "en" ? simpleLangName(firstItem.lang) : langName(firstItem.lang);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
simpleLangName,
|
||||
langName,
|
||||
},
|
||||
};
|
||||
const props = defineProps<{ list: Extension[]; totalCount: number }>();
|
||||
const { list } = toRefs(props);
|
||||
|
||||
const groupName = computed(() => {
|
||||
const firstItem = list.value[0];
|
||||
|
||||
return firstItem.lang === "en"
|
||||
? simpleLangName(firstItem.lang)
|
||||
: langName(firstItem.lang);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="extension-group">
|
||||
<h2>
|
||||
{{ groupName }}
|
||||
<span>{{ groupName }}</span>
|
||||
|
||||
<span class="extensions-total">
|
||||
Total:
|
||||
@ -30,6 +28,7 @@ export default {
|
||||
</span>
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
<ExtensionItem
|
||||
v-for="extension in list"
|
||||
:id="extension.pkg.replace('eu.kanade.tachiyomi.extension.', '')"
|
||||
@ -40,10 +39,12 @@ export default {
|
||||
</template>
|
||||
|
||||
<style lang="stylus">
|
||||
.extensions-total {
|
||||
float: right
|
||||
.extension-group h2 {
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: space-between
|
||||
|
||||
&-sum {
|
||||
.extensions-total-sum {
|
||||
color: var(--vp-c-brand)
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,24 @@
|
||||
<script>
|
||||
export default {
|
||||
props: ["item"],
|
||||
computed: {
|
||||
pkgId: function () {
|
||||
return this.item.pkg.replace("eu.kanade.tachiyomi.extension.", "");
|
||||
},
|
||||
pkgName: function () {
|
||||
return this.item.name.split(": ")[1];
|
||||
},
|
||||
pkgVersion: function () {
|
||||
return this.item.version;
|
||||
},
|
||||
pkgIsNsfw: function () {
|
||||
return !!parseInt(this.item.nsfw);
|
||||
},
|
||||
pkgHasReadme: function () {
|
||||
return !!parseInt(this.item.hasReadme);
|
||||
},
|
||||
pkgHasChangelog: function () {
|
||||
return !!parseInt(this.item.hasChangelog);
|
||||
},
|
||||
iconUrl: function () {
|
||||
return `https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo/icon/${this.item.pkg}.png`;
|
||||
},
|
||||
apkUrl: function () {
|
||||
return `https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo/apk/${this.item.apk}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
<script setup lang="ts">
|
||||
import { computed, toRefs } from "vue";
|
||||
import type { Extension } from "../../queries/useExtensionsRepositoryQuery";
|
||||
|
||||
const props = defineProps<{ item: Extension }>();
|
||||
const { item } = toRefs(props);
|
||||
|
||||
const pkgId = computed(() => {
|
||||
return item.value.pkg.replace("eu.kanade.tachiyomi.extension.", "");
|
||||
});
|
||||
|
||||
const pkgName = computed(() => item.value.name.split(": ")[1]);
|
||||
const pkgIsNsfw = computed(() => item.value.nsfw === 1);
|
||||
|
||||
const iconUrl = computed(() => {
|
||||
return `https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo/icon/${item.value.pkg}.png`;
|
||||
});
|
||||
|
||||
const apkUrl = computed(() => {
|
||||
return `https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo/apk/${item.value.apk}`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -42,8 +33,8 @@ export default {
|
||||
{{ pkgId }}
|
||||
</div>
|
||||
</div>
|
||||
<Badge v-if="pkgIsNsfw" type="danger" :text="pkgVersion" title="This extension contains NSFW entries." />
|
||||
<Badge v-else type="info" :text="pkgVersion" title="This extension is free from NSFW entries." />
|
||||
<Badge v-if="pkgIsNsfw" type="danger" :text="item.version" title="This extension contains NSFW entries." />
|
||||
<Badge v-else type="info" :text="item.version" title="This extension is free from NSFW entries." />
|
||||
<a :href="apkUrl" class="extension-download" title="Download APK" download>↓</a>
|
||||
</div>
|
||||
</template>
|
||||
@ -53,14 +44,20 @@ export default {
|
||||
position: relative
|
||||
align-items: center
|
||||
display: flex
|
||||
width: 100%
|
||||
padding: 0.5em 0
|
||||
margin: 0.8em 0
|
||||
width: calc(100% + 1em)
|
||||
padding: 0.5em
|
||||
margin: 0.8em -0.5em
|
||||
border-radius: 8px
|
||||
gap: 0.675rem
|
||||
|
||||
&:hover {
|
||||
background-color: var(--vp-c-bg-soft-mute)
|
||||
background-color: var(--vp-c-bg-soft)
|
||||
}
|
||||
|
||||
&:target {
|
||||
background-color: var(--vp-c-brand-soft)
|
||||
border-radius: 8px
|
||||
transition: 500ms background-color
|
||||
}
|
||||
|
||||
.anchor {
|
||||
@ -142,6 +139,8 @@ export default {
|
||||
border: 1px solid var(--vp-c-divider)
|
||||
border-radius: 8px
|
||||
padding: 0.5em
|
||||
margin: 0.8em 0
|
||||
width: 100%
|
||||
|
||||
.extension-icon {
|
||||
margin-left: 0
|
||||
@ -152,10 +151,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:target {
|
||||
background-color: var(--vp-c-bg-soft-mute)
|
||||
border-radius: 8px
|
||||
transition: 500ms background-color
|
||||
}
|
||||
</style>
|
||||
|
@ -1,21 +1,28 @@
|
||||
<script>
|
||||
<script setup lang="ts">
|
||||
import { computed, toRefs } from "vue";
|
||||
import ExtensionGroup from "./ExtensionGroup.vue";
|
||||
import type { Extension } from "../../queries/useExtensionsRepositoryQuery";
|
||||
|
||||
export default {
|
||||
components: { ExtensionGroup },
|
||||
props: ["extensions"],
|
||||
computed: {
|
||||
totalCount() {
|
||||
return this.extensions.reduce((sum, item) => sum + item.length, 0);
|
||||
},
|
||||
},
|
||||
};
|
||||
const props = defineProps<{ extensions: Extension[][] }>();
|
||||
const { extensions } = toRefs(props);
|
||||
|
||||
const totalCount = computed(() => {
|
||||
return extensions.value.reduce((sum, item) => sum + item.length, 0);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="extension-list">
|
||||
<ExtensionGroup v-for="group in extensions" :key="group[0].lang" :list="group" :class="group[0].lang" :total-count="totalCount" />
|
||||
<ExtensionGroup
|
||||
v-for="group in extensions"
|
||||
:key="group[0].lang"
|
||||
:list="group"
|
||||
:class="group[0].lang"
|
||||
:total-count="totalCount"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="stylus">
|
||||
.extension-list {
|
||||
> div {
|
||||
|
@ -1,19 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import groupBy from "lodash.groupby";
|
||||
import { ElLoading } from "element-plus";
|
||||
|
||||
import { simpleLangName } from "../../../config/scripts/languages";
|
||||
import ExtensionFilters from "./ExtensionFilters.vue";
|
||||
import ExtensionList from "./ExtensionList.vue";
|
||||
import useExtensionsRepositoryQuery from "../../queries/useExtensionsRepositoryQuery";
|
||||
import { computed, onUpdated, ref } from 'vue';
|
||||
import { computed, nextTick, onMounted, onUpdated, reactive, ref, watch } from 'vue';
|
||||
import type { Extension } from "../../queries/useExtensionsRepositoryQuery";
|
||||
import type { Nsfw, Sort } from "./ExtensionFilters.vue";
|
||||
|
||||
const { data: extensions, isLoading } = useExtensionsRepositoryQuery();
|
||||
const { data: extensions, isLoading } = useExtensionsRepositoryQuery({
|
||||
select: (response) => {
|
||||
const values: Extension[][] = Object.values(groupBy(response, "lang"));
|
||||
values.sort(languageComparator)
|
||||
|
||||
const filters = ref({
|
||||
return values
|
||||
},
|
||||
});
|
||||
|
||||
const filters = reactive({
|
||||
search: "",
|
||||
lang: [] as string[],
|
||||
nsfw: "Show all",
|
||||
sort: "Ascending",
|
||||
nsfw: "Show all" as Nsfw,
|
||||
sort: "Ascending" as Sort,
|
||||
});
|
||||
|
||||
function languageComparator(a: Extension[], b: Extension[]) {
|
||||
@ -40,33 +50,26 @@ function languageComparator(a: Extension[], b: Extension[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const groupedExtensions = computed(() => {
|
||||
const values: Extension[][] = Object.values(groupBy(extensions.value, "lang"));
|
||||
values.sort(languageComparator)
|
||||
|
||||
return values
|
||||
})
|
||||
|
||||
const filteredExtensions = computed(() => {
|
||||
const filtered: Extension[][] = [];
|
||||
|
||||
for (const group of groupedExtensions.value) {
|
||||
let filteredGroup = filters.value.lang.length
|
||||
? (filters.value.lang.includes(group[0].lang) ? group : [])
|
||||
for (const group of (extensions.value ?? [])) {
|
||||
let filteredGroup = filters.lang.length
|
||||
? (filters.lang.includes(group[0].lang) ? group : [])
|
||||
: group;
|
||||
|
||||
if (filters.value.search) {
|
||||
if (filters.search) {
|
||||
filteredGroup = filteredGroup.filter(
|
||||
(ext) =>
|
||||
ext.name.toLowerCase().includes(filters.value.search.toLowerCase()) ||
|
||||
ext.sources.some((source) => source.id.includes(filters.value.search))
|
||||
ext.name.toLowerCase().includes(filters.search.toLowerCase()) ||
|
||||
ext.sources.some((source) => source.id.includes(filters.search))
|
||||
);
|
||||
}
|
||||
filteredGroup = filteredGroup.filter((ext) =>
|
||||
filters.value.nsfw === "Show all" ? true : ext.nsfw === (filters.value.nsfw === "NSFW" ? 1 : 0)
|
||||
filters.nsfw === "Show all" ? true : ext.nsfw === (filters.nsfw === "NSFW" ? 1 : 0)
|
||||
);
|
||||
|
||||
if (filters.value.sort && filters.value.sort === "Descending") {
|
||||
if (filters.sort && filters.sort === "Descending") {
|
||||
filteredGroup = filteredGroup.reverse();
|
||||
}
|
||||
if (filteredGroup.length) {
|
||||
@ -77,15 +80,46 @@ const filteredExtensions = computed(() => {
|
||||
return filtered;
|
||||
});
|
||||
|
||||
onUpdated(() => {
|
||||
const loadingInstance = ref<ReturnType<typeof ElLoading['service']>>();
|
||||
|
||||
onMounted(() => {
|
||||
loadingInstance.value = ElLoading.service({
|
||||
target: ".extensions",
|
||||
fullscreen: false,
|
||||
background: "transparent",
|
||||
});
|
||||
})
|
||||
|
||||
watch(extensions, async () => {
|
||||
if (window.location.hash) {
|
||||
window.location.replace(window.location.hash);
|
||||
await nextTick()
|
||||
document.getElementById(window.location.hash.substring(1))
|
||||
?.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
});
|
||||
|
||||
watch([isLoading, loadingInstance], async ([newIsLoading]) => {
|
||||
if (!newIsLoading) {
|
||||
loadingInstance.value?.close();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ExtensionFilters :extensions="groupedExtensions" @filters="filters = $event" />
|
||||
<div class="loading" v-if="isLoading" v-loading.lock="isLoading" style="min-height: 200px"></div>
|
||||
<ExtensionList v-else :extensions="filteredExtensions" />
|
||||
<ExtensionFilters
|
||||
:extensions="extensions ?? []"
|
||||
v-model:search="filters.search"
|
||||
v-model:lang="filters.lang"
|
||||
v-model:nsfw="filters.nsfw"
|
||||
v-model:sort="filters.sort"
|
||||
/>
|
||||
<div class="extensions">
|
||||
<ExtensionList v-if="!isLoading" :extensions="filteredExtensions" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.extensions {
|
||||
min-height: 200px
|
||||
}
|
||||
</style>
|
||||
|
@ -5,8 +5,6 @@ import DefaultTheme from "vitepress/theme";
|
||||
import "./styles/base.styl";
|
||||
|
||||
// Import Global plugins
|
||||
import ElementPlus from "element-plus";
|
||||
import "element-plus/dist/index.css";
|
||||
import "element-plus/theme-chalk/dark/css-vars.css";
|
||||
|
||||
import { VueQueryPlugin } from "@tanstack/vue-query";
|
||||
@ -19,7 +17,6 @@ import { IconDownload, IconNewspaperVariant, IconBugReport } from "@iconify-prer
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
enhanceApp({ app }) {
|
||||
app.use(ElementPlus);
|
||||
app.use(VueQueryPlugin);
|
||||
enhanceAppWithTabs(app);
|
||||
app.component("IconDownload", IconDownload);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import { UseQueryOptions, useQuery } from "@tanstack/vue-query";
|
||||
import axios from "axios";
|
||||
import { GITHUB_EXTENSION_JSON } from "../../config/constants";
|
||||
|
||||
@ -26,14 +26,11 @@ export interface Source {
|
||||
hasCloudflare: string;
|
||||
}
|
||||
|
||||
export interface GitHubAsset {
|
||||
name: string;
|
||||
content_type: string;
|
||||
browser_download_url: string;
|
||||
}
|
||||
type UseExtensionsRepositoryQueryOptions<S = Extension[]> =
|
||||
UseQueryOptions<Extension[], Error, S>
|
||||
|
||||
export default function useExtensionsRepositoryQuery() {
|
||||
return useQuery({
|
||||
export default function useExtensionsRepositoryQuery<S = Extension[]>(options: UseExtensionsRepositoryQueryOptions<S> = {}) {
|
||||
return useQuery<Extension[], Error, S>({
|
||||
queryKey: ["extensions"],
|
||||
queryFn: async () => {
|
||||
const { data } = await axios.get<Extension[]>(GITHUB_EXTENSION_JSON);
|
||||
@ -42,5 +39,6 @@ export default function useExtensionsRepositoryQuery() {
|
||||
},
|
||||
initialData: () => [],
|
||||
refetchOnWindowFocus: false,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
@ -217,3 +217,10 @@ main :where(h1, h2, h3, h4, h5, h6) + figure {
|
||||
margin-right: 4px
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Element Plus
|
||||
*/
|
||||
body {
|
||||
--el-color-primary: var(--vp-c-brand-1)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user