@studio-fes/layer-strapi 0.1.5 → 0.2.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/CHANGELOG.md +6 -0
- package/app/composables/useStrapiSEO.ts +14 -20
- package/app/plugins/vue-strapi-blocks-renderer.ts +5 -0
- package/nuxt.config.ts +7 -0
- package/package.json +3 -2
- package/app/components/StrapiRichText.vue +0 -47
- package/app/types/StrapiRichText.d.ts +0 -52
- package/app/utils/render-strapi-richtext.ts +0 -97
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @studio-fes/layer-strapi
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Media renderer is now stable, added Anchor component and CraftLink renderer. Replaced StrapiRichText with external dependency `vue-strapi-blocks-renderer`
|
|
8
|
+
|
|
3
9
|
## 0.1.5
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -1,30 +1,24 @@
|
|
|
1
1
|
import type { SeoMetaFragment } from '~/types/graphql-operations'
|
|
2
2
|
|
|
3
3
|
export function useStrapiSEO(seoData?: MaybeRefOrGetter<SeoMetaFragment>) {
|
|
4
|
-
const nuxtApp = useNuxtApp()
|
|
5
4
|
const runtimeConfig = useRuntimeConfig()
|
|
6
5
|
const nuxtImage = useImage()
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
if (!seoData) {
|
|
8
|
+
return
|
|
9
|
+
}
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
const { metaTitle, metaDescription, metaImage, openGraph, metaRobots, keywords } = toValue(seoData)
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
{ property: 'twitter:card', content: 'summary_large_image' },
|
|
25
|
-
],
|
|
26
|
-
}
|
|
13
|
+
useSeoMeta({
|
|
14
|
+
title: metaTitle,
|
|
15
|
+
description: metaDescription,
|
|
16
|
+
ogImage: nuxtImage(runtimeConfig.public.imageDomain + (openGraph?.ogImage || metaImage)?.url, { width: 1200, height: 630, quality: 60 }),
|
|
17
|
+
ogTitle: openGraph?.ogTitle || metaTitle,
|
|
18
|
+
ogDescription: openGraph?.ogDescription || metaDescription,
|
|
19
|
+
ogImageAlt: (openGraph?.ogImage || metaImage)?.alternativeText || metaTitle,
|
|
20
|
+
twitterCard: 'summary_large_image',
|
|
21
|
+
robots: metaRobots,
|
|
22
|
+
keywords,
|
|
27
23
|
})
|
|
28
|
-
|
|
29
|
-
nuxtApp.runWithContext(() => useHead(seo.value))
|
|
30
24
|
}
|
package/nuxt.config.ts
CHANGED
|
@@ -7,6 +7,13 @@ export default defineNuxtConfig({
|
|
|
7
7
|
'nuxt-multi-cache',
|
|
8
8
|
],
|
|
9
9
|
|
|
10
|
+
imports: {
|
|
11
|
+
imports: [
|
|
12
|
+
{ name: 'useStrapiBlocksContext', from: 'vue-strapi-blocks-renderer' },
|
|
13
|
+
{ name: 'StrapiBlocks', from: 'vue-strapi-blocks-renderer', type: true },
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
|
|
10
17
|
runtimeConfig: {
|
|
11
18
|
multiCacheEnabled: import.meta.env.NUXT_CACHE_ENABLED === 'true',
|
|
12
19
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@studio-fes/layer-strapi",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "nuxi dev",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@nuxt/eslint": "latest",
|
|
18
18
|
"nuxt-graphql-middleware": "^5.3.2",
|
|
19
|
-
"nuxt-multi-cache": "^4.0.3"
|
|
19
|
+
"nuxt-multi-cache": "^4.0.3",
|
|
20
|
+
"vue-strapi-blocks-renderer": "1.1.1"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"@studio-fes/layer-base": "workspace:*",
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { PropType } from 'vue'
|
|
3
|
-
|
|
4
|
-
import type * as StrapiRichText from '~/types/StrapiRichText'
|
|
5
|
-
|
|
6
|
-
export type StrapiRichTextBlocks = StrapiRichText.BlockNode[]
|
|
7
|
-
|
|
8
|
-
export default defineComponent({
|
|
9
|
-
props: {
|
|
10
|
-
blocks: { type: Array as PropType<StrapiRichTextBlocks>, default: () => [] },
|
|
11
|
-
inline: { type: Boolean, default: false },
|
|
12
|
-
tag: { type: String, default: undefined },
|
|
13
|
-
},
|
|
14
|
-
setup(props) {
|
|
15
|
-
return () => {
|
|
16
|
-
const tag = props.tag || (props.inline ? 'p' : 'div')
|
|
17
|
-
const children = renderStrapiRichText(props.blocks, props.inline)
|
|
18
|
-
const selector = props.inline ? 'inline-richtext' : 'richtext'
|
|
19
|
-
return h(tag, {
|
|
20
|
-
class: selector,
|
|
21
|
-
}, children)
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
})
|
|
25
|
-
</script>
|
|
26
|
-
|
|
27
|
-
<style scoped>
|
|
28
|
-
[class*='richtext'] {
|
|
29
|
-
&:not(.inline-richtext) {
|
|
30
|
-
display: block;
|
|
31
|
-
|
|
32
|
-
:deep(br) {
|
|
33
|
-
display: block;
|
|
34
|
-
height: 1em;
|
|
35
|
-
line-height: 1;
|
|
36
|
-
|
|
37
|
-
content: '';
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
&.inline-richtext {
|
|
42
|
-
:deep(span) {
|
|
43
|
-
display: block;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
</style>
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
// import type { StrapiFileFragment } from '#graphql-operations'
|
|
2
|
-
import type { StrapiFileFragment } from '~/types/graphql-operations'
|
|
3
|
-
|
|
4
|
-
export interface HeadingNode {
|
|
5
|
-
type: 'heading'
|
|
6
|
-
level: 1 | 2 | 3 | 4 | 5 | 6
|
|
7
|
-
children: (TextNode | LinkNode)[]
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface ParagraphNode {
|
|
11
|
-
type: 'paragraph'
|
|
12
|
-
children: (TextNode | LinkNode)[]
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface TextNode {
|
|
16
|
-
text: string
|
|
17
|
-
type: 'text'
|
|
18
|
-
bold?: boolean
|
|
19
|
-
underline?: boolean
|
|
20
|
-
italic?: boolean
|
|
21
|
-
strikethrough?: boolean
|
|
22
|
-
code?: boolean
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface LinkNode {
|
|
26
|
-
url: string
|
|
27
|
-
type: 'link'
|
|
28
|
-
children: TextNode[]
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface ListNode {
|
|
32
|
-
type: 'list'
|
|
33
|
-
format: 'unordered' | 'ordered'
|
|
34
|
-
children: ListItemNode[]
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface ListItemNode {
|
|
38
|
-
type: 'list-item'
|
|
39
|
-
children: (TextNode | LinkNode | ListNode)[]
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface QuoteNode {
|
|
43
|
-
type: 'quote'
|
|
44
|
-
children: (TextNode | LinkNode)[]
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface ImageNode {
|
|
48
|
-
type: 'image'
|
|
49
|
-
image: StrapiFileFragment
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export type BlockNode = HeadingNode | ParagraphNode | TextNode | LinkNode | ListNode | ListItemNode | QuoteNode | ImageNode
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import type * as RichText from '~/types/StrapiRichText'
|
|
2
|
-
|
|
3
|
-
import { Comment, Fragment } from 'vue'
|
|
4
|
-
|
|
5
|
-
function renderParagraphBlock(block: RichText.ParagraphNode, inline?: boolean) {
|
|
6
|
-
const children = renderStrapiRichText(block.children, inline)
|
|
7
|
-
const tag = inline ? 'fragment' : 'p'
|
|
8
|
-
return children.length ? h(tag, children) : h('br')
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function renderTextBlock(block: RichText.TextNode) {
|
|
12
|
-
const lines = block.text.split('\n')
|
|
13
|
-
const children = lines.reduce((acc, line, i) => {
|
|
14
|
-
if (i < lines.length - 1) {
|
|
15
|
-
acc.push(`${line} `.replace(/\s+/g, ' '), h('br'))
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
acc.push(line)
|
|
19
|
-
}
|
|
20
|
-
return acc
|
|
21
|
-
}, [] as (string | VNode)[])
|
|
22
|
-
|
|
23
|
-
if (block.bold)
|
|
24
|
-
return h('strong', children)
|
|
25
|
-
if (block.code)
|
|
26
|
-
return h('code', children)
|
|
27
|
-
if (block.italic)
|
|
28
|
-
return h('em', children)
|
|
29
|
-
if (block.strikethrough)
|
|
30
|
-
return h('span', { class: 'line-through' }, children)
|
|
31
|
-
if (block.underline)
|
|
32
|
-
return h('span', { class: 'underline' }, children)
|
|
33
|
-
return h(Fragment, children)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function renderInlineBlock(block: RichText.BlockNode): VNode {
|
|
37
|
-
switch (block.type) {
|
|
38
|
-
case 'heading':
|
|
39
|
-
case 'list-item':
|
|
40
|
-
case 'quote':
|
|
41
|
-
return h('span', renderStrapiRichText(block.children, true))
|
|
42
|
-
case 'image':
|
|
43
|
-
return h(Fragment)
|
|
44
|
-
case 'link':
|
|
45
|
-
return h('a', { href: block.url, target: '_blank', rel: 'noopener noreferrer' }, renderStrapiRichText(block.children, true))
|
|
46
|
-
case 'list':
|
|
47
|
-
return h(Fragment, renderStrapiRichText(block.children, true))
|
|
48
|
-
case 'paragraph':
|
|
49
|
-
return renderParagraphBlock(block, true)
|
|
50
|
-
case 'text':
|
|
51
|
-
return renderTextBlock(block)
|
|
52
|
-
default:
|
|
53
|
-
return h(Comment, 'Unknown block type')
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function renderBlock(block: RichText.BlockNode): VNode {
|
|
58
|
-
switch (block.type) {
|
|
59
|
-
case 'heading':
|
|
60
|
-
return h(`h${block.level}`, renderStrapiRichText(block.children))
|
|
61
|
-
case 'image':
|
|
62
|
-
return h('img', { src: block.image.url, width: block.image.width, height: block.image.height, alt: block.image.alternativeText })
|
|
63
|
-
case 'link':
|
|
64
|
-
return h('a', { title: getPlainText(block.children), href: block.url, target: '_blank', rel: 'noopener noreferrer' }, h('span', renderStrapiRichText(block.children)))
|
|
65
|
-
case 'list':
|
|
66
|
-
return h(block.format === 'ordered' ? 'ol' : 'ul', renderStrapiRichText(block.children))
|
|
67
|
-
case 'list-item':
|
|
68
|
-
return h('li', renderStrapiRichText(block.children))
|
|
69
|
-
case 'paragraph':
|
|
70
|
-
return renderParagraphBlock(block)
|
|
71
|
-
case 'quote':
|
|
72
|
-
return h('blockquote', renderStrapiRichText(block.children))
|
|
73
|
-
case 'text':
|
|
74
|
-
return renderTextBlock(block)
|
|
75
|
-
default:
|
|
76
|
-
return h(Comment, 'Unknown block type')
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function renderStrapiRichText(blocks: RichText.BlockNode[], inline?: boolean): VNode[] {
|
|
81
|
-
const render = inline ? renderInlineBlock : renderBlock
|
|
82
|
-
return blocks.map(render)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function getPlainText(block: RichText.BlockNode[]) {
|
|
86
|
-
if (!block)
|
|
87
|
-
return ''
|
|
88
|
-
|
|
89
|
-
const text: string = block.reduce((acc, node) => {
|
|
90
|
-
if (node.type === 'text') {
|
|
91
|
-
return acc + node.text
|
|
92
|
-
}
|
|
93
|
-
return acc + getPlainText(node.children)
|
|
94
|
-
}, '')
|
|
95
|
-
|
|
96
|
-
return text
|
|
97
|
-
}
|