@fy-/fws-vue 1.8.8 → 1.9.0
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.
- package/components/ui/DefaultBreadcrumb.vue +19 -17
- package/composables/seo.ts +25 -97
- package/composables/ssr.ts +2 -0
- package/package.json +2 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import { HomeIcon, ChevronRightIcon } from "@heroicons/vue/24/solid";
|
|
3
3
|
import type { BreadcrumbLink } from "../../types";
|
|
4
|
+
import { defineBreadcrumb, useSchemaOrg } from "@unhead/schema-org";
|
|
4
5
|
|
|
5
|
-
withDefaults(
|
|
6
|
+
const props = withDefaults(
|
|
6
7
|
defineProps<{
|
|
7
8
|
nav: BreadcrumbLink[];
|
|
8
9
|
showHome: Boolean;
|
|
@@ -12,23 +13,26 @@ withDefaults(
|
|
|
12
13
|
showHome: () => true,
|
|
13
14
|
},
|
|
14
15
|
);
|
|
16
|
+
|
|
17
|
+
const breadcrumbsSchemaFormat = props.nav.map((item, index) => {
|
|
18
|
+
return {
|
|
19
|
+
position: index + 1,
|
|
20
|
+
name: item.name,
|
|
21
|
+
item: item.to,
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
useSchemaOrg([
|
|
26
|
+
defineBreadcrumb({
|
|
27
|
+
itemListElement: breadcrumbsSchemaFormat,
|
|
28
|
+
}),
|
|
29
|
+
]);
|
|
15
30
|
</script>
|
|
16
31
|
|
|
17
32
|
<template>
|
|
18
|
-
<ol
|
|
19
|
-
class="inline-flex items-center flex-wrap"
|
|
20
|
-
itemscope
|
|
21
|
-
itemtype="https://schema.org/BreadcrumbList"
|
|
22
|
-
>
|
|
33
|
+
<ol class="inline-flex items-center flex-wrap">
|
|
23
34
|
<template v-for="(item, index) in nav" :key="`bc_${index.toString()}`">
|
|
24
|
-
<li
|
|
25
|
-
class="inline-flex items-center"
|
|
26
|
-
itemprop="itemListElement"
|
|
27
|
-
itemscope
|
|
28
|
-
itemtype="https://schema.org/ListItem"
|
|
29
|
-
>
|
|
30
|
-
<meta itemprop="position" :content="(index + 1).toString()" />
|
|
31
|
-
|
|
35
|
+
<li class="inline-flex items-center">
|
|
32
36
|
<ChevronRightIcon
|
|
33
37
|
v-if="index != 0"
|
|
34
38
|
:class="
|
|
@@ -46,7 +50,6 @@ withDefaults(
|
|
|
46
50
|
? 'text-xs font-medium text-fv-neutral-700 hover:text-fv-neutral-900 dark:text-fv-neutral-200 dark:hover:text-white'
|
|
47
51
|
: 'text-xs font-medium text-fv-neutral-700 hover:text-fv-neutral-900 dark:text-fv-neutral-200 dark:hover:text-white'
|
|
48
52
|
"
|
|
49
|
-
itemprop="item"
|
|
50
53
|
>
|
|
51
54
|
<HomeIcon
|
|
52
55
|
v-if="showHome && index === 0"
|
|
@@ -56,12 +59,11 @@ withDefaults(
|
|
|
56
59
|
: 'w-4 h-4 text-fv-neutral-400 inline-block mx-0.5 md:mx-1.5'
|
|
57
60
|
"
|
|
58
61
|
/>
|
|
59
|
-
<span
|
|
62
|
+
<span>{{ item.name }}</span>
|
|
60
63
|
</router-link>
|
|
61
64
|
<span
|
|
62
65
|
v-else
|
|
63
66
|
class="text-xs font-medium text-fv-neutral-500 dark:text-fv-neutral-200"
|
|
64
|
-
itemprop="name"
|
|
65
67
|
>
|
|
66
68
|
{{ item.name }}
|
|
67
69
|
</span>
|
package/composables/seo.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import type { Ref } from "vue";
|
|
3
|
-
import {
|
|
3
|
+
import { useSeo, useSeoMeta } from "@unhead/vue";
|
|
4
4
|
import { getURL, getLocale, getPrefix } from "@fy-/fws-js";
|
|
5
5
|
import { useRoute } from "vue-router";
|
|
6
6
|
|
|
@@ -31,8 +31,7 @@ export const useSeo = (seo: Ref<LazyHead>, initial: boolean = false) => {
|
|
|
31
31
|
const route = useRoute();
|
|
32
32
|
const currentLocale = seo.value.locale || getLocale();
|
|
33
33
|
const url = getURL();
|
|
34
|
-
|
|
35
|
-
title: () => seo.value.title || "",
|
|
34
|
+
useSeo({
|
|
36
35
|
link: () => {
|
|
37
36
|
const links: any[] = [];
|
|
38
37
|
|
|
@@ -69,100 +68,29 @@ export const useSeo = (seo: Ref<LazyHead>, initial: boolean = false) => {
|
|
|
69
68
|
|
|
70
69
|
return links;
|
|
71
70
|
},
|
|
72
|
-
|
|
73
|
-
const metas: any[] = [];
|
|
74
|
-
// @ts-ignore
|
|
75
|
-
metas.push(
|
|
76
|
-
{ property: "og:locale", content: currentLocale },
|
|
77
|
-
{
|
|
78
|
-
property: "og:url",
|
|
79
|
-
key: "og:url",
|
|
80
|
-
content: seo.value.url
|
|
81
|
-
? seo.value.url
|
|
82
|
-
: `${getURL().Scheme}://${getURL().Host}${route.fullPath}`,
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
property: "og:type",
|
|
86
|
-
content: seo.value.type || "website",
|
|
87
|
-
key: "og:type",
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
name: "robots",
|
|
91
|
-
content:
|
|
92
|
-
"index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1",
|
|
93
|
-
key: "robots",
|
|
94
|
-
},
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
if (seo.value.isAdult) {
|
|
98
|
-
// @ts-ignore
|
|
99
|
-
metas.push(
|
|
100
|
-
{ name: "rating", content: "adult", key: "rating" },
|
|
101
|
-
{
|
|
102
|
-
name: "RATING",
|
|
103
|
-
content: "RTA-5042-1996-1400-1577-RTA",
|
|
104
|
-
key: "RTA",
|
|
105
|
-
},
|
|
106
|
-
{ property: "rating", content: "adult", key: "rating-property" },
|
|
107
|
-
{
|
|
108
|
-
property: "RATING",
|
|
109
|
-
content: "RTA-5042-1996-1400-1577-RTA",
|
|
110
|
-
key: "RTA-property",
|
|
111
|
-
},
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const metaPairs = [
|
|
116
|
-
["name", "og:site_name"],
|
|
117
|
-
["title", "og:title", "twitter:title"],
|
|
118
|
-
["description", "og:description", "twitter:description"],
|
|
119
|
-
["published", "article:published_time"],
|
|
120
|
-
["modified", "article:modified_time"],
|
|
121
|
-
["imageWidth", "og:image:width"],
|
|
122
|
-
["imageHeight", "og:image:height"],
|
|
123
|
-
["imageType", "og:image:type", "twitter:image:type"],
|
|
124
|
-
["image", "og:image", "twitter:image"],
|
|
125
|
-
["keywords"],
|
|
126
|
-
];
|
|
127
|
-
|
|
128
|
-
metaPairs.forEach(([seoKey, ...properties]) => {
|
|
129
|
-
const seoValue = seo.value[seoKey as keyof LazyHead];
|
|
130
|
-
|
|
131
|
-
if (seoValue !== undefined && seoValue !== null && seoValue !== "") {
|
|
132
|
-
properties.forEach((property) => {
|
|
133
|
-
if (
|
|
134
|
-
property === "description" ||
|
|
135
|
-
property === "keywords" ||
|
|
136
|
-
property.startsWith("twitter")
|
|
137
|
-
) {
|
|
138
|
-
// @ts-ignore
|
|
139
|
-
metas.push({
|
|
140
|
-
name: property,
|
|
141
|
-
content: seoValue,
|
|
142
|
-
key: property,
|
|
143
|
-
});
|
|
144
|
-
} else {
|
|
145
|
-
// @ts-ignore
|
|
146
|
-
metas.push({
|
|
147
|
-
property,
|
|
148
|
-
content: seoValue as string,
|
|
149
|
-
key: property,
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
if (seo.value.description) {
|
|
157
|
-
// @ts-ignore
|
|
158
|
-
metas.push({
|
|
159
|
-
name: "description",
|
|
160
|
-
content: seo.value.description,
|
|
161
|
-
key: "description",
|
|
162
|
-
});
|
|
163
|
-
}
|
|
71
|
+
});
|
|
164
72
|
|
|
165
|
-
|
|
166
|
-
|
|
73
|
+
useSeoMeta({
|
|
74
|
+
url: seo.value.url
|
|
75
|
+
? seo.value.url
|
|
76
|
+
: `${getURL().Scheme}://${getURL().Host}${route.fullPath}`,
|
|
77
|
+
robots:
|
|
78
|
+
"index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1",
|
|
79
|
+
title: () => seo.value.title,
|
|
80
|
+
rating: seo.value.isAdult ? "adult" : undefined,
|
|
81
|
+
RATING: seo.value.isAdult ? "adult" : undefined,
|
|
82
|
+
ogTitle: () => seo.value.title,
|
|
83
|
+
ogDescription: () => seo.value.description,
|
|
84
|
+
ogImage: () => seo.value.image || undefined,
|
|
85
|
+
ogImageType: () => seo.value.imageType || undefined,
|
|
86
|
+
ogImageWidth: () => seo.value.imageWidth || undefined,
|
|
87
|
+
ogImageHeight: () => seo.value.imageHeight || undefined,
|
|
88
|
+
twitterCard: "summary_large_image",
|
|
89
|
+
twitterTitle: () => seo.value.title,
|
|
90
|
+
twitterDescription: () => seo.value.description,
|
|
91
|
+
twitterImage: () => seo.value.image || undefined,
|
|
92
|
+
twitterImageAlt: () => seo.value.title,
|
|
93
|
+
description: () => seo.value.description || undefined,
|
|
94
|
+
keywords: () => seo.value.keywords?.split(",") || undefined,
|
|
167
95
|
});
|
|
168
96
|
};
|
package/composables/ssr.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface SSRResult {
|
|
|
14
14
|
meta?: string;
|
|
15
15
|
link?: string;
|
|
16
16
|
bodyAttributes?: string;
|
|
17
|
+
bodyTagsOpen?: string;
|
|
17
18
|
htmlAttributes?: string;
|
|
18
19
|
bodyTags?: string;
|
|
19
20
|
app?: string;
|
|
@@ -71,6 +72,7 @@ export async function initVueServer(
|
|
|
71
72
|
result.bodyAttributes = payload.bodyAttrs;
|
|
72
73
|
result.htmlAttributes = payload.htmlAttrs;
|
|
73
74
|
result.bodyTags = payload.bodyTags;
|
|
75
|
+
result.bodyTagsOpen = payload.bodyTagsOpen;
|
|
74
76
|
result.app = html;
|
|
75
77
|
if (serverRouter.status != 200) {
|
|
76
78
|
if ([301, 302, 303, 307].includes(serverRouter.status)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fy-/fws-vue",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"author": "Florian 'Fy' Gasquez <m@fy.to>",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"@fy-/fws-types": "^0.0.x",
|
|
32
32
|
"@unhead/ssr": "^1.9.x",
|
|
33
33
|
"@unhead/vue": "^1.9.x",
|
|
34
|
+
"@unhead/schema-org": "1.9.x",
|
|
34
35
|
"@vuelidate/core": "^2.0.x",
|
|
35
36
|
"@vuelidate/validators": "^2.0.x",
|
|
36
37
|
"@vueuse/core": "^10.x.x",
|