This commit is contained in:
2026-02-16 07:59:52 +01:00
parent 1fc267faa8
commit b8b8e53f07
70 changed files with 3088 additions and 272 deletions

View File

@@ -0,0 +1,91 @@
<template>
<component v-if="isText && hasTextValue && wrapTag" :is="wrapTag">
{{ textValue }}
</component>
<template v-else-if="isText && hasTextValue">
{{ textValue }}
</template>
<component v-else-if="isList" :is="listTag">
<StrapiBlockChildsConvert
v-for="(child, i) in children"
:key="i"
:node="child"
/>
</component>
<li v-else-if="isListItem">
<StrapiBlockChildsConvert
v-for="(child, i) in children"
:key="i"
:node="child"
/>
</li>
<a
v-else-if="isLink"
:href="href"
class="strapi-inline__link"
rel="noopener noreferrer"
target="_blank"
>
<StrapiBlockChildsConvert
v-for="(child, i) in children"
:key="i"
:node="child"
/>
</a>
<template v-else>
<StrapiBlockChildsConvert
v-for="(child, i) in children"
:key="i"
:node="child"
/>
</template>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
node: { type: Object, required: true },
})
const isText = computed(() => props.node?.type === 'text')
const isLink = computed(() => props.node?.type === 'link')
const isList = computed(() => props.node?.type === 'list')
const isListItem = computed(() => props.node?.type === 'list-item')
const children = computed(() => props.node?.children || [])
const listTag = computed(() => (
props.node?.format === 'ordered' ? 'ol' : 'ul'
))
const textValue = computed(() => (props.node?.text ?? '').toString())
const hasTextValue = computed(() => textValue.value.length > 0)
// Dans Strapi blocks, les liens peuvent être "url" ou "href" selon les versions/plugins
const href = computed(() => props.node?.url || props.node?.href || '#')
/**
* Gestion des "marks" (bold/italic/underline/...)
* Ici on choisit une stratégie simple : UN seul wrapper.
* -> si tu veux combiner plusieurs marks (bold + italic), on le fait après.
*/
const wrapTag = computed(() => {
const n = props.node || {}
if (!isText.value) return null
if (n.code) return 'code'
if (n.bold) return 'strong'
if (n.italic) return 'em'
if (n.underline) return 'u'
if (n.strikethrough) return 's'
return null
})
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,222 @@
<template>
<div class="strapi-blocks">
<template v-for="(block, i) in blocks" :key="i">
<!-- Paragraph -->
<p v-if="block.type === 'paragraph'" class="strapi-blocks--p">
<template v-for="(child, j) in (block.children)" :key="j">
<StrapiBlockChildsConvert :node="child" />
</template>
</p>
<!-- Heading -->
<component
v-else-if="block.type === 'heading'"
:is="`h${block.level}`"
class="strapi-blocks__h"
>
<template v-for="(child, j) in (block.children)" :key="j">
<StrapiBlockChildsConvert :node="child" />
</template>
</component>
<!-- Quote -->
<blockquote
v-else-if="block.type === 'quote'"
>
<template v-for="(child, j) in (block.children)" :key="j">
<StrapiBlockChildsConvert :node="child" />
</template>
</blockquote>
<!-- Lists -->
<ul
v-else-if="block.type === 'list' && block.format === 'unordered'"
>
<li
v-for="(item, j) in normalizeListItems(block.children)"
:key="j"
>
<template v-for="(child, k) in (item.children)" :key="k">
<StrapiBlockChildsConvert :node="child" />
</template>
</li>
</ul>
<ol
v-else-if="block.type === 'list' && block.format === 'ordered'"
>
<li
v-for="(item, j) in normalizeListItems(block.children)"
:key="j"
>
<template v-for="(child, k) in (item.children)" :key="k">
<StrapiBlockChildsConvert :node="child" />
</template>
</li>
</ol>
<!-- Fallback -->
<div v-else class="strapi-blocks--unknown">
<!-- debug éventuel -->
</div>
</template>
</div>
</template>
<script setup>
import StrapiBlockChildsConvert from './StrapiBlockChildsConvert.vue'
const props = defineProps({
blocks: { type: Array, default: () => [] },
})
const normalizeListItems = (children = []) => {
const normalized = []
for (const child of children) {
if (!child || typeof child !== 'object') continue
if (child.type === 'list-item') {
normalized.push({
...child,
children: Array.isArray(child.children) ? [...child.children] : [],
})
continue
}
// Certains contenus Strapi placent une sous-liste comme sibling d'un list-item.
// On la rattache au dernier list-item pour produire un HTML imbriqué valide.
if (child.type === 'list' && normalized.length > 0) {
normalized[normalized.length - 1].children.push(child)
}
}
return normalized
}
</script>
<style lang="scss">
.strapi-blocks {
font-family: var(--font-roboto);
&--p {
font-weight: var(--fw-light);
font-size: 17px;
line-height: 22px;
margin-bottom: 5px;
}
h1 {
padding-bottom: 10px;
font-weight: var(--fw-bold);
font-size: 30px;
}
h2 {
padding-bottom: 7px;
font-weight: var(--fw-bold);
font-size: 27px;
color: var(--c-brand_rouge);
}
h3 {
padding-bottom: 5px;
font-weight: var(--fw-semibold);
font-size: 26px;
}
h4 {
padding-bottom: 3px;
font-weight: var(--fw-medium);
font-size: 22px;
}
h4 {
padding-bottom: 3px;
font-weight: var(--fw-medium);
font-size: 22px;
}
h4 {
padding-bottom: 3px;
font-weight: var(--fw-medium);
font-size: 22px;
}
a {
text-decoration: underline;
color: var(--c-brand_rouge-weak);
text-decoration-thickness: from-font;
}
ul,
ol {
list-style: none;
margin: 0 0 5px;
padding-left: 0;
}
li {
position: relative;
margin: 0;
padding-left: 23px;
margin-bottom: 5px;
}
ul > li::before {
content: "•";
position: absolute;
left: 0;
top: -7px;
width: 0.5rem;
height: 0.5rem;
/*background-image: var(--strapi-li-icon);
background-repeat: no-repeat;
background-size: contain;
background-position: center;*/
font-size: 39px;
line-height: 1;
/* color: var(--c-brand_rouge); /* couleur */
}
ol {
counter-reset: item;
}
ol > li {
counter-increment: item;
}
ol > li::before {
content: counter(item) ". ";
}
li > ul,
li > ol {
margin-top: 4px;
padding-left: 1rem;
}
li > ul > li::before {
/* background-image: var(--strapi-li-icon-nested); */
content: "◦";
top: -6px;
font-size: 30px;
line-height: 1;
color: var(--c-text); /* couleur */
}
blockquote {
border-left: 11px var(--c-text-muted) solid;
padding-left: 20px;
margin-left: 2%;
margin-bottom: 30px;
margin-top: 30px;
}
// Espace uniquement avant un titre s'il suit un bloc de contenu
&--p + &__h,
&--ul + &__h,
&--ol + &__h,
&--quote + &__h {
margin-top: 13px;
}
}
</style>