Refactor blocks architecture to modular approach
- Restore "1/2, 1/2" layout for flexible column combinations - Simplify beforeafter block: remove toggle and text field, keep only image comparison - Create new video block with URL support (YouTube/Vimeo/direct files) - Create horizontal-gallery block for scrollable image galleries - Add H4 heading level support - All blocks now modular: combine with text blocks in 2-column layouts Blocks available: - Text, Heading (h2-h4), Image, Video - Before/After comparison (no text) - Horizontal gallery (with text below) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
6251d8f09f
commit
561932724b
23 changed files with 539 additions and 252 deletions
2
site/plugins/video/.gitignore
vendored
Normal file
2
site/plugins/video/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
node_modules/
|
||||
*.log
|
||||
11
site/plugins/video/blueprints/blocks/video.yml
Normal file
11
site/plugins/video/blueprints/blocks/video.yml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
name: Vidéo
|
||||
icon: video
|
||||
preview: video
|
||||
fields:
|
||||
url:
|
||||
label: URL de la vidéo
|
||||
type: url
|
||||
help: URL YouTube, Vimeo ou lien direct vers un fichier vidéo
|
||||
caption:
|
||||
label: Légende
|
||||
type: text
|
||||
10
site/plugins/video/index.php
Normal file
10
site/plugins/video/index.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
Kirby::plugin('index/video', [
|
||||
'blueprints' => [
|
||||
'blocks/video' => __DIR__ . '/blueprints/blocks/video.yml'
|
||||
],
|
||||
'snippets' => [
|
||||
'blocks/video' => __DIR__ . '/snippets/blocks/video.php'
|
||||
]
|
||||
]);
|
||||
12
site/plugins/video/package.json
Normal file
12
site/plugins/video/package.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "video-block",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "npx -y kirbyup src/index.js --watch",
|
||||
"build": "npx -y kirbyup src/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"vite": "^7.1.7"
|
||||
}
|
||||
}
|
||||
39
site/plugins/video/snippets/blocks/video.php
Normal file
39
site/plugins/video/snippets/blocks/video.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
/** @var \Kirby\Cms\Block $block */
|
||||
$url = $block->url()->value();
|
||||
$caption = $block->caption()->value();
|
||||
|
||||
// Fonction pour détecter le type de vidéo
|
||||
function getVideoEmbedCode($url) {
|
||||
// YouTube
|
||||
if (preg_match('/youtube\.com\/watch\?v=([^&]+)/', $url, $matches) ||
|
||||
preg_match('/youtu\.be\/([^?]+)/', $url, $matches)) {
|
||||
$videoId = $matches[1];
|
||||
return '<iframe src="https://www.youtube.com/embed/' . $videoId . '" frameborder="0" allowfullscreen></iframe>';
|
||||
}
|
||||
|
||||
// Vimeo
|
||||
if (preg_match('/vimeo\.com\/(\d+)/', $url, $matches)) {
|
||||
$videoId = $matches[1];
|
||||
return '<iframe src="https://player.vimeo.com/video/' . $videoId . '" frameborder="0" allowfullscreen></iframe>';
|
||||
}
|
||||
|
||||
// Vidéo directe (mp4, webm, etc.)
|
||||
if (preg_match('/\.(mp4|webm|ogg)$/i', $url)) {
|
||||
return '<video controls><source src="' . $url . '" type="video/' . pathinfo($url, PATHINFO_EXTENSION) . '"></video>';
|
||||
}
|
||||
|
||||
// Par défaut, iframe
|
||||
return '<iframe src="' . $url . '" frameborder="0" allowfullscreen></iframe>';
|
||||
}
|
||||
?>
|
||||
|
||||
<?php if ($url): ?>
|
||||
<div class="container-video">
|
||||
<?= getVideoEmbedCode($url) ?>
|
||||
</div>
|
||||
|
||||
<?php if ($caption): ?>
|
||||
<p class="caption"><?= $caption ?></p>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
95
site/plugins/video/src/components/VideoBlock.vue
Normal file
95
site/plugins/video/src/components/VideoBlock.vue
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<template>
|
||||
<div @click="$emit('open')" class="video-preview">
|
||||
<div v-if="content.url" class="video-preview__container">
|
||||
<div class="video-preview__placeholder">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M8 5v14l11-7z"/>
|
||||
</svg>
|
||||
<p class="video-preview__url">{{ truncatedUrl }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p v-if="content.caption" class="video-preview__caption">
|
||||
{{ content.caption }}
|
||||
</p>
|
||||
|
||||
<div v-if="!content.url" class="video-preview__empty">
|
||||
Cliquer pour ajouter une URL de vidéo
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
content: Object
|
||||
});
|
||||
|
||||
const truncatedUrl = computed(() => {
|
||||
if (!props.content?.url) return '';
|
||||
const url = props.content.url;
|
||||
return url.length > 50 ? url.substring(0, 50) + '...' : url;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.video-preview {
|
||||
cursor: pointer;
|
||||
border-radius: var(--rounded);
|
||||
overflow: hidden;
|
||||
background: var(--color-background);
|
||||
border: 1px solid var(--color-gray-300);
|
||||
}
|
||||
|
||||
.video-preview__container {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
background: var(--color-gray-900);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.video-preview__placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
color: var(--color-gray-400);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.video-preview__placeholder svg {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.video-preview__url {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--color-gray-500);
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.video-preview__caption {
|
||||
padding: 0.75rem;
|
||||
font-size: var(--text-sm);
|
||||
color: var(--color-gray-600);
|
||||
font-style: italic;
|
||||
background: var(--color-background);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.video-preview__empty {
|
||||
padding: 3rem 1rem;
|
||||
text-align: center;
|
||||
color: var(--color-gray-500);
|
||||
font-size: var(--text-sm);
|
||||
background: var(--color-gray-100);
|
||||
}
|
||||
|
||||
.video-preview:hover {
|
||||
border-color: var(--color-gray-400);
|
||||
}
|
||||
</style>
|
||||
7
site/plugins/video/src/index.js
Normal file
7
site/plugins/video/src/index.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import VideoBlock from "./components/VideoBlock.vue";
|
||||
|
||||
window.panel.plugin("index/video", {
|
||||
blocks: {
|
||||
video: VideoBlock
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue