generated from gitea_admin/default
322 lines
7.1 KiB
Vue
322 lines
7.1 KiB
Vue
<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>
|
|
|
|
<!-- Code -->
|
|
<pre
|
|
v-else-if="block.type === 'code'"
|
|
class="strapi-blocks__code"
|
|
><code :class="getCodeLanguageClass(block.language)">{{ getCodeContent(block.children) }}</code></pre>
|
|
|
|
<!-- Image -->
|
|
<figure
|
|
v-else-if="block.type === 'image' && block.image?.url"
|
|
class="strapi-blocks__figure"
|
|
>
|
|
<img
|
|
:src="block.image.url"
|
|
:alt="getImageAlt(block.image)"
|
|
:width="block.image.width || undefined"
|
|
:height="block.image.height || undefined"
|
|
class="strapi-blocks__image"
|
|
>
|
|
<figcaption
|
|
v-if="block.image.caption"
|
|
class="strapi-blocks__caption"
|
|
>
|
|
{{ block.image.caption }}
|
|
</figcaption>
|
|
</figure>
|
|
|
|
<!-- 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
|
|
}
|
|
|
|
const getImageAlt = (image = {}) => (
|
|
image.alternativeText
|
|
|| image.caption
|
|
|| image.name
|
|
|| ''
|
|
)
|
|
|
|
const getCodeContent = (children = []) => (
|
|
Array.isArray(children)
|
|
? children
|
|
.filter((child) => child?.type === 'text')
|
|
.map((child) => child.text ?? '')
|
|
.join('')
|
|
: ''
|
|
)
|
|
|
|
const getCodeLanguageClass = (language) => (
|
|
language ? `language-${language}` : undefined
|
|
)
|
|
</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;
|
|
text-align: justify;
|
|
}
|
|
h1 {
|
|
padding-bottom: 10px;
|
|
font-weight: var(--fw-bold);
|
|
font-size: 30px;
|
|
text-transform: uppercase;
|
|
}
|
|
h2 {
|
|
padding-bottom: 7px;
|
|
font-weight: var(--fw-bold);
|
|
font-size: 27px;
|
|
color: var(--c-brand_rouge);
|
|
}
|
|
h3 {
|
|
padding-bottom: 10px;
|
|
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;
|
|
}
|
|
|
|
em {
|
|
font-style: var(--fi-hight);
|
|
}
|
|
|
|
strong {
|
|
font-weight: var(--fw-semibold);
|
|
}
|
|
|
|
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-top: 5px;
|
|
padding-left: 0;
|
|
}
|
|
|
|
li {
|
|
position: relative;
|
|
margin: 0;
|
|
padding-left: 23px;
|
|
margin-bottom: 10px;
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
&__figure {
|
|
margin: 30px 0 30px;
|
|
align-self: center;
|
|
}
|
|
|
|
&__image {
|
|
display: block;
|
|
max-width: 100%;
|
|
height: auto;
|
|
max-height: 430px;
|
|
object-fit: contain;
|
|
border-radius: 6px;
|
|
|
|
}
|
|
|
|
&__caption {
|
|
margin-top: 8px;
|
|
font-size: 14px;
|
|
line-height: 18px;
|
|
color: var(--c-text-muted);
|
|
}
|
|
|
|
&__code {
|
|
overflow-x: auto;
|
|
margin: 20px 0 25px;
|
|
padding: 14px 16px;
|
|
background: var(--c-bg-muted, #f3f3f3);
|
|
border-radius: 4px;
|
|
font-family: monospace;
|
|
font-size: 15px;
|
|
line-height: 1.5;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
// Espace uniquement avant un titre s'il suit un bloc de contenu
|
|
&--p + &__h,
|
|
ul + &__h,
|
|
ol + &__h,
|
|
blockquote + &__h,
|
|
&__code + &__h,
|
|
&__figure + &__h {
|
|
margin-top: 23px;
|
|
}
|
|
> :not(h1) {
|
|
margin-left: 20px;
|
|
}
|
|
|
|
}
|
|
</style>
|