From dc1b8c4963719f57032e0195596ac3cf815e1361 Mon Sep 17 00:00:00 2001 From: Brandon Hubbard Date: Mon, 16 Mar 2026 12:49:22 -0700 Subject: [PATCH 1/2] Add support for the progress bar. This would support issue #88 - Add support for progress bar. --- inc/Plugin.php | 1 + src/blocks/carousel/progress/block.json | 20 ++++++++++++++++++++ src/blocks/carousel/progress/edit.tsx | 23 +++++++++++++++++++++++ src/blocks/carousel/progress/index.ts | 11 +++++++++++ src/blocks/carousel/progress/save.tsx | 17 +++++++++++++++++ src/blocks/carousel/progress/style.scss | 16 ++++++++++++++++ src/blocks/carousel/types.ts | 2 ++ src/blocks/carousel/view.ts | 10 ++++++++++ 8 files changed, 100 insertions(+) create mode 100644 src/blocks/carousel/progress/block.json create mode 100644 src/blocks/carousel/progress/edit.tsx create mode 100644 src/blocks/carousel/progress/index.ts create mode 100644 src/blocks/carousel/progress/save.tsx create mode 100644 src/blocks/carousel/progress/style.scss diff --git a/inc/Plugin.php b/inc/Plugin.php index 25f975a..67e4ddc 100644 --- a/inc/Plugin.php +++ b/inc/Plugin.php @@ -70,6 +70,7 @@ public function register_blocks(): void { 'carousel', 'carousel/controls', 'carousel/dots', + 'carousel/progress', 'carousel/viewport', 'carousel/slide', ]; diff --git a/src/blocks/carousel/progress/block.json b/src/blocks/carousel/progress/block.json new file mode 100644 index 0000000..7b99a38 --- /dev/null +++ b/src/blocks/carousel/progress/block.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "version": "1.0.0", + "name": "carousel-kit/carousel-progress", + "title": "Carousel Progress", + "category": "carousel-kit", + "icon": "minus", + "ancestor": [ + "carousel-kit/carousel" + ], + "description": "Progress bar for the carousel.", + "textdomain": "carousel-kit", + "attributes": {}, + "supports": { + "interactivity": true + }, + "editorScript": "file:./index.js", + "style": "file:./style-index.css" +} diff --git a/src/blocks/carousel/progress/edit.tsx b/src/blocks/carousel/progress/edit.tsx new file mode 100644 index 0000000..7293e4b --- /dev/null +++ b/src/blocks/carousel/progress/edit.tsx @@ -0,0 +1,23 @@ +import { __ } from '@wordpress/i18n'; +import { useBlockProps } from '@wordpress/block-editor'; +import { useContext } from '@wordpress/element'; +import { EditorCarouselContext } from '../editor-context'; + +export default function Edit() { + const blockProps = useBlockProps( { + className: 'carousel-kit-progress', + } ); + + const { scrollProgress } = useContext( EditorCarouselContext ) as { scrollProgress?: number }; + + return ( +
+
+
+ ); +} diff --git a/src/blocks/carousel/progress/index.ts b/src/blocks/carousel/progress/index.ts new file mode 100644 index 0000000..104f097 --- /dev/null +++ b/src/blocks/carousel/progress/index.ts @@ -0,0 +1,11 @@ +import { registerBlockType, type BlockConfiguration } from '@wordpress/blocks'; +import Edit from './edit'; +import Save from './save'; +import metadata from './block.json'; +import type { CarouselProgressAttributes } from '../types'; +import './style.scss'; + +registerBlockType( metadata as BlockConfiguration, { + edit: Edit, + save: Save, +} ); diff --git a/src/blocks/carousel/progress/save.tsx b/src/blocks/carousel/progress/save.tsx new file mode 100644 index 0000000..7fb62a3 --- /dev/null +++ b/src/blocks/carousel/progress/save.tsx @@ -0,0 +1,17 @@ +import { useBlockProps } from '@wordpress/block-editor'; + +export default function Save() { + return ( +
+
+
+ ); +} diff --git a/src/blocks/carousel/progress/style.scss b/src/blocks/carousel/progress/style.scss new file mode 100644 index 0000000..87f9061 --- /dev/null +++ b/src/blocks/carousel/progress/style.scss @@ -0,0 +1,16 @@ +.carousel-kit-progress { + width: 100%; + height: 4px; + background-color: #eee; + overflow: hidden; + position: relative; + margin-top: 10px; + + &__bar { + height: 100%; + background-color: #000; + width: 0; + transition: width 0.1s ease-out; + transform-origin: left; + } +} diff --git a/src/blocks/carousel/types.ts b/src/blocks/carousel/types.ts index c59eba4..0339acc 100644 --- a/src/blocks/carousel/types.ts +++ b/src/blocks/carousel/types.ts @@ -23,6 +23,7 @@ export type CarouselViewportAttributes = Record; export type CarouselSlideAttributes = Record; export type CarouselControlsAttributes = Record; export type CarouselDotsAttributes = Record; +export type CarouselProgressAttributes = Record; /** * Typed subset of the block editor store selectors used in this plugin. @@ -51,6 +52,7 @@ export type CarouselContext = { scrollSnaps: { index: number }[]; canScrollPrev: boolean; canScrollNext: boolean; + scrollProgress: number; ariaLabelPattern: string; ref?: HTMLElement | null; }; diff --git a/src/blocks/carousel/view.ts b/src/blocks/carousel/view.ts index 293aa22..f270609 100644 --- a/src/blocks/carousel/view.ts +++ b/src/blocks/carousel/view.ts @@ -133,6 +133,12 @@ store( 'carousel-kit/carousel', { const index = ( snap?.index || 0 ) + 1; return context.ariaLabelPattern.replace( '%d', index.toString() ); }, + getProgressBarStyle: () => { + const { scrollProgress } = getContext(); + return { + width: `${ ( scrollProgress || 0 ) * 100 }%`, + }; + }, initCarousel: () => { try { const context = getContext(); @@ -220,10 +226,14 @@ store( 'carousel-kit/carousel', { context.scrollSnaps = embla .scrollSnapList() .map( ( _, index ) => ( { index } ) ); + context.scrollProgress = embla.scrollProgress(); }; embla.on( 'select', updateState ); embla.on( 'reInit', updateState ); + embla.on( 'scroll', () => { + context.scrollProgress = embla.scrollProgress(); + } ); // Autoplay API Integration embla.on( 'autoplay:timerset', () => { From 30cdeba71a3b58c56ae7771c52e43689ad5facfc Mon Sep 17 00:00:00 2001 From: Masud Rana Date: Thu, 26 Mar 2026 13:26:43 +0600 Subject: [PATCH 2/2] feat: implement scroll progress tracking for carousel component --- src/blocks/carousel/edit.tsx | 30 ++++++++++++++++++++++++- src/blocks/carousel/editor-context.ts | 4 ++++ src/blocks/carousel/progress/edit.tsx | 10 ++++++++- src/blocks/carousel/progress/save.tsx | 5 +++++ src/blocks/carousel/progress/style.scss | 4 ++-- 5 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/blocks/carousel/edit.tsx b/src/blocks/carousel/edit.tsx index 3b0d652..fa7a5f8 100644 --- a/src/blocks/carousel/edit.tsx +++ b/src/blocks/carousel/edit.tsx @@ -20,7 +20,7 @@ import { } from '@wordpress/components'; import { plus } from '@wordpress/icons'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useState, useMemo, useCallback } from '@wordpress/element'; +import { useState, useMemo, useCallback, useEffect } from '@wordpress/element'; import { createBlock, type BlockConfiguration } from '@wordpress/blocks'; import type { CarouselAttributes } from './types'; import { EditorCarouselContext } from './editor-context'; @@ -55,6 +55,7 @@ export default function Edit( { const [ emblaApi, setEmblaApi ] = useState(); const [ canScrollPrev, setCanScrollPrev ] = useState( false ); const [ canScrollNext, setCanScrollNext ] = useState( false ); + const [ scrollProgress, setScrollProgress ] = useState( 0 ); const { replaceInnerBlocks, insertBlock } = useDispatch( 'core/block-editor' ); @@ -126,19 +127,46 @@ export default function Edit( { setCanScrollPrev, canScrollNext, setCanScrollNext, + scrollProgress, + setScrollProgress, carouselOptions, } ), [ emblaApi, canScrollPrev, canScrollNext, + scrollProgress, carouselOptions, setEmblaApi, setCanScrollPrev, setCanScrollNext, + setScrollProgress, ], ); + // Subscribe to Embla scroll event to update scrollProgress + useEffect(() => { + if (!emblaApi) return; + + const updateScrollProgress = () => { + setScrollProgress(emblaApi.scrollProgress()); + }; + + emblaApi + .on('scroll', updateScrollProgress) + .on('select', updateScrollProgress) + .on('reInit', updateScrollProgress); + + updateScrollProgress(); + + return () => { + emblaApi + .off('scroll', updateScrollProgress) + .off('select', updateScrollProgress) + .off('reInit', updateScrollProgress); + }; + }, [emblaApi]); + const createNavGroup = () => createBlock( 'core/group', diff --git a/src/blocks/carousel/editor-context.ts b/src/blocks/carousel/editor-context.ts index 9937ba0..778497c 100644 --- a/src/blocks/carousel/editor-context.ts +++ b/src/blocks/carousel/editor-context.ts @@ -9,6 +9,8 @@ export type EditorCarouselContextType = { canScrollNext: boolean; setCanScrollPrev: ( value: boolean ) => void; setCanScrollNext: ( value: boolean ) => void; + scrollProgress: number; + setScrollProgress: ( value: number ) => void; carouselOptions: Omit, 'slidesToScroll'> & { slidesToScroll?: number | string; }; @@ -22,6 +24,8 @@ const defaultValue: EditorCarouselContextType = { canScrollNext: false, setCanScrollPrev: () => {}, setCanScrollNext: () => {}, + scrollProgress: 0, + setScrollProgress: () => {}, carouselOptions: {}, }; diff --git a/src/blocks/carousel/progress/edit.tsx b/src/blocks/carousel/progress/edit.tsx index 7293e4b..24a804b 100644 --- a/src/blocks/carousel/progress/edit.tsx +++ b/src/blocks/carousel/progress/edit.tsx @@ -8,12 +8,20 @@ export default function Edit() { className: 'carousel-kit-progress', } ); - const { scrollProgress } = useContext( EditorCarouselContext ) as { scrollProgress?: number }; + const { scrollProgress, slideCount } = useContext( EditorCarouselContext ) as { scrollProgress?: number, slideCount?: number }; + + // Hide if only one slide + if (slideCount === 1) return null; return (
diff --git a/src/blocks/carousel/progress/style.scss b/src/blocks/carousel/progress/style.scss index 87f9061..a4b4a48 100644 --- a/src/blocks/carousel/progress/style.scss +++ b/src/blocks/carousel/progress/style.scss @@ -1,14 +1,14 @@ .carousel-kit-progress { width: 100%; height: 4px; - background-color: #eee; + background-color: var(--carousel-kit-progress-bg-color, rgb(221, 221, 221)); overflow: hidden; position: relative; margin-top: 10px; &__bar { height: 100%; - background-color: #000; + background-color: var(--carousel-kit-progress-color, rgba(28, 28, 28, 1)); width: 0; transition: width 0.1s ease-out; transform-origin: left;