diff --git a/plugins/carhop-blocks/src/image-stack/block.json b/plugins/carhop-blocks/src/image-stack/block.json new file mode 100755 index 0000000..b5c19c2 --- /dev/null +++ b/plugins/carhop-blocks/src/image-stack/block.json @@ -0,0 +1,65 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "carhop-blocks/image-stack", + "version": "0.1.0", + "title": "Image Stack (Focal Point)", + "category": "carhop-blocks", + "icon": "images-alt2", + "description": "Layer multiple images with individual focal point positioning.", + "example": { + "attributes": { + "images": [ + { + "id": 1, + "url": "https://images.unsplash.com/photo-1506905925346-21bda4d32df4", + "alt": "Mountain landscape", + "focalPoint": { + "x": 0.5, + "y": 0.3 + }, + "scale": 0.8 + }, + { + "id": 2, + "url": "https://images.unsplash.com/photo-1511884642898-4c92249e20b6", + "alt": "Forest scene", + "focalPoint": { + "x": 0.7, + "y": 0.6 + }, + "scale": 0.6 + } + ], + "height": 400 + } + }, + "attributes": { + "images": { + "type": "array", + "default": [], + "items": { + "type": "object" + } + }, + "height": { + "type": "number", + "default": 400 + } + }, + "supports": { + "html": false, + "align": [ + "wide", + "full" + ], + "spacing": { + "margin": true, + "padding": true + } + }, + "textdomain": "image-stack", + "editorScript": "file:./index.js", + "editorStyle": "file:./index.css", + "style": "file:./style-index.css" +} \ No newline at end of file diff --git a/plugins/carhop-blocks/src/image-stack/edit.js b/plugins/carhop-blocks/src/image-stack/edit.js new file mode 100755 index 0000000..9418a95 --- /dev/null +++ b/plugins/carhop-blocks/src/image-stack/edit.js @@ -0,0 +1,243 @@ +import { __ } from "@wordpress/i18n"; +import { + useBlockProps, + InspectorControls, + MediaUpload, + MediaUploadCheck, +} from "@wordpress/block-editor"; +import { + PanelBody, + Button, + FocalPointPicker, + RangeControl, + ToolbarGroup, + ToolbarButton, +} from "@wordpress/components"; +import { BlockControls } from "@wordpress/block-editor"; +import { useState } from "@wordpress/element"; + +import "./editor.scss"; + +export default function Edit({ attributes, setAttributes }) { + const { images, height } = attributes; + const [selectedImageIndex, setSelectedImageIndex] = useState(null); + + const blockProps = useBlockProps(); + + const onSelectImages = (media) => { + const newImages = media.map((img) => ({ + id: img.id, + url: img.url, + alt: img.alt || "", + focalPoint: { x: 0.5, y: 0.5 }, + scale: 0.8, + rotation: 0, + })); + setAttributes({ images: [...images, ...newImages] }); + }; + + const updateImageFocalPoint = (index, focalPoint) => { + const newImages = [...images]; + newImages[index] = { ...newImages[index], focalPoint }; + setAttributes({ images: newImages }); + }; + + const updateImageScale = (index, scale) => { + const newImages = [...images]; + newImages[index] = { ...newImages[index], scale }; + setAttributes({ images: newImages }); + }; + + const updateImageRotation = (index, rotation) => { + const newImages = [...images]; + newImages[index] = { ...newImages[index], rotation }; + setAttributes({ images: newImages }); + }; + + const removeImage = (index) => { + const newImages = images.filter((_, i) => i !== index); + setAttributes({ images: newImages }); + if (selectedImageIndex === index) { + setSelectedImageIndex(null); + } + }; + + const moveImage = (index, direction) => { + const newImages = [...images]; + const newIndex = index + direction; + if (newIndex >= 0 && newIndex < images.length) { + [newImages[index], newImages[newIndex]] = [ + newImages[newIndex], + newImages[index], + ]; + setAttributes({ images: newImages }); + setSelectedImageIndex(newIndex); + } + }; + + const getImageStyle = (image) => { + const focalPoint = image.focalPoint || { x: 0.5, y: 0.5 }; + const scale = image.scale || 0.8; + const rotation = image.rotation || 0; + return { + left: `${focalPoint.x * 100}%`, + top: `${focalPoint.y * 100}%`, + transform: ` rotate(${rotation}deg) scale(${scale})`, + }; + }; + + return ( + <> + + + + img.id)} + render={({ open }) => ( + + {__("Add Images", "image-stack")} + + )} + /> + + + + + + + setAttributes({ height: value })} + min={200} + max={800} + step={10} + /> + + + {images.length > 0 && ( + + {images.map((image, index) => ( + + setSelectedImageIndex( + selectedImageIndex === index ? null : index + ) + } + > +
+ {image.alt} +
+ + + updateImageFocalPoint(index, focalPoint) + } + /> + + updateImageScale(index, scale)} + min={0.1} + max={3} + step={0.05} + /> + + updateImageRotation(index, rotation)} + min={-180} + max={180} + step={1} + /> + +
+ + + +
+
+ ))} +
+ )} +
+ +
+
+ {images.length === 0 && ( +
+ + ( + + )} + /> + +
+ )} + + {images.map((image, index) => ( +
setSelectedImageIndex(index)} + role="button" + tabIndex={0} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + setSelectedImageIndex(index); + } + }} + style={getImageStyle(image)} + > + {image.alt} +
+ ))} +
+
+ + ); +} diff --git a/plugins/carhop-blocks/src/image-stack/editor.scss b/plugins/carhop-blocks/src/image-stack/editor.scss new file mode 100755 index 0000000..544b762 --- /dev/null +++ b/plugins/carhop-blocks/src/image-stack/editor.scss @@ -0,0 +1,56 @@ +/** + * The following styles get applied inside the editor only. + */ + +.wp-block-carhop-blocks-image-stack { + overflow: visible; + .image-stack-placeholder { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + background-color: rgba(240, 240, 240, 0.5); + background-color: red; + border: 2px dashed #ccc; + } + + .image-stack-item { + cursor: pointer; + transition: opacity 0.2s ease; + + &:hover { + opacity: 0.9; + } + + &.is-selected { + outline: 3px solid #007cba; + outline-offset: -3px; + z-index: 10; + } + } +} + +.image-stack-image-preview { + margin-bottom: 16px; + + img { + width: 100%; + height: auto; + display: block; + border-radius: 4px; + } +} + +.image-stack-image-controls { + display: flex; + gap: 8px; + margin-top: 16px; + flex-wrap: wrap; + + .components-button { + flex: 1; + min-width: 80px; + justify-content: center; + } +} diff --git a/plugins/carhop-blocks/src/image-stack/index.js b/plugins/carhop-blocks/src/image-stack/index.js new file mode 100755 index 0000000..d537a9d --- /dev/null +++ b/plugins/carhop-blocks/src/image-stack/index.js @@ -0,0 +1,11 @@ +import { registerBlockType } from "@wordpress/blocks"; +import "./style.scss"; + +import Edit from "./edit"; +import save from "./save"; +import metadata from "./block.json"; + +registerBlockType(metadata.name, { + edit: Edit, + save, +}); diff --git a/plugins/carhop-blocks/src/image-stack/save.js b/plugins/carhop-blocks/src/image-stack/save.js new file mode 100755 index 0000000..7af7734 --- /dev/null +++ b/plugins/carhop-blocks/src/image-stack/save.js @@ -0,0 +1,33 @@ +import { useBlockProps } from "@wordpress/block-editor"; + +export default function save({ attributes }) { + const { images, height } = attributes; + const blockProps = useBlockProps.save(); + + const getImageStyle = (image) => { + const focalPoint = image.focalPoint || { x: 0.5, y: 0.5 }; + const scale = image.scale || 0.8; + const rotation = image.rotation || 0; + return { + left: `${focalPoint.x * 100}%`, + top: `${focalPoint.y * 100}%`, + transform: `rotate(${rotation}deg) scale(${scale})`, + }; + }; + + return ( +
+
+ {images.map((image) => ( +
+ {image.alt} +
+ ))} +
+
+ ); +} diff --git a/plugins/carhop-blocks/src/image-stack/style.scss b/plugins/carhop-blocks/src/image-stack/style.scss new file mode 100755 index 0000000..5b0195e --- /dev/null +++ b/plugins/carhop-blocks/src/image-stack/style.scss @@ -0,0 +1,23 @@ +/** + * The following styles get applied both on the front of your site + * and in the editor. + */ + +.wp-block-carhop-blocks-image-stack { + .image-stack-container { + position: relative; + width: 100%; + overflow: visible; + } + + .image-stack-item { + position: absolute; + + img { + width: 100%; + height: 100%; + object-fit: contain; + display: block; + } + } +} diff --git a/plugins/carhop-blocks/src/image-stack/view.js b/plugins/carhop-blocks/src/image-stack/view.js new file mode 100755 index 0000000..a1f041b --- /dev/null +++ b/plugins/carhop-blocks/src/image-stack/view.js @@ -0,0 +1,5 @@ +/** + * Frontend JavaScript for the Image Stack block. + * Currently no interactive features required, but this file + * is available for future enhancements. + */