From 6be917406a5f0ad4fbb1d1778583d293168bb61f Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Tue, 28 Nov 2023 13:48:44 +0100 Subject: [PATCH 01/10] Swap libraries, now it uses embla caroussel --- .github/workflows/acceptance.yml | 2 +- package.json | 3 +- src/components/View.jsx | 224 ++++++++++++++++++++----------- src/theme/main.less | 110 ++++++++++++++- yarn.lock | 93 ++++--------- 5 files changed, 283 insertions(+), 149 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 25f99d9..6d120ae 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -4,7 +4,7 @@ on: [push] env: ADDON_NAME: "@kitconcept/volto-slider-block" ADDON_PATH: "volto-slider-block" - VOLTO_VERSION: "17.2.0" + VOLTO_VERSION: "17.6.0" jobs: diff --git a/package.json b/package.json index 9abf289..49ea076 100644 --- a/package.json +++ b/package.json @@ -99,8 +99,7 @@ }, "dependencies": { "deepmerge": "4.2.2", - "react-slick": "0.29.0", - "slick-carousel": "1.8.1" + "embla-carousel-react": "^8.0.0-rc15" }, "peerDependencies": { "@plone/volto": "^17.0.0-alpha.21" diff --git a/src/components/View.jsx b/src/components/View.jsx index e8cf146..3298591 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -1,6 +1,6 @@ -import React from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { Message } from 'semantic-ui-react'; -import Slider from 'react-slick'; +import useEmblaCarousel from 'embla-carousel-react'; import cx from 'classnames'; import { defineMessages, useIntl } from 'react-intl'; import Body from './Body'; @@ -20,6 +20,56 @@ const messages = defineMessages({ }, }); +const DotButton = (props) => { + const { children, ...restProps } = props; + + return ( + + ); +}; + +export const PrevButton = (props) => { + const { children, ...restProps } = props; + + return ( + + ); +}; + +export const NextButton = (props) => { + const { children, ...restProps } = props; + + return ( + + ); +}; + const PrevArrow = ({ className, style, onClick }) => ( + ); +}; + +export const PrevButton = (props) => { + const { children, ...restProps } = props; + + return ( + + ); +}; + +export const NextButton = (props) => { + const { children, ...restProps } = props; + + return ( + + ); +}; + +const PrevArrow = ({ className, style, onClick }) => ( + +); + +const NextArrow = ({ className, style, onClick }) => ( + +); diff --git a/src/components/View.jsx b/src/components/View.jsx index 3298591..bb58497 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -5,12 +5,8 @@ import cx from 'classnames'; import { defineMessages, useIntl } from 'react-intl'; import Body from './Body'; import { withBlockExtensions } from '@plone/volto/helpers'; -import { Icon } from '@plone/volto/components'; -import rightArrowSVG from '@plone/volto/icons/right-key.svg'; -import leftArrowSVG from '@plone/volto/icons/left-key.svg'; +import { DotButton, NextButton, PrevButton } from './DotsAndArrows'; import teaserTemplate from '../icons/teaser-template.svg'; -import { SlidesWidthFix, useNodeDimensions } from '../helpers'; -import config from '@plone/volto/registry'; const messages = defineMessages({ PleaseChooseContent: { @@ -20,78 +16,6 @@ const messages = defineMessages({ }, }); -const DotButton = (props) => { - const { children, ...restProps } = props; - - return ( - - ); -}; - -export const PrevButton = (props) => { - const { children, ...restProps } = props; - - return ( - - ); -}; - -export const NextButton = (props) => { - const { children, ...restProps } = props; - - return ( - - ); -}; - -const PrevArrow = ({ className, style, onClick }) => ( - -); - -const NextArrow = ({ className, style, onClick }) => ( - -); - const SliderView = (props) => { const { className, @@ -160,8 +84,10 @@ const SliderView = (props) => { // This syncs the current slide with the objectwidget (or other sources // able to access the slider context) // that can modify the SliderContext (and come here via props slideIndex) - scrollTo(slideIndex); - }, [slideIndex, scrollTo]); + if (isEditMode) { + scrollTo(slideIndex); + } + }, [slideIndex, scrollTo, isEditMode]); return ( <> @@ -175,35 +101,36 @@ const SliderView = (props) => { )} {data.slides?.length > 0 && ( -
-
-
- {data.slides && - data.slides.map((item, index) => { - return ( -
- -
- ); - })} + <> +
+
+
+ {data.slides && + data.slides.map((item, index) => { + return ( +
+ +
+ ); + })} +
-
{scrollSnaps.map((_, index) => ( { /> ))}
-
+ )}
diff --git a/src/theme/main.less b/src/theme/main.less index d4c3330..86991e7 100644 --- a/src/theme/main.less +++ b/src/theme/main.less @@ -8,6 +8,8 @@ :root { --brand-primary: rgb(47, 112, 193); --brand-secondary: rgb(116, 97, 195); + --slider-dots-selected-bg: #000; + --slider-dots-bg: #ececec; } .embla { @@ -77,11 +79,6 @@ } .embla__dots { - position: absolute; - z-index: 1; - right: 0; - bottom: 1.6rem; - left: 0; display: flex; align-items: center; justify-content: center; @@ -89,27 +86,21 @@ .embla__dot { display: flex; - width: 2.4rem; - height: 2.4rem; + width: 46px; + height: 20px; align-items: center; - margin-right: 0.75rem; - margin-left: 0.75rem; + margin-right: 13px; } .embla__dot:after { width: 100%; - height: 0.3rem; - border-radius: 0.2rem; - background: var(--background-site); + height: 6px; + background: var(--slider-dots-bg); content: ''; } .embla__dot--selected:after { - background: linear-gradient( - 45deg, - var(--brand-primary), - var(--brand-secondary) - ); + background: var(--slider-dots-selected-bg); } .block.slider { From b137d143ce7b4ce7828e836af3b872ecb02265dd Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Tue, 28 Nov 2023 17:40:06 +0100 Subject: [PATCH 03/10] Fix all a11y --- src/components/DotsAndArrows.jsx | 10 ++++++++-- src/components/View.jsx | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/DotsAndArrows.jsx b/src/components/DotsAndArrows.jsx index 04bc1da..4cb22ba 100644 --- a/src/components/DotsAndArrows.jsx +++ b/src/components/DotsAndArrows.jsx @@ -3,10 +3,14 @@ import rightArrowSVG from '@plone/volto/icons/right-key.svg'; import leftArrowSVG from '@plone/volto/icons/left-key.svg'; export const DotButton = (props) => { - const { children, ...restProps } = props; + const { children, index, ...restProps } = props; return ( - ); @@ -19,6 +23,7 @@ export const PrevButton = (props) => { ); }; - -const PrevArrow = ({ className, style, onClick }) => ( - -); - -const NextArrow = ({ className, style, onClick }) => ( - -); diff --git a/src/components/View.jsx b/src/components/View.jsx index 372975f..ca76b78 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -109,16 +109,16 @@ const SliderView = (props) => { )} {data.slides?.length > 0 && ( <> -
+
-
-
+
+
{data.slides && data.slides.map((item, index) => { return ( -
+
{
-
+ +
{scrollSnaps.map((_, index) => ( scrollTo(index)} - className={'embla__dot'.concat( - index === selectedIndex ? ' embla__dot--selected' : '', + className={'slider-dot'.concat( + index === selectedIndex ? ' slider-dot--selected' : '', )} /> ))} diff --git a/src/helpers/SlidesWidthFix/SlidesWidthFix.jsx b/src/helpers/SlidesWidthFix/SlidesWidthFix.jsx deleted file mode 100644 index 3e6a33e..0000000 --- a/src/helpers/SlidesWidthFix/SlidesWidthFix.jsx +++ /dev/null @@ -1,18 +0,0 @@ -export const SlidesWidthFix = ({ width }) => { - return ( - - ); -}; - -export default SlidesWidthFix; diff --git a/src/helpers/index.js b/src/helpers/index.js index 0bc15fd..027536a 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -1,16 +1,5 @@ -/** - * Add your helpers here. - * @module helpers - * @example - * export { Api } from './Api/Api'; - */ - import { getTeaserImageURL } from './Image/Image'; import { mergeSchemas } from './Schema/Schema'; -import useNodeDimensions from './useNodeDimensions/useNodeDimensions'; -import SlidesWidthFix from './SlidesWidthFix/SlidesWidthFix'; export { getTeaserImageURL }; export { mergeSchemas }; -export { useNodeDimensions }; -export { SlidesWidthFix }; diff --git a/src/helpers/useNodeDimensions/useNodeDimensions.js b/src/helpers/useNodeDimensions/useNodeDimensions.js deleted file mode 100644 index 75c8111..0000000 --- a/src/helpers/useNodeDimensions/useNodeDimensions.js +++ /dev/null @@ -1,46 +0,0 @@ -import { useState, useEffect } from 'react'; - -function getDimensionObject(node) { - const rect = node.getBoundingClientRect(); - - if (rect.toJSON) { - return rect.toJSON(); - } else { - return { - width: rect.width, - height: rect.height, - top: rect.top || rect.y, - left: rect.left || rect.x, - x: rect.x || rect.left, - y: rect.y || rect.top, - right: rect.right, - bottom: rect.bottom, - }; - } -} - -function useNodeDimensions(node) { - const [dimensions, setDimensions] = useState({}); - - useEffect(() => { - if (node) { - const measure = () => - window.requestAnimationFrame(() => - setDimensions(getDimensionObject(node)), - ); - measure(); - - window.addEventListener('resize', measure); - window.addEventListener('scroll', measure); - - return () => { - window.removeEventListener('resize', measure); - window.removeEventListener('scroll', measure); - }; - } - }, [node]); - - return dimensions; -} - -export default useNodeDimensions; diff --git a/src/theme/main.less b/src/theme/main.less index 15d6923..f5ed3d2 100644 --- a/src/theme/main.less +++ b/src/theme/main.less @@ -12,19 +12,20 @@ --slider-dots-bg: #ececec; } -.embla { +.slider-wrapper { position: relative; overflow: hidden; } -.embla__viewport { +.slider-viewport { + margin-bottom: 24px; } -.embla__container { +.slider-container { display: flex; } -.embla__slide { +.slider-slide { min-width: 0; flex: 0 0 100%; } @@ -52,6 +53,12 @@ &:hover { opacity: 1; } + + &:disabled { + svg { + opacity: 0.3; + } + } } .slider-button-prev { @@ -64,40 +71,15 @@ // } } -.embla__buttons { - position: absolute; - top: 50%; - left: 1.6rem; +.slider-dot { display: flex; + width: 46px; + height: 20px; align-items: center; - transform: translateY(-50%); -} - -.embla__button { - z-index: 1; - display: flex; - width: 4rem; - height: 4rem; - align-items: center; - justify-content: center; - color: var(--background-site); - cursor: pointer; -} - -.embla__button:disabled { - opacity: 0.3; -} - -.embla__button__svg { - width: 65%; - height: 65%; -} - -.embla__dot { - display: inline-flex; padding: 0; border: 0; margin: 0; + margin-right: 13px; -webkit-appearance: none; background-color: transparent; cursor: pointer; @@ -105,28 +87,20 @@ touch-action: manipulation; } -.embla__dots { +.slider-dots { display: flex; align-items: center; justify-content: center; } -.embla__dot { - display: flex; - width: 46px; - height: 20px; - align-items: center; - margin-right: 13px; -} - -.embla__dot:after { +.slider-dot:after { width: 100%; height: 6px; background: var(--slider-dots-bg); content: ''; } -.embla__dot--selected:after { +.slider-dot--selected:after { background: var(--slider-dots-selected-bg); } From df7af2edff5af9981a8188500be98334370f5452 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Wed, 29 Nov 2023 12:22:48 +0100 Subject: [PATCH 07/10] Changelog --- news/37.breaking | 1 + 1 file changed, 1 insertion(+) diff --git a/news/37.breaking b/news/37.breaking index 364027b..f5d476a 100644 --- a/news/37.breaking +++ b/news/37.breaking @@ -1 +1,2 @@ Change the underlying library from react-slick to embla-carousel-react @sneridagh +See Upgrade Guide section in README From 80e801e66b0e6ea112a9976cf7bfa0c4e4fad0fb Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Wed, 29 Nov 2023 12:52:55 +0100 Subject: [PATCH 08/10] Fix linters --- .eslintrc-patch.js | 61 ++++++++++++++++++++++++++++++++++++++ dockerfiles/Dockerfile.dev | 1 + 2 files changed, 62 insertions(+) create mode 100644 .eslintrc-patch.js diff --git a/.eslintrc-patch.js b/.eslintrc-patch.js new file mode 100644 index 0000000..b3c71c1 --- /dev/null +++ b/.eslintrc-patch.js @@ -0,0 +1,61 @@ +const fs = require('fs'); +const path = require('path'); +const projectRootPath = __dirname; +const packageJson = require(path.join(projectRootPath, 'package.json')); + +let voltoPath = './node_modules/@plone/volto'; + +let configFile; +if (fs.existsSync(`${this.projectRootPath}/tsconfig.json`)) + configFile = `${this.projectRootPath}/tsconfig.json`; +else if (fs.existsSync(`${this.projectRootPath}/jsconfig.json`)) + configFile = `${this.projectRootPath}/jsconfig.json`; + +if (configFile) { + const jsConfig = require(configFile).compilerOptions; + const pathsConfig = jsConfig.paths; + if (pathsConfig['@plone/volto']) + voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`; +} + +const AddonConfigurationRegistry = require( + `${voltoPath}/addon-registry.js`, +); +const reg = new AddonConfigurationRegistry(__dirname); + +// Extends ESlint configuration for adding the aliases to `src` directories in Volto addons +const addonAliases = Object.keys(reg.packages).map((o) => [ + o, + reg.packages[o].modulePath, +]); + +const addonExtenders = reg.getEslintExtenders().map((m) => require(m)); + +const defaultConfig = { + extends: `${voltoPath}/.eslintrc`, + settings: { + 'import/resolver': { + alias: { + map: [ + ['@plone/volto', '@plone/volto/src'], + ['@plone/volto-slate', '@plone/volto/packages/volto-slate/src'], + ...addonAliases, + ['@package', `${__dirname}/src`], + ['@root', `${__dirname}/src`], + ['~', `${__dirname}/src`], + ], + extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], + }, + 'babel-plugin-root-import': { + rootPathSuffix: 'src', + }, + }, + }, +}; + +const config = addonExtenders.reduce( + (acc, extender) => extender.modify(acc), + defaultConfig, +); + +module.exports = config; diff --git a/dockerfiles/Dockerfile.dev b/dockerfiles/Dockerfile.dev index b2177be..7911495 100644 --- a/dockerfiles/Dockerfile.dev +++ b/dockerfiles/Dockerfile.dev @@ -9,6 +9,7 @@ COPY --chown=node:node package.json /app/src/addons/${ADDON_PATH}/ # Copy linter / prettier configs COPY --chown=node:node .eslintignore* .prettierignore* /app/ +COPY --chown=node:node .eslintrc-patch.js /app/.eslintrc.js RUN < Date: Wed, 29 Nov 2023 14:18:23 +0100 Subject: [PATCH 09/10] More polishments --- src/index.js | 1 - src/theme/main.less | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/index.js b/src/index.js index 6bb65d2..6f2486b 100644 --- a/src/index.js +++ b/src/index.js @@ -16,7 +16,6 @@ const applyConfig = (config) => { restricted: false, mostUsed: true, sidebarTab: 1, - referenceContainerQuery: 'body.has-sidebar .container .header', dataAdapter: SliderBlockDataAdapter, }; return config; diff --git a/src/theme/main.less b/src/theme/main.less index f5ed3d2..45e8628 100644 --- a/src/theme/main.less +++ b/src/theme/main.less @@ -34,17 +34,17 @@ position: absolute; z-index: 10; width: 50px; - height: calc(var(--slider-container-width) * 9 / 16); + // Since we are forcing the aspect ratio, if we know the slider container width (we do) + // (we are injecting the CSS property in the main slider wrapper) + // we can infer the height of the image, by using: + height: calc(var(--slider-container-width) * 1 / @slider-images-aspect-ratio); padding: 0; border: 0; margin: 0; -webkit-appearance: none; - // Equal to the space we give at the bottom to place the buttons - // height: calc(100% - $spacing-xlarge); background-color: rgba(0, 0, 0, 0.15); color: #fff; cursor: pointer; - // border: 2px solid red; opacity: 0; text-decoration: none; touch-action: manipulation; @@ -66,6 +66,7 @@ .slider-button-next { right: 0; + // In case you want to remove the buttons from the mobile view // @media only screen and (max-width: $computer-width) { // display: none !important; // } From d4334126a203dee506a2b22621070a52f693a9c9 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Wed, 29 Nov 2023 14:26:20 +0100 Subject: [PATCH 10/10] Keep the infinite loop --- src/components/View.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/View.jsx b/src/components/View.jsx index ca76b78..61d9cff 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -34,7 +34,7 @@ const SliderView = (props) => { const [selectedIndex, setSelectedIndex] = useState(0); const [scrollSnaps, setScrollSnaps] = useState([]); - const [emblaRef, emblaApi] = useEmblaCarousel(); + const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true }); const scrollPrev = useCallback(() => { if (emblaApi) {