From ca8b63da672788de615a7d4d728fb1df058d51b5 Mon Sep 17 00:00:00 2001 From: Steve Struemph Date: Thu, 5 Sep 2024 15:28:44 -0500 Subject: [PATCH 1/5] cleanup: Remove unused slider-vertical block --- assets/blocks/slider-vertical/index.js | 170 ------------------- assets/blocks/slider-vertical/swiper-init.js | 66 ------- 2 files changed, 236 deletions(-) delete mode 100644 assets/blocks/slider-vertical/index.js delete mode 100644 assets/blocks/slider-vertical/swiper-init.js diff --git a/assets/blocks/slider-vertical/index.js b/assets/blocks/slider-vertical/index.js deleted file mode 100644 index 915460f..0000000 --- a/assets/blocks/slider-vertical/index.js +++ /dev/null @@ -1,170 +0,0 @@ -/* eslint-disable no-unused-vars */ - -/** - * Kindling Slider Block with Swiper and Slide innerblock - */ - -import { registerBlockType, createBlock } from '@wordpress/blocks'; -import { __ } from '@wordpress/i18n'; -import { - useBlockProps, - BlockControls, - InnerBlocks, - RichText, -} from '@wordpress/block-editor'; -import { ToolbarGroup, ToolbarButton } from '@wordpress/components'; -import { useSelect, useDispatch } from '@wordpress/data'; - -const TEMPLATE = [ - [ 'kindling/slide-block', { placeholder: 'Enter side content...' } ], -]; - -registerBlockType( 'kindling/slider-block', { - title: __( 'Slider', 'kindling-slider-block' ), - category: 'design', - supports: { - multiple: false, - spacing: { - margin: true, - padding: true, - }, - }, - attributes: { - textString: { - type: 'string', - selector: 'h4', - }, - }, - - edit(props) { - const blockProps = useBlockProps({ - className: 'wp-block-kindling-slider-block slider-block swiper', - }); - - const { setAttributes, attributes, clientId } = props; - - function onTextChange(changes) { - setAttributes({ - textString: changes, - }); - } - - const { replaceInnerBlocks } = useDispatch( 'core/block-editor' ); - const innerBlocks = useSelect( - ( select ) => select( 'core/block-editor' ).getBlocks( clientId ), - [ clientId ] - ); - - const addNewSlide = () => { - const slideBlock = createBlock('kindling/slide-block', {}, [ - createBlock('core/heading', { level: 4, placeholder: 'Enter heading...' }), - createBlock('core/paragraph', { placeholder: 'Enter text...' }), - createBlock('core/button', { text: 'Click me' }), - ]); - - replaceInnerBlocks(clientId, [...innerBlocks, slideBlock], false); - }; - - return ( -
- - - - - - {/* TODO - make this a heading block instead of a richtext field */} - {/* */} -
-
- -
-
- -
-
- -
-
-
-
- ); - }, - - save: ({ attributes }) => { - const blockProps = useBlockProps.save({ - className: 'slider-block swiper', - }); - - return ( - -
-
-

{attributes.textString}

-
-
-
- -
- -
-
-
-
-
- - ); - }, -}); - -registerBlockType( 'kindling/slide-block', { - title: __( 'Slide', 'kindling-slide-block' ), - category: 'design', - parent: ['kindling/kindling-slider-block'], - - edit() { - const blockProps = useBlockProps({ - className: 'swiper-slide', - }); - - return ( - - ); - }, - - save() { - const blockProps = useBlockProps.save({ - className: 'swiper-slide', - }); - - return ( -
- -
- ); - }, -}); diff --git a/assets/blocks/slider-vertical/swiper-init.js b/assets/blocks/slider-vertical/swiper-init.js deleted file mode 100644 index 7701dfd..0000000 --- a/assets/blocks/slider-vertical/swiper-init.js +++ /dev/null @@ -1,66 +0,0 @@ -// TODO only import what we need -// Swiper bundle with all modules installed -import Swiper from 'swiper/bundle'; - -// TODO only import what we need -// Swiper styles bundle -import 'swiper/css/bundle'; - -/** - * Slider block headings for side "bullets" nav - */ - let slideBlocks = document.getElementsByClassName('wp-block-kindling-slide-block'); - - let titles = []; - - if (slideBlocks.length > 0) { - Array.from(slideBlocks).forEach(el => { - let slideHeading = el.querySelector('.wp-block-heading'); - - if (slideHeading && slideHeading !== null) { - slideHeading.dataset.title = slideHeading.innerHTML; - - titles.push(slideHeading.innerHTML); - return titles; - } - }); - } - - - -document.addEventListener('DOMContentLoaded', function () { - - let sliders = document.querySelectorAll('.slider-block'); - - sliders.forEach(slider => { - new Swiper(slider, { - a11y: { - prevSlideMessage: 'Previous slide', - nextSlideMessage: 'Next slide', - }, - autoHeight: true, - effect: 'fade', - fadeEffect: { - crossFade: true, - }, - loop: true, - pagination: { - el: slider.querySelector('.slider-block__pagination'), - clickable: true, - // The registerBlockType function has 'supports: multiple' set to false so this block can only be added to a page once - // The Swiper code here has been updated to enable multiple blocks on a page - // But there is a bug in this renderBullet function. The headings that make up the navigation on the left will - // load from the second to the last block. So if you have two sliders, they will come from the first one. - // It should be easy to fix but I ran into a wall with it. - renderBullet: function (index, className) { - let heading = this.slides[index].querySelector('.wp-block-heading').innerHTML; - return '' + heading + ''; - }, - }, - navigation: { - nextEl: slider.querySelector('.slider-block__button--next'), - prevEl: slider.querySelector('.slider-block__button--prev'), - }, - }); - }); -}); From 75da603bb9b82ae83660364bbf478a0a69a7ead3 Mon Sep 17 00:00:00 2001 From: Steve Struemph Date: Thu, 5 Sep 2024 15:29:51 -0500 Subject: [PATCH 2/5] feat(slider-udpate): Move swiper module and asset imports to block files --- assets/scripts/front.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/assets/scripts/front.js b/assets/scripts/front.js index 22451f3..ca060a1 100644 --- a/assets/scripts/front.js +++ b/assets/scripts/front.js @@ -3,17 +3,6 @@ */ import '../styles/front.scss'; -/** - * Swiper bundle with all modules installed - */ -// TODO only import what we need -import Swiper from 'swiper/bundle'; - -/** - * Swiper CSS bundle - */ -// TODO only import what we need -import 'swiper/css/bundle'; // A11y import coverBlockRolePresentation from './a11y/cover-block/presentation-role'; From 8bec4b4210e8115401790a62a88d0f8a51780b0a Mon Sep 17 00:00:00 2001 From: Steve Struemph Date: Thu, 5 Sep 2024 15:36:27 -0500 Subject: [PATCH 3/5] feat(slider-block): Add slider blocks --- assets/blocks/slide/block.json | 14 ++ assets/blocks/slide/edit.js | 23 +++ assets/blocks/slide/index.js | 23 +++ assets/blocks/slide/save.js | 18 +++ assets/blocks/slide/style.scss | 10 ++ assets/blocks/slider/index.js | 208 ++++++++++++++++++++++++++++ assets/blocks/slider/style.scss | 63 +++++++++ assets/blocks/slider/swiper-init.js | 29 ++++ assets/scripts/editor.js | 9 ++ assets/scripts/front.js | 5 + 10 files changed, 402 insertions(+) create mode 100644 assets/blocks/slide/block.json create mode 100644 assets/blocks/slide/edit.js create mode 100644 assets/blocks/slide/index.js create mode 100644 assets/blocks/slide/save.js create mode 100644 assets/blocks/slide/style.scss create mode 100644 assets/blocks/slider/index.js create mode 100644 assets/blocks/slider/style.scss create mode 100644 assets/blocks/slider/swiper-init.js diff --git a/assets/blocks/slide/block.json b/assets/blocks/slide/block.json new file mode 100644 index 0000000..6646052 --- /dev/null +++ b/assets/blocks/slide/block.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "kindling/slide", + "title": "Slide", + "category": "design", + "editorScript": "file:./index.js", + "attributes": { + "className": { + "type": "string", + "default": "swiper-slide" + } + } +} \ No newline at end of file diff --git a/assets/blocks/slide/edit.js b/assets/blocks/slide/edit.js new file mode 100644 index 0000000..344010e --- /dev/null +++ b/assets/blocks/slide/edit.js @@ -0,0 +1,23 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useBlockProps, InnerBlocks } from '@wordpress/block-editor'; + +const Edit = (props) => { + // Retrieve attributes from props + const { attributes } = props; + const { className } = attributes; + + // Use useBlockProps to dynamically set the className + const blockProps = useBlockProps({ + className: className || 'swiper-slide', // Fallback to 'swiper-slide' if className is undefined + }); + + return ( +
+ +
+ ); +}; +export default Edit; diff --git a/assets/blocks/slide/index.js b/assets/blocks/slide/index.js new file mode 100644 index 0000000..a266810 --- /dev/null +++ b/assets/blocks/slide/index.js @@ -0,0 +1,23 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import Edit from './edit'; +import Save from './save'; + +// Export this so we can use it in the edit and save files +export const innerBlocksTemplate = [ + [ 'kindling/slide-block', { placeholder: 'Enter slide content...' } ], +]; + +// Register the block +registerBlockType( metadata.name, { + title: metadata.title, + edit: Edit, + save: Save, +} ); diff --git a/assets/blocks/slide/save.js b/assets/blocks/slide/save.js new file mode 100644 index 0000000..f752830 --- /dev/null +++ b/assets/blocks/slide/save.js @@ -0,0 +1,18 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useBlockProps, InnerBlocks } from '@wordpress/block-editor'; + +const Save = () => { + const blockProps = useBlockProps.save({ + className: 'swiper-slide', + }); + + return ( +
+ +
+ ); +}; +export default Save; diff --git a/assets/blocks/slide/style.scss b/assets/blocks/slide/style.scss new file mode 100644 index 0000000..e52a64c --- /dev/null +++ b/assets/blocks/slide/style.scss @@ -0,0 +1,10 @@ +// Editor only +// .block-editor-block-list__block.wp-block.slider-block { +// // editor styles +// } + +// .editor-styles-wrapper .block-editor-block-list__block.wp-block.slider-block { +// // editor styles +// } + +// End Editor only diff --git a/assets/blocks/slider/index.js b/assets/blocks/slider/index.js new file mode 100644 index 0000000..c317936 --- /dev/null +++ b/assets/blocks/slider/index.js @@ -0,0 +1,208 @@ +import { registerBlockType, createBlock } from '@wordpress/blocks'; +import { InnerBlocks, BlockControls, useBlockProps } from '@wordpress/block-editor'; +import { Toolbar, ToolbarButton, Card, CardBody, Button } from '@wordpress/components'; +import { chevronLeft, chevronRight, plus } from '@wordpress/icons'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { useEffect, useState } from '@wordpress/element'; + +/** + * Register a new block type for managing slides + */ +registerBlockType( 'kindling/slider', { + apiVersion: 2, + title: 'Slider', + category: 'layout', + icon: 'id', + + /** + * Supports + */ + supports: { + align: ['wide', 'full'], // You can specify which alignments to support + }, + + /** + * The attributes object defines the data structure of the block. + */ + attributes: { + activeBlockIndex: { + type: 'number', + default: 0, + }, + }, + + /** + * The edit function defines the functionality of the block within the editor. + * This is where we use React Hooks to manage state and control the block's behaviour. + */ + edit: (props) => { + // destructuring props and attributes + const { clientId, attributes, setAttributes } = props; + const { activeBlockIndex } = attributes; + + // initializing hooks for dispatching actions and selecting data from the store + const { getBlocks } = useSelect( select => select( 'core/block-editor' ) ); + + // Get the selected block ID + const selectedBlockId = useSelect( ( select ) => { + const selectedBlock = select('core/block-editor').getSelectedBlock(); + return selectedBlock ? selectedBlock.clientId : null; + }, []); + + // Dispatch actions + const { updateBlockAttributes } = useDispatch( 'core/block-editor' ); + const { replaceInnerBlocks } = useDispatch( 'core/block-editor' ); + + // state variables + const innerBlocks = getBlocks( clientId ); + const [ numOfBlocks, setNumOfBlocks ] = useState( innerBlocks.length ); + + // block properties + const blockProps = useBlockProps({ + className: 'slider-block', + }); + + // + const handlePrev = () => { + const newIndex = activeBlockIndex === 0 ? numOfBlocks - 1 : activeBlockIndex - 1; + setAttributes({ activeBlockIndex: newIndex }); + }; + const handleNext = () => { + const newIndex = activeBlockIndex === numOfBlocks - 1 ? 0 : activeBlockIndex + 1; + setAttributes({ activeBlockIndex: newIndex }); + }; + + // Function to add a new slide block + const addNewSlide = () => { + + // Create a new slide block + const slideBlock = createBlock('kindling/slide', { className: 'editor-inactive-block' }, [ + // Create a paragraph block with a placeholder + createBlock('core/paragraph', { placeholder: 'Enter content.' }), + ]); + + // Replace the inner blocks with the new slide block + replaceInnerBlocks(clientId, [...innerBlocks, slideBlock], false); + + // Update the class name of the slide block if there are no inner blocks + if (innerBlocks.length === 0) { + updateBlockAttributes(slideBlock.clientId, { className: '' }); + } + + // Increment the number of blocks + setNumOfBlocks(numOfBlocks + 1) + }; + + // hook to manage effects + useEffect(() => { + // Ensure state consistency and manage DOM querying + setNumOfBlocks(innerBlocks.length); + + // Iterate over each block and set the active block + innerBlocks.forEach((block, index) => { + + // Set the active block to the selected block - This is working + if (block.clientId === selectedBlockId) { + setAttributes({ activeBlockIndex: index }); + } + + // Set the class name of the block based on the active block index - This is not working + const newClassName = index === activeBlockIndex ? 'editor-active-block' : 'editor-inactive-block'; + updateBlockAttributes(block.clientId, { className: newClassName }); + + }); + }, [innerBlocks, activeBlockIndex, selectedBlockId, updateBlockAttributes]); + + // rendering the block within the editor + return ( + <> + + + + + + + + + + + + + +

Editing slide {`${activeBlockIndex + 1} of ${numOfBlocks}`}

+ +
+ + +
+ +
+
+ +
+ {/* Slide block content will be rendered here */} + +
+ + ); + }, + + /** + * Defines how the block will be saved in the post_content. + * It will be saved as a div with the content of the InnerBlocks. + */ + save: () => { + const blockProps = useBlockProps.save({ + className: 'slider-block', + }); + + return ( +
+
+ +
+
+ {/*
+
*/} +
+ ); + }, +}); diff --git a/assets/blocks/slider/style.scss b/assets/blocks/slider/style.scss new file mode 100644 index 0000000..2b9ed79 --- /dev/null +++ b/assets/blocks/slider/style.scss @@ -0,0 +1,63 @@ +/* Hide inactive Slide Blocks in editor. */ +.wp-block[data-type="kindling/slide"] { + .editor-inactive-block { + @apply hidden; + } +} + +/* Editor styles - Block heading */ +.editor-styles-wrapper { + + // Card component + .kindling-block-header { + @apply flex justify-between items-center px-4 py-2 mb-4; + + &__button-wrapper { + @apply flex gap-4; + } + + // Button components + &__button { + &.has-icon.has-text { + + &.prev { + @apply pr-4; + } + + &.next { + @apply pl-4 flex flex-row-reverse; + } + + &.is-primary { + @apply pr-4; + } + + svg { + @apply mr-0; + } + } + } + + p { + font-size: 14px; + color: var(--wp-admin-theme-color); + } + } +} + +/* Frontend styles */ +.wp-block-kindling-slider { + overflow: hidden !important; + position: relative !important; +} + +// Hide Notification Bar +.swiper-notification { + @apply hidden invisible; +} + +// Reset pagination bottom position +.slider-block.swiper-horizontal>.swiper-pagination-bullets, +.slider-block .swiper-pagination-bullets.swiper-pagination-horizontal { + bottom: 0; +} \ No newline at end of file diff --git a/assets/blocks/slider/swiper-init.js b/assets/blocks/slider/swiper-init.js new file mode 100644 index 0000000..33f669e --- /dev/null +++ b/assets/blocks/slider/swiper-init.js @@ -0,0 +1,29 @@ +import Swiper from 'swiper/bundle'; +import 'swiper/css/bundle'; + +/** + * Initialize Swiper with settings. + */ +console.log('sliderSwiperInit file loaded'); + +function sliderSwiperInit() { + const swiper = new Swiper('.slider-block', { + autoplay: true, + loop: true, + slidesPerView: 1, + spaceBetween: 0, + + pagination: { + el: '.slider-block__pagination', + clickable: true, + }, + + // Navigation arrows + // navigation: { + // nextEl: '.slider-block__button--next', + // prevEl: '.slider-block__button--prev', + // }, + }); +} + +export default sliderSwiperInit; diff --git a/assets/scripts/editor.js b/assets/scripts/editor.js index 6d6b88d..2704422 100644 --- a/assets/scripts/editor.js +++ b/assets/scripts/editor.js @@ -49,6 +49,15 @@ import '../block-extensions/safe-svg'; */ import '../block-extensions/block-toolbar/block-links'; +/** + * Custom Theme Blocks. + */ + +// Slider block +import '../blocks/slider'; +// Individiual slide block +import '../blocks/slide'; + /** * Unregister two block styles for the 'core/button' block type when the DOM is ready. * diff --git a/assets/scripts/front.js b/assets/scripts/front.js index ca060a1..de10113 100644 --- a/assets/scripts/front.js +++ b/assets/scripts/front.js @@ -3,6 +3,8 @@ */ import '../styles/front.scss'; +// Slider block +import sliderSwiperInit from '../blocks/slider/swiper-init'; // A11y import coverBlockRolePresentation from './a11y/cover-block/presentation-role'; @@ -54,4 +56,7 @@ document.addEventListener('DOMContentLoaded', function () { // Add role = presentation to cover block bg image coverBlockRolePresentation(); + // Blocks + sliderSwiperInit(); // Slider block + }); From fbdfc8aa7bba693283b6271e0cbe3018db15e533 Mon Sep 17 00:00:00 2001 From: Steve Struemph Date: Thu, 5 Sep 2024 15:36:56 -0500 Subject: [PATCH 4/5] feat(slider-block): Add css to theme styles --- assets/styles/editor.scss | 2 ++ assets/styles/front.scss | 2 ++ 2 files changed, 4 insertions(+) diff --git a/assets/styles/editor.scss b/assets/styles/editor.scss index 4c46bc0..a2e44a0 100644 --- a/assets/styles/editor.scss +++ b/assets/styles/editor.scss @@ -19,6 +19,8 @@ @import './components/blocks'; @import '../blocks/search-icon/style.scss'; +@import '../blocks/slider/style.scss'; +@import '../blocks/slide/style.scss'; @import '../block-extensions/block-toolbar/block-links/style.scss'; @import '../block-extensions/box-shadow/style.scss'; diff --git a/assets/styles/front.scss b/assets/styles/front.scss index 69d77ab..59c736e 100644 --- a/assets/styles/front.scss +++ b/assets/styles/front.scss @@ -19,6 +19,8 @@ @import './components/blocks'; @import '../blocks/search-icon/style.scss'; +@import '../blocks/slider/style.scss'; +@import '../blocks/slide/style.scss'; @import '../block-extensions/block-toolbar/block-links/style.scss'; @import '../block-extensions/box-shadow/style.scss'; From 89018dbf5fb09eb20595e3a02ec62545bfc72e9b Mon Sep 17 00:00:00 2001 From: Steve Struemph Date: Thu, 5 Sep 2024 15:41:07 -0500 Subject: [PATCH 5/5] feat(slider-block): Add a project-level slider stylesheet for overrides --- assets/styles/components/slider-block.scss | 127 +++++---------------- 1 file changed, 27 insertions(+), 100 deletions(-) diff --git a/assets/styles/components/slider-block.scss b/assets/styles/components/slider-block.scss index 755cb87..1883c20 100644 --- a/assets/styles/components/slider-block.scss +++ b/assets/styles/components/slider-block.scss @@ -1,112 +1,39 @@ -// Editor only -.block-editor-block-list__block.wp-block.slider-block { - display: block !important; - height: auto !important; -} +// This file is external to the Slider block (and Slide innerblock). +// These are meant to be overrides to customize the block for this site. -.editor-styles-wrapper .block-editor-block-list__block.wp-block.slider-block { - flex-direction: row-reverse; - display: flex !important; - height: auto !important; -} -/* End Editor only */ +.wp-block-kindling-slider { -.slider-block { - @apply flex w-full relative; + .swiper-pagination { + bottom: 0; - &::before { - @apply content-[''] bg-blue absolute bottom-0 right-0 shadow-sm; + max-width: var(--wp--style--global--content-size) !important; + margin-left: auto !important; + margin-right: auto !important; - height: calc(100% - 84px); - width: 685px; + //// Left align + // left: 50% !important; + // text-align: left !important; + // transform: translateX(-50%) !important; } - &__heading { - @apply text-white font-semibold text-5xl leading-tight mt-0 mb-14; - } - - &__pagination-wrapper { - @apply min-w-[640px] min-h-[550px] bg-blue p-14 z-20 mb-[84px]; - } - - &__pagination { - .pagination-item { - @apply text-2xl font-light leading-snug text-blue pl-[110px]; - - // TODO is there a setting to disable the pagination swiper styles per variation or entirely? - // TODO cont... maybe if those styles just aren't loaded? Right now it's loading the entire bundle of styles and features - &.swiper-pagination-bullet-active { - @apply font-bold text-white relative; - - background: unset; - opacity: unset; - - &::before { - @apply content-[''] absolute w-[80px] h-[5px] bg-black left-0 top-1/2 transform -translate-y-1/2; - } - } - - &.swiper-pagination-bullet { - @apply w-full; - - background: unset; - border-radius: unset; - height: unset; - opacity: unset; - } - } - } + // Swiper "bullet" pagination example styles + /* Set to equal width and height with rounded-full for a bullet */ + .swiper-pagination-bullet { + @apply w-[65px] h-[6px] bg-medium-grey opacity-[0.35] mr-2 cursor-pointer transition; - // (Single) Slide block - .swiper-wrapper { - @apply mt-[84px] pl-[94px] z-[1] pt-[84px] pb-[84px]; - } - - .swiper-slide { - @apply w-[380px]; - } -} + // Resets + border-radius: 0; -// Pagination overrides -.swiper-pagination-vertical.swiper-pagination-bullets, -.swiper-vertical > .swiper-pagination-bullets { - @apply relative left-0 right-auto top-0 transform-none; -} - -// Nav Arrows -.slider-block__swiper-nav { - @apply absolute bottom-4 flex flex-row left-1/2 -translate-x-1/2 gap-[25px] z-20; - - .slider-block__button { - &--next, - &--prev { - @apply h-[50px] w-[50px] cursor-pointer focus:outline-none focus:ring-2 focus:ring-blue focus:ring-offset-2 transition duration-75 hover:scale-110; + // Hover or focused state + &:hover, + &:active, + &:focus { + @apply bg-grey opacity-100; } - &--prev { - - &::after { - @apply inline-block h-[50px] w-[50px] transition-all bg-dark-blue relative left-0 ease-out; - - content: ''; - mask-image: url(../images/arrow-left-circle.svg); - mask-repeat: no-repeat; - mask-position: center; - mask-size: contain; - } - } - - &--next { - - &::after { - @apply inline-block h-[50px] w-[50px] transition-all bg-dark-blue relative right-0 ease-out; - - content: ''; - mask-image: url(../images/arrow-right-circle.svg); - mask-repeat: no-repeat; - mask-position: center; - mask-size: contain; - } + // Current slide + &-active { + @apply bg-grey opacity-100; } } -} +} \ No newline at end of file