@studio-fes/layer-strapi 0.1.2 → 0.1.3
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/CHANGELOG.md +6 -0
- package/app/components/StrapiImage.vue +3 -3
- package/app/components/StrapiMedia.vue +32 -0
- package/app/components/StrapiVideo.vue +27 -0
- package/app/composables/useStrapiSEO.ts +7 -26
- package/app/types/graphql-operations.ts +27 -27
- package/app/utils/check-page-data.ts +5 -5
- package/app/utils/parse-strapi-image.ts +0 -2
- package/app/utils/parse-strapi-video.ts +17 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import type { NuxtImgProps } from '@studio-fes/layer-base/app/components/Image.vue'
|
|
3
3
|
import type { StrapiFileFragment } from '~/types/graphql-operations'
|
|
4
4
|
|
|
5
|
-
interface
|
|
5
|
+
export interface StrapiImageProps extends NuxtImgProps {
|
|
6
6
|
image?: StrapiFileFragment
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
const props = defineProps<
|
|
9
|
+
const props = defineProps<StrapiImageProps>()
|
|
10
10
|
const imageProps = computed(() => ({
|
|
11
|
-
...props,
|
|
11
|
+
...reactiveOmit(props, 'image'),
|
|
12
12
|
...parseStrapiImage(props.image),
|
|
13
13
|
}))
|
|
14
14
|
</script>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { StrapiImageProps } from '@studio-fes/layer-strapi/app/components/StrapiImage.vue'
|
|
3
|
+
import type { StrapiVideoProps } from '~/components/StrapiVideo.vue'
|
|
4
|
+
import type { StrapiFileFragment } from '~/types/graphql-operations'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
media: StrapiFileFragment
|
|
8
|
+
imgProps?: StrapiImageProps
|
|
9
|
+
videoProps?: StrapiVideoProps
|
|
10
|
+
}>()
|
|
11
|
+
|
|
12
|
+
const isVideo = computed(() => !mediaIsImage(props.media))
|
|
13
|
+
const isImage = computed(() => mediaIsImage(props.media))
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<StrapiImage
|
|
18
|
+
v-if="isImage"
|
|
19
|
+
v-bind="imgProps"
|
|
20
|
+
:image="media"
|
|
21
|
+
/>
|
|
22
|
+
<StrapiVideo
|
|
23
|
+
v-else-if="isVideo"
|
|
24
|
+
v-bind="videoProps"
|
|
25
|
+
:video="media"
|
|
26
|
+
>
|
|
27
|
+
<template #default="{ progress, isPlaying }">
|
|
28
|
+
<slot name="media-chrome" :progress :is-playing />
|
|
29
|
+
</template>
|
|
30
|
+
</StrapiVideo>
|
|
31
|
+
<pre v-else>There is no format for you.</pre>
|
|
32
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { VideoProps } from '@studio-fes/layer-base/app/components/Video.vue'
|
|
3
|
+
import type { StrapiFileFragment } from '~/types/graphql-operations'
|
|
4
|
+
import Video from '@studio-fes/layer-base/app/components/Video.vue'
|
|
5
|
+
|
|
6
|
+
export interface StrapiVideoProps extends VideoProps {
|
|
7
|
+
video?: StrapiFileFragment
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const props = defineProps<StrapiVideoProps>()
|
|
11
|
+
|
|
12
|
+
const videoProps = computed(() => ({
|
|
13
|
+
...reactiveOmit(props, 'video'),
|
|
14
|
+
...parseStrapiVideo(props.video),
|
|
15
|
+
}))
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<Video
|
|
20
|
+
v-if="videoProps"
|
|
21
|
+
v-bind="videoProps"
|
|
22
|
+
>
|
|
23
|
+
<template #default="{ progress, isPlaying }">
|
|
24
|
+
<slot :progress :is-playing />
|
|
25
|
+
</template>
|
|
26
|
+
</Video>
|
|
27
|
+
</template>
|
|
@@ -1,37 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SeoMetaFragment } from '~/types/graphql-operations'
|
|
2
2
|
|
|
3
3
|
export function useStrapiSEO(seoData?: MaybeRefOrGetter<SeoMetaFragment>) {
|
|
4
4
|
const nuxtApp = useNuxtApp()
|
|
5
5
|
const runtimeConfig = useRuntimeConfig()
|
|
6
|
+
const nuxtImage = useImage()
|
|
6
7
|
|
|
7
8
|
const seo = computed(() => {
|
|
8
9
|
if (!seoData)
|
|
9
10
|
return {}
|
|
10
11
|
|
|
11
|
-
const { metaTitle, metaDescription, metaImage,
|
|
12
|
-
|
|
13
|
-
const metaSocials = (metaSocial as MetaSocialFragment[]).map((social: MetaSocialFragment) => {
|
|
14
|
-
let key = null
|
|
15
|
-
|
|
16
|
-
switch (social.socialNetwork) {
|
|
17
|
-
case 'Facebook':
|
|
18
|
-
key = 'og'
|
|
19
|
-
break
|
|
20
|
-
case 'Twitter':
|
|
21
|
-
key = 'twitter'
|
|
22
|
-
break
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (!key)
|
|
26
|
-
return []
|
|
27
|
-
|
|
28
|
-
return [
|
|
29
|
-
{ property: `${key}:title`, content: social.title || metaTitle },
|
|
30
|
-
{ property: `${key}:description`, content: social.description || metaDescription },
|
|
31
|
-
{ property: `${key}:image`, content: runtimeConfig.public.imageDomain + (social.image || metaImage)?.url },
|
|
32
|
-
{ property: `${key}:image:alt`, content: (social.image || metaImage)?.alternativeText || metaTitle },
|
|
33
|
-
]
|
|
34
|
-
})
|
|
12
|
+
const { metaTitle, metaDescription, metaImage, openGraph, metaRobots, keywords } = toValue(seoData)
|
|
35
13
|
|
|
36
14
|
return {
|
|
37
15
|
title: metaTitle,
|
|
@@ -39,8 +17,11 @@ export function useStrapiSEO(seoData?: MaybeRefOrGetter<SeoMetaFragment>) {
|
|
|
39
17
|
{ name: 'description', content: metaDescription },
|
|
40
18
|
{ name: 'robots', content: metaRobots },
|
|
41
19
|
{ name: 'keywords', content: keywords },
|
|
20
|
+
{ property: `og:title`, content: openGraph?.ogTitle || metaTitle },
|
|
21
|
+
{ property: `og:description`, content: openGraph?.ogDescription || metaDescription },
|
|
22
|
+
{ property: `og:image`, content: nuxtImage(runtimeConfig.public.imageDomain + (openGraph?.ogImage || metaImage)?.url, { width: 1200, height: 630, quality: 60 }) },
|
|
23
|
+
{ property: `og:image:alt`, content: (openGraph?.ogImage || metaImage)?.alternativeText || metaTitle },
|
|
42
24
|
{ property: 'twitter:card', content: 'summary_large_image' },
|
|
43
|
-
...metaSocials.flat(),
|
|
44
25
|
],
|
|
45
26
|
}
|
|
46
27
|
})
|
|
@@ -36,8 +36,8 @@ export interface EntryInterfaceTypeFragment {
|
|
|
36
36
|
* metaImage {
|
|
37
37
|
* ...StrapiFile
|
|
38
38
|
* }
|
|
39
|
-
*
|
|
40
|
-
* ...
|
|
39
|
+
* openGraph {
|
|
40
|
+
* ...OpenGraph
|
|
41
41
|
* }
|
|
42
42
|
* keywords
|
|
43
43
|
* metaRobots
|
|
@@ -52,14 +52,37 @@ export interface SeoMetaFragment {
|
|
|
52
52
|
id: string | number
|
|
53
53
|
keywords?: string
|
|
54
54
|
metaDescription: string
|
|
55
|
-
metaImage
|
|
55
|
+
metaImage?: StrapiFileFragment
|
|
56
56
|
metaRobots?: string
|
|
57
|
-
metaSocial?: (MetaSocialFragment | null)[]
|
|
58
57
|
metaTitle: string
|
|
59
58
|
metaViewport?: string
|
|
59
|
+
openGraph?: OpenGraphFragment
|
|
60
60
|
structuredData?: any
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```graphql
|
|
67
|
+
* fragment OpenGraph on ComponentSharedOpenGraph {
|
|
68
|
+
* ogTitle
|
|
69
|
+
* ogDescription
|
|
70
|
+
* ogImage {
|
|
71
|
+
* ...StrapiFile
|
|
72
|
+
* }
|
|
73
|
+
* ogUrl
|
|
74
|
+
* ogType
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export interface OpenGraphFragment {
|
|
79
|
+
ogDescription: string
|
|
80
|
+
ogImage?: StrapiFileFragment
|
|
81
|
+
ogTitle: string
|
|
82
|
+
ogType?: string
|
|
83
|
+
ogUrl?: string
|
|
84
|
+
}
|
|
85
|
+
|
|
63
86
|
/**
|
|
64
87
|
*
|
|
65
88
|
* @example
|
|
@@ -82,26 +105,3 @@ export interface StrapiFileFragment {
|
|
|
82
105
|
url: string
|
|
83
106
|
width?: number
|
|
84
107
|
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* ```graphql
|
|
90
|
-
* fragment MetaSocial on ComponentSharedMetaSocial {
|
|
91
|
-
* description
|
|
92
|
-
* id
|
|
93
|
-
* socialNetwork
|
|
94
|
-
* title
|
|
95
|
-
* image {
|
|
96
|
-
* ...StrapiFile
|
|
97
|
-
* }
|
|
98
|
-
* }
|
|
99
|
-
* ```
|
|
100
|
-
*/
|
|
101
|
-
export interface MetaSocialFragment {
|
|
102
|
-
description: string
|
|
103
|
-
id: string | number
|
|
104
|
-
image?: StrapiFileFragment
|
|
105
|
-
socialNetwork: EnumComponentsharedmetasocialSocialnetwork
|
|
106
|
-
title: string
|
|
107
|
-
}
|
|
@@ -16,8 +16,8 @@ export function throw404(
|
|
|
16
16
|
message: string = 'Page Not Found.',
|
|
17
17
|
): never {
|
|
18
18
|
throw createError({
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
status: 404,
|
|
20
|
+
statusText: message,
|
|
21
21
|
fatal: true,
|
|
22
22
|
})
|
|
23
23
|
}
|
|
@@ -33,8 +33,8 @@ export function throw500(
|
|
|
33
33
|
message: string = 'Server Error',
|
|
34
34
|
): never {
|
|
35
35
|
throw createError({
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
status: 500,
|
|
37
|
+
statusText: message,
|
|
38
38
|
fatal: true,
|
|
39
39
|
})
|
|
40
40
|
}
|
|
@@ -67,7 +67,7 @@ export function checkPageData(response: Awaited<ReturnType<typeof useAsyncData<G
|
|
|
67
67
|
throw500('No data returned from the server.')
|
|
68
68
|
}
|
|
69
69
|
if (response.error.value || response.data.value?.errors?.length) {
|
|
70
|
-
throw500(response.error.value?.
|
|
70
|
+
throw500(response.error.value?.statusText || response.error.value?.message || response.data.value?.errors?.map((e: any) => e.message).join(', ') || 'Unknown error occurred.')
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
if (!response.data.value?.data) {
|
|
@@ -14,12 +14,10 @@ export function parseStrapiImage(image: StrapiFileFragment | undefined): NuxtImg
|
|
|
14
14
|
height,
|
|
15
15
|
src: runtimeConfig.public.imageDomain + src,
|
|
16
16
|
alt,
|
|
17
|
-
format: 'webp',
|
|
18
17
|
} as {
|
|
19
18
|
width: number
|
|
20
19
|
height: number
|
|
21
20
|
src: string
|
|
22
21
|
alt: string
|
|
23
|
-
format?: string
|
|
24
22
|
}
|
|
25
23
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { VideoProps } from '@studio-fes/layer-base/app/components/Video.vue'
|
|
2
|
+
import type { StrapiFileFragment } from '~/types/graphql-operations'
|
|
3
|
+
|
|
4
|
+
export function parseStrapiVideo(video: StrapiFileFragment | null | undefined): VideoProps {
|
|
5
|
+
if (!video)
|
|
6
|
+
return {}
|
|
7
|
+
|
|
8
|
+
const runtimeConfig = useRuntimeConfig()
|
|
9
|
+
const id = useState(() => uuidv4())
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
id: `video-${id.value}`,
|
|
13
|
+
src: runtimeConfig.public.imageDomain + video.url,
|
|
14
|
+
width: video.width,
|
|
15
|
+
height: video.height,
|
|
16
|
+
}
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@studio-fes/layer-strapi",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "nuxi dev",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@studio-fes/layer-base": "workspace:*",
|
|
23
|
-
"@types/node": "^25.
|
|
24
|
-
"eslint": "^
|
|
25
|
-
"nuxt": "^4.3.
|
|
23
|
+
"@types/node": "^25.2.3",
|
|
24
|
+
"eslint": "^10.0.0",
|
|
25
|
+
"nuxt": "^4.3.1",
|
|
26
26
|
"typescript": "^5.9.3",
|
|
27
27
|
"vue": "latest"
|
|
28
28
|
},
|