FEATURE Introducing the component

This commit is contained in:
Antoine M 2026-03-20 17:08:48 +01:00
parent 5a7cfe63da
commit a62f00f31e
6 changed files with 466 additions and 0 deletions

View File

@ -0,0 +1,54 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "carhop-blocks/document-card",
"version": "0.1.0",
"title": "Document Card",
"category": "carhop-blocks",
"icon": "smiley",
"description": "Document Card pour la mise en forme supérieure d'éléments de contenu",
"example": {},
"supports": {
"html": false,
"color": {
"text": true,
"background": false,
"link": false
}
},
"textdomain": "document-card",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js",
"attributes": {
"isInitilized": {
"type": "boolean",
"default": false
},
"documentType": {
"type": "string",
"default": "document",
"enum": [
"internal",
"external"
]
},
"documentFileSize": {
"type": "number",
"default": 0
},
"documentUrl": {
"type": "string",
"default": ""
},
"documentId": {
"type": "number",
"default": 0
},
"UserhasValidatedExternalUrl": {
"type": "boolean",
"default": false
}
}
}

View File

@ -0,0 +1,342 @@
import { __ } from "@wordpress/i18n";
import {
useBlockProps,
InnerBlocks,
InspectorControls,
MediaUpload,
MediaUploadCheck,
BlockControls,
MediaReplaceFlow,
} from "@wordpress/block-editor";
import { __experimentalHStack as HStack } from "@wordpress/components";
import {
PanelBody,
TextControl,
Button,
Placeholder,
ToolbarButton,
Icon,
__experimentalToggleGroupControl as ToggleGroupControl,
__experimentalToggleGroupControlOption as ToggleGroupControlOption,
} from "@wordpress/components";
import { file, link, external } from "@wordpress/icons";
import { MediaPlaceholder } from "@wordpress/block-editor";
import "./editor.scss";
export default function Edit({ attributes, setAttributes }) {
const {
isInitilized,
documentType,
documentUrl,
documentId,
UserhasValidatedExternalUrl,
documentFileSize,
} = attributes;
const blockProps = useBlockProps({
className: "document-card",
});
const hasDocument =
(documentType === "internal" && documentId && documentUrl) ||
(documentType === "external" && documentUrl && UserhasValidatedExternalUrl);
const resetDocument = () => {
setAttributes({
documentId: 0,
documentUrl: "",
documentFileSize: 0,
isInitilized: false,
UserhasValidatedExternalUrl: false,
});
};
const setInternalDocument = (media) => {
const fileSize = media.filesizeInBytes || 0;
const fileSizeInKB = Math.round(fileSize / 1024);
setAttributes({
documentType: "internal",
documentFileSize: fileSizeInKB,
documentId: media.id,
documentUrl: media.url,
isInitilized: true,
});
};
const setExternalDocument = (url) => {
setAttributes({
documentType: "external",
documentFileSize: 0,
documentId: 0,
documentUrl: url,
isInitilized: true,
});
};
return (
<>
<InspectorControls>
<PanelBody title={__("Type de document", "carhop-blocks")}>
<ToggleGroupControl
label={__("Document relié", "carhop-blocks")}
value={documentType}
onChange={(value) => {
setAttributes({ documentType: value, isInitilized: true });
resetDocument();
}}
>
<ToggleGroupControlOption
label={__("PDF interne", "carhop-blocks")}
value="internal"
/>
<ToggleGroupControlOption
label={__("Lien externe", "carhop-blocks")}
value="external"
/>
</ToggleGroupControl>
{documentType === "external" && (
<>
<TextControl
label={__("URL du document", "carhop-blocks")}
value={documentUrl}
onChange={(value) => setExternalDocument(value)}
placeholder={__(
"https://example.com/document.pdf",
"carhop-blocks",
)}
/>
</>
)}
{documentType === "internal" && (
<MediaUploadCheck>
<MediaUpload
onSelect={(media) => {
setInternalDocument(media);
}}
allowedTypes={["application/pdf"]}
value={documentId}
render={({ open }) => (
<>
{documentUrl ? (
<div className="document-card__media-preview">
<span
style={{
width: 48,
height: 48,
display: "flex",
alignItems: "center",
}}
>
<Icon icon={file} />
</span>
<span className="document-card__media-filename">
{documentUrl.split("/").pop()?.split("?")[0] ||
__("Document", "carhop-blocks")}
</span>
<div style={{ marginTop: 8 }}>
<Button
variant="secondary"
onClick={open}
style={{ marginRight: 8 }}
>
{__("Remplacer", "carhop-blocks")}
</Button>
<Button
variant="tertiary"
isDestructive
onClick={() => resetDocument()}
>
{__("Supprimer", "carhop-blocks")}
</Button>
</div>
</div>
) : (
<Button variant="secondary" onClick={open}>
{__("Choisir un document", "carhop-blocks")}
</Button>
)}
</>
)}
/>
</MediaUploadCheck>
)}
</PanelBody>
</InspectorControls>
<div {...blockProps}>
{!isInitilized && (
<Placeholder
icon={file}
label={__("Document Card", "carhop-blocks")}
instructions={__(
"Choisissez le type de document que vous souhaitez afficher.",
"carhop-blocks",
)}
className="document-card__initialization"
>
<HStack spacing={3} alignment="stretch" expanded={false}>
<Button
variant="primary"
icon={file}
onClick={() =>
setAttributes({
isInitilized: true,
documentType: "internal",
})
}
>
{__("PDF interne", "carhop-blocks")}
</Button>
<Button
variant="secondary"
icon={link}
onClick={() =>
setAttributes({
isInitilized: true,
documentType: "external",
})
}
>
{__("Lien externe", "carhop-blocks")}
</Button>
</HStack>
</Placeholder>
)}
{isInitilized &&
documentType === "internal" &&
!documentId &&
!documentUrl && (
<MediaPlaceholder
icon="media-default"
labels={{
title: "Fichier",
instructions:
"Glissez-déposez, téléversez ou sélectionnez un fichier depuis votre médiathèque.",
}}
onSelect={(media) => setInternalDocument(media)}
accept="application/pdf"
allowedTypes={["application/pdf"]}
multiple={false}
/>
)}
{isInitilized &&
documentType === "external" &&
!UserhasValidatedExternalUrl && (
<Placeholder
icon={link}
label={__("Lien externe", "carhop-blocks")}
instructions={__(
"Entrez l'URL du document (PDF, etc.) ou utilisez le panneau de réglages à droite.",
"carhop-blocks",
)}
className="document-card__url-placeholder"
>
<TextControl
value={documentUrl}
onChange={(value) => setExternalDocument(value)}
placeholder="https://example.com/document.pdf"
style={{ minWidth: 320 }}
/>
<Button
variant="secondary"
onClick={() =>
setAttributes({ UserhasValidatedExternalUrl: true })
}
>
{__("Valider le lien", "carhop-blocks")}
</Button>
</Placeholder>
)}
{hasDocument && (
<>
<BlockControls group="other">
{documentType === "internal" ? (
<MediaReplaceFlow
mediaId={documentId}
mediaUrl={documentUrl}
allowedTypes={["application/pdf"]}
accept="application/pdf"
onSelect={(media) => setInternalDocument(media)}
name={__("Remplacer le document", "carhop-blocks")}
/>
) : null}
<ToolbarButton onClick={() => resetDocument()}>
{__("Supprimer le document", "carhop-blocks")}
</ToolbarButton>
</BlockControls>
<div className="document-card__preview">
<div className="document-card__content">
<InnerBlocks
allowedBlocks={[
"core/heading",
"core/paragraph",
"core/list",
"core/button",
"core/buttons",
"core/image",
"core/embed",
"core/quote",
"core/pullquote",
"core/media-text",
"core/table",
"core/group",
"core/columns",
"core/post-title",
"carhop-blocks/cta",
"carhop-blocks/heading",
"carhop-blocks/cta-group",
"carhop-blocks/audio-player",
"carhop-blocks/content-box",
"carhop-blocks/notice-panel",
"shortcode",
]}
template={[
[
"carhop-blocks/heading",
{},
[
[
"core/heading",
{
content: "Titre du document",
placeholder: "Saisir le titre",
},
],
[
"core/paragraph",
{
content: "Sous-titre du document",
placeholder: "Saisir le sous-titre",
},
],
],
],
]}
/>
<div className="file_info">
<div className="file_info__type">
{documentType === "internal" ? "PDF" : "Document externe"}
</div>
{documentFileSize > 0 && (
<div className="file_info__size">
( {documentFileSize} Ko )
</div>
)}
</div>
</div>
</div>
</>
)}
</div>
</>
);
}

View File

@ -0,0 +1,31 @@
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: {
src: (
<svg
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
context="list-view"
aria-hidden="true"
focusable="false"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#146E63"
d="M12.848 8a1 1 0 0 1-.914-.594l-.723-1.63a.5.5 0 0 0-.447-.276H5a.5.5 0 0 0-.5.5v11.5a.5.5 0 0 0 .5.5h14a.5.5 0 0 0 .5-.5v-9A.5.5 0 0 0 19 8h-6.152Zm.612-1.5a.5.5 0 0 1-.462-.31l-.445-1.084A2 2 0 0 0 10.763 4H5a2 2 0 0 0-2 2v11.5a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2h-5.54Z"
></path>
</svg>
),
},
edit: Edit,
save,
});

View File

@ -0,0 +1,39 @@
import { useBlockProps } from "@wordpress/block-editor";
import { InnerBlocks } from "@wordpress/block-editor";
export default function save({ attributes }) {
const { documentUrl, documentType, documentFileSize } = attributes;
const blockProps = useBlockProps.save({
className: "document-card",
});
if (!documentUrl) {
return (
<div {...blockProps}>
<InnerBlocks.Content />
</div>
);
}
return (
<a
{...blockProps}
href={documentUrl}
target="_blank"
rel="noopener noreferrer"
className={`${blockProps.className || ""} document-card--${documentType}`}
>
<div className="document-card__content">
<InnerBlocks.Content />
<div className="file_info">
<div className="file_info__type">
{documentType === "internal" ? "PDF" : "Document externe"}
</div>
{documentFileSize > 0 && (
<div className="file_info__size">( {documentFileSize} KB )</div>
)}
</div>
</div>
</a>
);
}