FEATURE ibntroducing the story timeline blocks

This commit is contained in:
Antoine M 2025-12-04 16:57:09 +01:00
parent 61503f2753
commit d99a296280
15 changed files with 333 additions and 0 deletions

View File

@ -0,0 +1,28 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "carhop-blocks/story-timeline-step",
"version": "0.1.0",
"title": "Étape de timeline",
"category": "carhop-blocks",
"icon": "calendar",
"description": "Étape de timeline pour la mise en forme d'une étape de timeline",
"example": {},
"parent": [
"carhop-blocks/story-timeline"
],
"supports": {
"html": false
},
"textdomain": "carhop-blocks",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js",
"attributes": {
"year": {
"type": "number",
"default": 2025
}
}
}

View File

@ -0,0 +1,77 @@
import { __ } from "@wordpress/i18n";
import { useBlockProps, InnerBlocks } from "@wordpress/block-editor";
import "./editor.scss";
import {
PanelBody,
Card,
CardBody,
CardHeader,
TextControl,
Button,
__experimentalNumberControl as NumberControl,
} from "@wordpress/components";
import { InspectorControls } from "@wordpress/block-editor";
import { RichText } from "@wordpress/block-editor";
export default function Edit({ attributes, setAttributes, ...props }) {
const { year } = attributes;
return (
<>
<InspectorControls>
<PanelBody title={__("Étape de timeline", "carhop-blocks")}>
<NumberControl
label={__("Année", "carhop-blocks")}
value={year}
onChange={(value) => {
const n = parseInt(value, 10);
setAttributes({ year: Number.isFinite(n) ? n : undefined });
}}
/>
</PanelBody>
</InspectorControls>
<div
{...useBlockProps({
className: `story-timeline-step`,
id: `year-${year}`,
})}
>
<p className="story-timeline-step__year">{year}</p>
<div className="story-timeline-step__innerblocks">
<InnerBlocks
allowedBlocks={[
"core/heading",
"core/paragraph",
"core/group",
"core/list",
"core/button",
"core/image",
"core/buttons",
"core/columns",
"core/post-title",
"core/embed",
"core/quote",
"core/pullquote",
"core/media-text",
"core/table",
"carhop-blocks/image-stack",
"carhop-blocks/heading",
"carhop-blocks/decorative-shapes",
"carhop-blocks/scroll-story-block",
"carhop-blocks/cta-group",
"carhop-blocks/audio-player",
"carhop-blocks/localisation-map",
"carhop-blocks/notice-panel",
"acf/statistics-datas",
"ninja-forms/form",
"gravityforms/form",
"dynamiques-blocks/sitemap",
"mailpoet/subscription-form-block",
"shortcode",
]}
/>
</div>
</div>
</>
);
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,24 @@
import { registerBlockType } from "@wordpress/blocks";
import "./style.scss";
import Edit from "./edit";
import save from "./save";
import metadata from "./block.json";
registerBlockType(metadata.name, {
icon: {
foreground: "#136f63",
src: (
<svg width="100" height="100" viewBox="0 0 100 100">
<g stroke="null" id="svg_10" class="fills">
<path
stroke="null"
d="m3.74998,19.74151c0,-8.82607 7.16546,-15.99153 15.99153,-15.99153l60.51698,0c8.82607,0 15.99153,7.16546 15.99153,15.99153l0,60.51698c0,8.82607 -7.16546,15.99153 -15.99153,15.99153l-60.51698,0c-8.82607,0 -15.99153,-7.16546 -15.99153,-15.99153l0,-60.51698zm22.57628,3.44915l0,53.61867l23.67374,-18.7148l23.67374,18.7148l0,-53.61867l-47.34748,0z"
/>
</g>
</svg>
),
},
edit: Edit,
save,
});

View File

@ -0,0 +1,18 @@
import { useBlockProps, InnerBlocks } from "@wordpress/block-editor";
export default function save({ attributes }) {
const { year } = attributes;
return (
<div
{...useBlockProps.save({
className: `story-timeline-step`,
id: `year-${year}`,
})}
>
<p className="story-timeline-step__year">{year}</p>
<div className="story-timeline-step__innerblocks">
<InnerBlocks.Content />
</div>
</div>
);
}

View File

@ -0,0 +1,20 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "carhop-blocks/story-timeline",
"version": "0.1.0",
"title": "Timeline",
"category": "carhop-blocks",
"icon": "calendar-alt",
"description": "Boite de timeline pour la mise en forme d'une timeline",
"example": {},
"supports": {
"html": false
},
"textdomain": "carhop-blocks",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js",
"render": "file:./render.php"
}

View File

@ -0,0 +1,40 @@
import { __ } from "@wordpress/i18n";
import { useBlockProps, InnerBlocks } from "@wordpress/block-editor";
import { useSelect } from "@wordpress/data";
import "./editor.scss";
export default function Edit({ attributes, setAttributes, ...props }) {
const years = useSelect(
(select) => {
const { getBlocks } = select("core/block-editor");
const childBlocks = getBlocks(props.clientId) || [];
return childBlocks
.filter((b) => b.name === "carhop-blocks/story-timeline-step")
.map((b) => b.attributes?.year)
.filter((y) => y !== undefined && y !== null && y !== "");
},
[props.clientId]
);
return (
<>
<section
{...useBlockProps({
className: `story-timeline`,
})}
>
<aside className="story-timeline__years">
<ul>
{years.map((y, idx) => (
<li key={idx} className="story-timeline__year">
{y}
</li>
))}
</ul>
</aside>
<div className="story-timeline__innerblocks">
<InnerBlocks allowedBlocks={["carhop-blocks/story-timeline-step"]} />
</div>
</section>
</>
);
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,24 @@
import { registerBlockType } from "@wordpress/blocks";
import "./style.scss";
import Edit from "./edit";
import save from "./save";
import metadata from "./block.json";
registerBlockType(metadata.name, {
icon: {
foreground: "#136f63",
src: (
<svg width="100" height="100" viewBox="0 0 100 100">
<g stroke="null" id="svg_10" class="fills">
<path
stroke="null"
d="m3.74998,19.74151c0,-8.82607 7.16546,-15.99153 15.99153,-15.99153l60.51698,0c8.82607,0 15.99153,7.16546 15.99153,15.99153l0,60.51698c0,8.82607 -7.16546,15.99153 -15.99153,15.99153l-60.51698,0c-8.82607,0 -15.99153,-7.16546 -15.99153,-15.99153l0,-60.51698zm22.57628,3.44915l0,53.61867l23.67374,-18.7148l23.67374,18.7148l0,-53.61867l-47.34748,0z"
/>
</g>
</svg>
),
},
edit: Edit,
save,
});

View File

@ -0,0 +1,28 @@
<?php
$wrapper_attributes = get_block_wrapper_attributes(array('class' => 'story-timeline'));
$years = array();
if (isset($block) && is_object($block) && ! empty($block->inner_blocks)) {
foreach ($block->inner_blocks as $inner_block) {
if (isset($inner_block->name) && $inner_block->name === 'carhop-blocks/story-timeline-step') {
$y = isset($inner_block->attributes['year']) ? $inner_block->attributes['year'] : null;
if ($y !== null && $y !== '') {
$years[] = $y;
}
}
}
}
?>
<section <?php echo $wrapper_attributes; ?>>
<aside class="story-timeline__years">
<ul>
<?php foreach ($years as $y) : ?>
<li class="story-timeline__year"><a href="#year-<?php echo esc_attr($y); ?>"><?php echo esc_html($y); ?></a></li>
<?php endforeach; ?>
</ul>
</aside>
<div class="story-timeline__innerblocks" id="<?php echo esc_attr($years[0]); ?>">
<?php echo $content; ?>
</div>
</section>

View File

@ -0,0 +1,5 @@
import { useBlockProps, InnerBlocks } from "@wordpress/block-editor";
export default function save({ attributes }) {
return <InnerBlocks.Content />;
}

View File

@ -0,0 +1,67 @@
document.addEventListener("DOMContentLoaded", function () {
const timeline = document.querySelector(
".wp-block-carhop-blocks-story-timeline"
);
if (!timeline) return;
function initBlock() {
const years = timeline.querySelectorAll(".story-timeline__year");
years.forEach((year, index) => {
year.setAttribute("data-active", index === 0 ? "true" : "false");
});
}
function removePreviousActiveLinkInSidebar() {
const activeSidebarLinks = document.querySelectorAll(
`.story-timeline__years .story-timeline__year[data-active="true"]`
);
activeSidebarLinks.forEach((sidebarLink) => {
sidebarLink.setAttribute("data-active", "false");
});
}
const timelineStepsProgressionObserver = new IntersectionObserver(
(entries) => {
// Ne pas traiter les entrées si l'observer est en pause (pendant un clic)
// const isIntersetionObserverPaused = getChapterObserverPausedState();
// if (isIntersetionObserverPaused) return;
entries.forEach((entry) => {
const blockId = entry.target.getAttribute("id");
const relatedStepLink = document.querySelector(`a[href="#${blockId}"]`);
console.log(relatedStepLink);
if (entry.isIntersecting) {
removePreviousActiveLinkInSidebar();
// setActiveLinkInSidebar();
entry.target.setAttribute("active", "true");
relatedStepLink?.parentElement?.setAttribute("data-active", "true");
}
});
},
{
rootMargin: "-10% 0px -50% 0px",
}
);
const timelineSteps = document.querySelectorAll(
".story-timeline__innerblocks .wp-block-carhop-blocks-story-timeline-step"
);
console.log(timelineSteps);
timelineSteps.forEach((step) => {
timelineStepsProgressionObserver.observe(step);
});
initBlock();
});
// Initialiser les écouteurs de liens
// observeChapterLinks();
// // Observer tous les titres h2 de l'article
// const titlesBlocks = document.querySelectorAll('.article-content h2');
// titlesBlocks.forEach((block) => {
// chapterProgressionObserver.observe(block);
// });