@ansidev-oss/vitepress-theme-ansidev 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/dist/client/Donation-D9Xn0MPt.js +76 -0
  3. package/dist/client/GoogleAnalytics-Dp3EGn1x.js +29 -0
  4. package/dist/client/VPAlgoliaSearchBox-DXE-LCVf.js +126 -0
  5. package/dist/client/VPCarbonAds-Czmm53YE.js +31 -0
  6. package/dist/client/VPLocalSearchBox-ihwA4uH-.js +4086 -0
  7. package/dist/client/composables/date.d.ts +2 -0
  8. package/dist/client/composables/index.d.ts +4 -0
  9. package/dist/client/composables/locale.d.ts +3 -0
  10. package/dist/client/composables/project.d.ts +13 -0
  11. package/dist/client/composables/rank.d.ts +1 -0
  12. package/dist/client/composables/slug.d.ts +2 -0
  13. package/dist/client/index-BtT3qA6T.js +12663 -0
  14. package/dist/client/index-CCR5sUM8.js +10688 -0
  15. package/dist/client/index-CZCdwVUW.js +7566 -0
  16. package/dist/client/index.d.ts +6 -0
  17. package/dist/client/index.js +5 -0
  18. package/dist/client/plugins/donation/index.d.ts +19 -0
  19. package/dist/client/plugins/google-analytics/index.d.ts +8 -0
  20. package/dist/client/plugins/i18n/index.d.ts +53 -0
  21. package/dist/client/styles.css +1 -0
  22. package/dist/client/types/index.d.ts +45 -0
  23. package/dist/node/composables.d.mts +30 -0
  24. package/dist/node/composables.mjs +51 -0
  25. package/dist/node/config.d.mts +46 -0
  26. package/dist/node/config.mjs +47 -0
  27. package/package.json +87 -0
  28. package/src/client/components/EmptyFooter.vue +3 -0
  29. package/src/client/components/Footer.vue +87 -0
  30. package/src/client/components/Link.vue +31 -0
  31. package/src/client/components/LocaleSwitcher.vue +59 -0
  32. package/src/client/components/PostDate.vue +42 -0
  33. package/src/client/components/PostItem.vue +33 -0
  34. package/src/client/components/PostList.vue +23 -0
  35. package/src/client/components/ProjectCard.vue +68 -0
  36. package/src/client/components/ProjectList.vue +23 -0
  37. package/src/client/components/RouterLink.vue +19 -0
  38. package/src/client/components/StatusBadge.vue +56 -0
  39. package/src/client/components/TermBadge.vue +29 -0
  40. package/src/client/components/TermLink.vue +25 -0
  41. package/src/client/composables/date.ts +12 -0
  42. package/src/client/composables/index.ts +4 -0
  43. package/src/client/composables/locale.ts +7 -0
  44. package/src/client/composables/project.ts +30 -0
  45. package/src/client/composables/rank.ts +13 -0
  46. package/src/client/composables/slug.ts +12 -0
  47. package/src/client/index.ts +43 -0
  48. package/src/client/layouts/Layout.vue +25 -0
  49. package/src/client/pages/ArchivesPage.vue +43 -0
  50. package/src/client/pages/CategoriesPage.vue +30 -0
  51. package/src/client/pages/HomePage.vue +18 -0
  52. package/src/client/pages/Page.vue +20 -0
  53. package/src/client/pages/PostsPage.vue +23 -0
  54. package/src/client/pages/ProjectsPage.vue +65 -0
  55. package/src/client/pages/TagsPage.vue +30 -0
  56. package/src/client/plugins/donation/components/Donation.vue +43 -0
  57. package/src/client/plugins/donation/components/DonationButton.vue +22 -0
  58. package/src/client/plugins/donation/index.ts +42 -0
  59. package/src/client/plugins/google-analytics/components/GoogleAnalytics.vue +52 -0
  60. package/src/client/plugins/google-analytics/index.ts +8 -0
  61. package/src/client/plugins/i18n/index.ts +13 -0
  62. package/src/client/plugins/i18n/locales/en.json +25 -0
  63. package/src/client/plugins/i18n/locales/vi.json +25 -0
  64. package/src/client/styles/index.css +30 -0
  65. package/src/client/types/index.ts +50 -0
  66. package/src/node/composables/index.ts +3 -0
  67. package/src/node/composables/markdown.ts +14 -0
  68. package/src/node/composables/route.ts +26 -0
  69. package/src/node/composables/slug.ts +40 -0
  70. package/src/node/composables/types.ts +7 -0
  71. package/src/node/config.ts +79 -0
  72. package/src/vite-env.d.ts +7 -0
@@ -0,0 +1,19 @@
1
+ <script setup lang='ts'>
2
+ import { useRouter } from 'vitepress'
3
+
4
+ defineProps({
5
+ to: {
6
+ type: String,
7
+ required: true,
8
+ default: '#',
9
+ },
10
+ })
11
+
12
+ const router = useRouter()
13
+ </script>
14
+
15
+ <template>
16
+ <a :href="to" @click="router.go(to)" v-bind="$attrs">
17
+ <slot />
18
+ </a>
19
+ </template>
@@ -0,0 +1,56 @@
1
+ <script setup lang='ts'>
2
+ defineProps({
3
+ backgroundColor: {
4
+ type: String,
5
+ required: false,
6
+ default: 'bg-blue-100',
7
+ },
8
+ darkBackgroundColor: {
9
+ type: String,
10
+ required: false,
11
+ default: 'dark:bg-blue-800',
12
+ },
13
+ textColor: {
14
+ type: String,
15
+ required: false,
16
+ default: 'text-blue-800',
17
+ },
18
+ darkTextColor: {
19
+ type: String,
20
+ required: false,
21
+ default: 'dark:text-blue-100',
22
+ },
23
+ fontSize: {
24
+ type: String,
25
+ required: false,
26
+ default: 'text-base',
27
+ },
28
+ fontWeight: {
29
+ type: String,
30
+ required: false,
31
+ default: 'font-normal',
32
+ },
33
+ rounded: {
34
+ type: Boolean,
35
+ required: false,
36
+ default: true,
37
+ },
38
+ })
39
+ </script>
40
+
41
+ <template>
42
+ <span
43
+ class="px-2.5 py-0.5"
44
+ :class="[
45
+ backgroundColor,
46
+ darkBackgroundColor,
47
+ textColor,
48
+ darkTextColor,
49
+ fontSize,
50
+ fontWeight,
51
+ rounded ? 'rounded' : '',
52
+ ]"
53
+ >
54
+ <slot />
55
+ </span>
56
+ </template>
@@ -0,0 +1,29 @@
1
+ <script setup lang="ts">
2
+ import Link from './Link.vue'
3
+
4
+ defineProps({
5
+ title: {
6
+ type: String,
7
+ required: true,
8
+ },
9
+ count: {
10
+ type: Number,
11
+ required: true,
12
+ },
13
+ href: {
14
+ type: String,
15
+ required: true,
16
+ },
17
+ })
18
+ </script>
19
+
20
+ <template>
21
+ <Link :key="title" :href="href"
22
+ class="inline-flex items-center mt-2 mb-2 mr-2 px-5 py-2.5 text-sm font-medium text-center text-white component-border">
23
+ {{ title }}
24
+ <span
25
+ class="inline-flex items-center justify-center w-4 h-4 ms-2 text-xs font-semibold text-gray-900 dark:text-white bg-gray-300 dark:bg-gray-700 p-2.5 rounded">
26
+ {{ count }}
27
+ </span>
28
+ </Link>
29
+ </template>
@@ -0,0 +1,25 @@
1
+ <script setup lang='ts'>
2
+ import { computed } from 'vue'
3
+ import { useSlug } from '../composables'
4
+ import RouterLink from './RouterLink.vue'
5
+
6
+ const props = defineProps({
7
+ text: {
8
+ type: String,
9
+ required: true,
10
+ },
11
+ basePath: {
12
+ type: String,
13
+ required: true,
14
+ },
15
+ })
16
+
17
+ const slug = computed(() => useSlug(props.text))
18
+ const link = computed(() => `${props.basePath}${slug.value}`)
19
+ </script>
20
+
21
+ <template>
22
+ <RouterLink :to="link" class="mr-2 text-sm font-medium uppercase link-alt">
23
+ {{ text }}
24
+ </RouterLink>
25
+ </template>
@@ -0,0 +1,12 @@
1
+ import type { PostDate } from '../types'
2
+
3
+ export const usePostDate: (raw: Date) => PostDate = (raw: Date) => {
4
+ const date = raw instanceof Date ? raw : new Date(raw as string)
5
+ date.setUTCHours(12)
6
+ return {
7
+ time: +date,
8
+ day: date.toLocaleDateString('en-US', { day: 'numeric' }),
9
+ month: date.toLocaleDateString('en-US', { month: 'short' }),
10
+ year: date.toLocaleDateString('en-US', { year: 'numeric' }),
11
+ }
12
+ }
@@ -0,0 +1,4 @@
1
+ export * from './date'
2
+ export * from './project'
3
+ export * from './rank'
4
+ export * from './slug'
@@ -0,0 +1,7 @@
1
+ import type { WritableComputedRef } from 'vue'
2
+ import type { Locale } from 'vue-i18n'
3
+
4
+ export const switchLocale = (availableLocales: string[], locale: WritableComputedRef<Locale>) => {
5
+ const locales = availableLocales
6
+ locale.value = locales[(locales.indexOf(locale.value) + 1) % locales.length]
7
+ }
@@ -0,0 +1,30 @@
1
+ export const useProjectStatusStyle = (developmentStatus: string) => {
2
+ switch (developmentStatus) {
3
+ case 'active':
4
+ return {
5
+ fontSize: 'text-sm',
6
+ backgroundColor: 'bg-green-100',
7
+ darkBackgroundColor: 'dark:bg-green-800',
8
+ textColor: 'text-green-800',
9
+ darkTextColor: 'dark:text-green-100',
10
+ }
11
+ case 'inactive':
12
+ return {
13
+ fontSize: 'text-sm',
14
+ backgroundColor: 'bg-yellow-100',
15
+ darkBackgroundColor: 'dark:bg-yellow-800',
16
+ textColor: 'text-yellow-800',
17
+ darkTextColor: 'dark:text-yellow-100',
18
+ }
19
+ case 'unmaintained':
20
+ return {
21
+ fontSize: 'text-sm',
22
+ backgroundColor: 'bg-red-100',
23
+ darkBackgroundColor: 'dark:bg-red-800',
24
+ textColor: 'text-red-800',
25
+ darkTextColor: 'dark:text-red-100',
26
+ }
27
+ default:
28
+ return {}
29
+ }
30
+ }
@@ -0,0 +1,13 @@
1
+ export const useRank = <T extends string>(arr: T[]): Record<T, number> => {
2
+ const rank: Record<T, number> = arr.reduce(
3
+ (acc: Record<T, number>, item: T) => {
4
+ acc[item] = (acc[item] || 0) + 1 // Increment the count if the key exists, otherwise initialize to 1
5
+ return acc
6
+ },
7
+ {} as Record<T, number>,
8
+ )
9
+
10
+ const entries = Object.entries(rank) as [T, number][]
11
+
12
+ return Object.fromEntries(entries.sort(([, a], [, b]) => b - a)) as Record<T, number>
13
+ }
@@ -0,0 +1,12 @@
1
+ export const useSlug = (str: string) => {
2
+ const s = str.replace(/#/, '-sharp-').replace(/\./, '-dot-').replace(/-$/, '')
3
+ return s
4
+ .toLowerCase()
5
+ .normalize('NFD') // Decompose Unicode characters
6
+ .replace(/[\u0300-\u036f]/g, '') // Remove diacritical marks
7
+ .replace(/đ/g, 'd') // Handle Vietnamese "đ"
8
+ .replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric characters with dashes
9
+ .replace(/^-+|-+$/g, '') // Remove leading or trailing dashes
10
+ }
11
+
12
+ export const useSlugFilter = (slug: string) => (str: string) => useSlug(str) === slug
@@ -0,0 +1,43 @@
1
+ import { Icon } from '@iconify/vue'
2
+ import type { Theme } from 'vitepress'
3
+ import DefaultTheme from 'vitepress/theme'
4
+ import Layout from './layouts/Layout.vue'
5
+ import ArchivesPage from './pages/ArchivesPage.vue'
6
+ import CategoriesPage from './pages/CategoriesPage.vue'
7
+ import HomePage from './pages/HomePage.vue'
8
+ import Page from './pages/Page.vue'
9
+ import PostsPage from './pages/PostsPage.vue'
10
+ import ProjectsPage from './pages/ProjectsPage.vue'
11
+ import TagsPage from './pages/TagsPage.vue'
12
+ import i18n from './plugins/i18n'
13
+ import './styles/index.css'
14
+ import type { EnhanceAppContext } from 'vitepress'
15
+
16
+ export type * from './types'
17
+
18
+ const theme: Theme = {
19
+ extends: DefaultTheme,
20
+ Layout,
21
+ enhanceApp({ app }: EnhanceAppContext) {
22
+ app.component('Icon', Icon)
23
+ app.component('Page', Page)
24
+ app.component('PostsPage', PostsPage)
25
+ app.component('HomePage', HomePage)
26
+ app.component('ProjectsPage', ProjectsPage)
27
+ app.component('CategoriesPage', CategoriesPage)
28
+ app.component('TagsPage', TagsPage)
29
+ app.component('ArchivesPage', ArchivesPage)
30
+ app.use(i18n)
31
+ },
32
+ } satisfies Theme
33
+
34
+ export type {
35
+ DonationPluginConfig,
36
+ GoogleAnalyticsOptions,
37
+ Post,
38
+ PostDate,
39
+ Project,
40
+ ThemeConfig,
41
+ } from './types'
42
+
43
+ export default theme
@@ -0,0 +1,25 @@
1
+ <script setup lang='ts'>
2
+ import { useData } from 'vitepress'
3
+ import DefaultTheme from 'vitepress/theme'
4
+ import Footer from '../components/Footer.vue'
5
+ import LocaleSwitcher from '../components/LocaleSwitcher.vue'
6
+
7
+ const { Layout: DefaultLayout } = DefaultTheme
8
+ const { frontmatter } = useData()
9
+ </script>
10
+
11
+ <template>
12
+ <DefaultLayout>
13
+ <template #doc-before>
14
+ <div class="vp-doc">
15
+ <h1>{{ frontmatter.title }}</h1>
16
+ </div>
17
+ </template>
18
+ <template #nav-bar-content-after>
19
+ <LocaleSwitcher />
20
+ </template>
21
+ <template #layout-bottom>
22
+ <Footer />
23
+ </template>
24
+ </DefaultLayout>
25
+ </template>
@@ -0,0 +1,43 @@
1
+ <script setup lang="ts">
2
+ import { computed, type PropType } from 'vue'
3
+ import { useI18n } from 'vue-i18n'
4
+ import TermBadge from '../components/TermBadge.vue'
5
+ import { useRank } from '../composables'
6
+ import type { Post } from '../types'
7
+
8
+ const props = defineProps({
9
+ posts: {
10
+ type: Object as PropType<Post[]>,
11
+ required: true,
12
+ default: [],
13
+ },
14
+ })
15
+
16
+ const { t } = useI18n()
17
+
18
+ const totalPostsByYears = computed(() =>
19
+ useRank(props.posts.flatMap((post) => new Date(post.date).getFullYear().toString())),
20
+ )
21
+ const sortedYearDesc = computed(() =>
22
+ Object.keys(totalPostsByYears.value)
23
+ .map((str) => Number(str))
24
+ .reverse(),
25
+ )
26
+ </script>
27
+
28
+ <template>
29
+ <Page :title="t('archives')">
30
+ <div class="flex flex-wrap pt-4">
31
+ <div v-if="Object.keys(sortedYearDesc).length === 0" class="mt-2 mb-2 mr-5">
32
+ {{ t('no_archive') }}
33
+ </div>
34
+ <TermBadge
35
+ v-for="year in sortedYearDesc"
36
+ :key="year"
37
+ :title="`${year}`"
38
+ :count="totalPostsByYears[year]"
39
+ :href="`/archives/${year}`"
40
+ />
41
+ </div>
42
+ </Page>
43
+ </template>
@@ -0,0 +1,30 @@
1
+ <script setup lang='ts'>
2
+ import { computed, type PropType } from 'vue'
3
+ import { useI18n } from 'vue-i18n'
4
+ import TermBadge from '../components/TermBadge.vue'
5
+ import { useRank, useSlug } from '../composables'
6
+ import type { Post } from '../types'
7
+
8
+ const props = defineProps({
9
+ posts: {
10
+ type: Object as PropType<Post[]>,
11
+ required: true,
12
+ default: [],
13
+ },
14
+ })
15
+
16
+ const { t } = useI18n()
17
+
18
+ const categories = computed(() => useRank(props.posts.flatMap((post) => post.categories)))
19
+ </script>
20
+
21
+ <template>
22
+ <Page :title="t('categories')">
23
+ <div class="flex flex-wrap pt-4">
24
+ <div v-if="Object.keys(categories).length === 0" class="mt-2 mb-2 mr-5">
25
+ {{ t('no_category') }}
26
+ </div>
27
+ <TermBadge v-for="(count, category) in categories" :key="category" :title="category" :count="count" :href="`/categories/${useSlug(category)}`" />
28
+ </div>
29
+ </Page>
30
+ </template>
@@ -0,0 +1,18 @@
1
+ <script setup lang='ts'>
2
+ import type { PropType } from 'vue'
3
+ import { useI18n } from 'vue-i18n'
4
+ import type { Post } from '../types'
5
+
6
+ defineProps({
7
+ posts: {
8
+ type: Object as PropType<Post[]>,
9
+ required: true,
10
+ default: [],
11
+ },
12
+ })
13
+ const { t } = useI18n()
14
+ </script>
15
+
16
+ <template>
17
+ <PostsPage :title="posts.length > 0 ? t('all_posts') : ''" :posts="posts" />
18
+ </template>
@@ -0,0 +1,20 @@
1
+ <script setup lang='ts'>
2
+ defineProps({
3
+ title: {
4
+ type: String,
5
+ required: true,
6
+ default: '',
7
+ },
8
+ })
9
+ </script>
10
+
11
+ <template>
12
+ <div class="pt-6 pb-8 space-y-2 md:space-y-5">
13
+ <h1
14
+ class="mb-6 text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14"
15
+ >
16
+ {{ title }}
17
+ </h1>
18
+ </div>
19
+ <slot />
20
+ </template>
@@ -0,0 +1,23 @@
1
+ <script setup lang='ts'>
2
+ import type { PropType } from 'vue'
3
+ import PostList from '../components/PostList.vue'
4
+ import type { Post } from '../types'
5
+
6
+ defineProps({
7
+ title: {
8
+ type: String,
9
+ required: true,
10
+ },
11
+ posts: {
12
+ type: Object as PropType<Post[]>,
13
+ required: true,
14
+ default: [],
15
+ },
16
+ })
17
+ </script>
18
+
19
+ <template>
20
+ <Page :title="title">
21
+ <PostList :posts="posts" />
22
+ </Page>
23
+ </template>
@@ -0,0 +1,65 @@
1
+ <script setup lang="ts">
2
+ import type { PropType } from 'vue'
3
+ import { computed } from 'vue'
4
+ import { useI18n } from 'vue-i18n'
5
+ import ProjectList from '../components/ProjectList.vue'
6
+ import { useSlug, useSlugFilter } from '../composables'
7
+ import type { Project } from '../types'
8
+
9
+ const props = defineProps({
10
+ projectPage: {
11
+ type: String,
12
+ required: false,
13
+ default: '/projects.html',
14
+ },
15
+ projects: {
16
+ type: Object as PropType<Project[]>,
17
+ required: true,
18
+ default: [],
19
+ },
20
+ technology: {
21
+ type: String,
22
+ required: false,
23
+ },
24
+ })
25
+
26
+ const { t } = useI18n()
27
+ const tech = props.technology
28
+ const hasTechParam = typeof tech === 'string' && tech.length > 0
29
+ const byTech: (project: Project) => boolean = (project) => !hasTechParam || project.techs.some(useSlugFilter(tech))
30
+ const projectsByTech = computed(() => props.projects.filter(byTech))
31
+ const technologies = computed(() => {
32
+ const uniqueTechnologies = [...new Set(props.projects.flatMap((project) => project.techs))]
33
+ return uniqueTechnologies
34
+ .map((name) => ({
35
+ name,
36
+ slug: useSlug(name),
37
+ selected: hasTechParam && useSlug(name) === tech,
38
+ }))
39
+ .sort((a, b) => (b.selected === a.selected ? 0 : b.selected ? 1 : -1))
40
+ })
41
+ </script>
42
+
43
+ <template>
44
+ <Page :title="t('my_projects')">
45
+ <div>
46
+ <p>
47
+ <span class="mr-2">Technologies</span>
48
+ <a v-if="hasTechParam" :href="projectPage">Reset</a>
49
+ </p>
50
+ <p class="flex flex-row flex-wrap">
51
+ <a
52
+ v-for="technology in technologies"
53
+ :class="[
54
+ 'border rounded-md m-1 p-1',
55
+ technology.selected ? 'bg-primary text-white! border-primary' : ''
56
+ ]"
57
+ :href="`/projects-by-tech/${technology.slug}`"
58
+ >
59
+ {{ technology.name }}
60
+ </a>
61
+ </p>
62
+ </div>
63
+ <ProjectList :projects="projectsByTech" />
64
+ </Page>
65
+ </template>
@@ -0,0 +1,30 @@
1
+ <script setup lang='ts'>
2
+ import { computed, type PropType } from 'vue'
3
+ import { useI18n } from 'vue-i18n'
4
+ import TermBadge from '../components/TermBadge.vue'
5
+ import { useRank, useSlug } from '../composables'
6
+ import type { Post } from '../types'
7
+
8
+ const props = defineProps({
9
+ posts: {
10
+ type: Object as PropType<Post[]>,
11
+ required: true,
12
+ default: [],
13
+ },
14
+ })
15
+
16
+ const { t } = useI18n()
17
+
18
+ const tags = computed(() => useRank(props.posts.flatMap((post) => post.tags)))
19
+ </script>
20
+
21
+ <template>
22
+ <Page :title="t('tags')">
23
+ <div class="flex flex-wrap pt-4">
24
+ <div v-if="Object.keys(tags).length === 0" class="mt-2 mb-2 mr-5">
25
+ {{ t('no_tag') }}
26
+ </div>
27
+ <TermBadge v-for="(count, tag) in tags" :key="tag" :title="tag" :count="count" :href="`/tags/${useSlug(tag)}`" />
28
+ </div>
29
+ </Page>
30
+ </template>
@@ -0,0 +1,43 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import type { CustomDonationConfig, DonationPluginConfig } from '..'
4
+ import { supportedDonationPlatforms } from '..'
5
+ import DonationButton from './DonationButton.vue'
6
+
7
+ const props = defineProps<{
8
+ donation: DonationPluginConfig
9
+ }>()
10
+
11
+ const donationConfigs = props.donation
12
+
13
+ const computedDonationConfig = computed(() => {
14
+ const donationConfig: { [key: string]: CustomDonationConfig } = {}
15
+
16
+ for (const donationPlatform in donationConfigs) {
17
+ if (donationPlatform === 'custom') {
18
+ continue
19
+ }
20
+ const donationId: string = donationConfigs[donationPlatform as keyof typeof donationConfigs] as string
21
+ donationConfig[donationPlatform] = {
22
+ ...supportedDonationPlatforms[donationPlatform],
23
+ donationId,
24
+ }
25
+ }
26
+ return {
27
+ ...donationConfig,
28
+ ...donationConfigs.custom,
29
+ }
30
+ })
31
+ </script>
32
+
33
+ <template>
34
+ <template
35
+ v-for="(donationConfig, donationPlatform) in computedDonationConfig"
36
+ :key="donationConfig?.name"
37
+ >
38
+ <DonationButton
39
+ v-bind="donationConfig"
40
+ :donation-platform="donationPlatform as string"
41
+ />
42
+ </template>
43
+ </template>
@@ -0,0 +1,22 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { CustomDonationConfig } from '..'
4
+
5
+ interface DonationButtonProps extends CustomDonationConfig {
6
+ donationPlatform: string
7
+ }
8
+
9
+ const props = defineProps<DonationButtonProps>()
10
+
11
+ const donationUrl = computed(() => new URL(props.donationId, props.donationBaseUrl).toString())
12
+ </script>
13
+
14
+ <template>
15
+ <a :href="donationUrl" target="_blank">
16
+ <img
17
+ :src="donationButtonImage"
18
+ :alt="donationPlatform"
19
+ :style="donationButtonStyle"
20
+ />
21
+ </a>
22
+ </template>
@@ -0,0 +1,42 @@
1
+ export declare interface DonationPluginConfig {
2
+ buymeacoffee?: string
3
+ kofi?: string
4
+ paypal?: string
5
+ custom?: {
6
+ [key: string]: CustomDonationConfig
7
+ }
8
+ }
9
+
10
+ export declare interface CustomDonationBaseConfig {
11
+ donationBaseUrl: string
12
+ donationButtonImage: string
13
+ donationButtonStyle?: Record<string, string>
14
+ }
15
+
16
+ export declare interface CustomDonationConfig extends CustomDonationBaseConfig {
17
+ donationId: string
18
+ }
19
+
20
+ export const supportedDonationPlatforms: {
21
+ [key: string]: CustomDonationBaseConfig
22
+ } = {
23
+ buymeacoffee: {
24
+ donationBaseUrl: 'https://buymeacoffee.com',
25
+ donationButtonImage: 'https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png',
26
+ donationButtonStyle: {
27
+ height: '60px !important',
28
+ width: '217px !important',
29
+ },
30
+ },
31
+ kofi: {
32
+ donationBaseUrl: 'https://ko-fi.com',
33
+ donationButtonImage: 'https://storage.ko-fi.com/cdn/brandasset/kofi_bg_tag_white.png',
34
+ donationButtonStyle: {
35
+ height: '60px !important',
36
+ },
37
+ },
38
+ paypal: {
39
+ donationBaseUrl: 'https://paypal.me',
40
+ donationButtonImage: 'https://paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif',
41
+ },
42
+ }