@salt-ds/embla-carousel 0.1.5 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/css/salt-embla-carousel.css +54 -27
- package/dist-cjs/Carousel.js +30 -9
- package/dist-cjs/Carousel.js.map +1 -1
- package/dist-cjs/CarouselAutoplayIndicator.css.js +1 -1
- package/dist-cjs/CarouselAutoplayIndicator.js.map +1 -1
- package/dist-cjs/CarouselAutoplayIndicatorSVG.js +12 -24
- package/dist-cjs/CarouselAutoplayIndicatorSVG.js.map +1 -1
- package/dist-cjs/CarouselCard.css.js +1 -1
- package/dist-cjs/CarouselCard.js +23 -9
- package/dist-cjs/CarouselCard.js.map +1 -1
- package/dist-cjs/CarouselContext.js.map +1 -1
- package/dist-cjs/CarouselNextButton.js +4 -1
- package/dist-cjs/CarouselNextButton.js.map +1 -1
- package/dist-cjs/CarouselPreviousButton.js +4 -1
- package/dist-cjs/CarouselPreviousButton.js.map +1 -1
- package/dist-cjs/CarouselProgressLabel.js +11 -33
- package/dist-cjs/CarouselProgressLabel.js.map +1 -1
- package/dist-cjs/CarouselSlides.css.js +1 -1
- package/dist-cjs/CarouselSlides.js +148 -25
- package/dist-cjs/CarouselSlides.js.map +1 -1
- package/dist-cjs/CarouselTab.css.js +1 -1
- package/dist-cjs/CarouselTab.js +1 -5
- package/dist-cjs/CarouselTab.js.map +1 -1
- package/dist-cjs/CarouselTabList.css.js +1 -1
- package/dist-cjs/CarouselTabList.js +47 -27
- package/dist-cjs/CarouselTabList.js.map +1 -1
- package/dist-cjs/createCustomSettle.js +1 -1
- package/dist-cjs/createCustomSettle.js.map +1 -1
- package/dist-cjs/getSlideDescription.js +32 -0
- package/dist-cjs/getSlideDescription.js.map +1 -0
- package/dist-cjs/getVisibleSlideDescription.js +26 -0
- package/dist-cjs/getVisibleSlideDescription.js.map +1 -0
- package/dist-cjs/getVisibleSlideIndexes.js +33 -0
- package/dist-cjs/getVisibleSlideIndexes.js.map +1 -0
- package/dist-cjs/index.js +0 -3
- package/dist-cjs/index.js.map +1 -1
- package/dist-cjs/usePrevNextButtons.js +3 -1
- package/dist-cjs/usePrevNextButtons.js.map +1 -1
- package/dist-es/Carousel.js +32 -11
- package/dist-es/Carousel.js.map +1 -1
- package/dist-es/CarouselAutoplayIndicator.css.js +1 -1
- package/dist-es/CarouselAutoplayIndicator.js.map +1 -1
- package/dist-es/CarouselAutoplayIndicatorSVG.js +13 -25
- package/dist-es/CarouselAutoplayIndicatorSVG.js.map +1 -1
- package/dist-es/CarouselCard.css.js +1 -1
- package/dist-es/CarouselCard.js +23 -9
- package/dist-es/CarouselCard.js.map +1 -1
- package/dist-es/CarouselContext.js.map +1 -1
- package/dist-es/CarouselNextButton.js +4 -1
- package/dist-es/CarouselNextButton.js.map +1 -1
- package/dist-es/CarouselPreviousButton.js +4 -1
- package/dist-es/CarouselPreviousButton.js.map +1 -1
- package/dist-es/CarouselProgressLabel.js +12 -34
- package/dist-es/CarouselProgressLabel.js.map +1 -1
- package/dist-es/CarouselSlides.css.js +1 -1
- package/dist-es/CarouselSlides.js +150 -27
- package/dist-es/CarouselSlides.js.map +1 -1
- package/dist-es/CarouselTab.css.js +1 -1
- package/dist-es/CarouselTab.js +1 -5
- package/dist-es/CarouselTab.js.map +1 -1
- package/dist-es/CarouselTabList.css.js +1 -1
- package/dist-es/CarouselTabList.js +48 -28
- package/dist-es/CarouselTabList.js.map +1 -1
- package/dist-es/createCustomSettle.js +1 -1
- package/dist-es/createCustomSettle.js.map +1 -1
- package/dist-es/getSlideDescription.js +30 -0
- package/dist-es/getSlideDescription.js.map +1 -0
- package/dist-es/getVisibleSlideDescription.js +24 -0
- package/dist-es/getVisibleSlideDescription.js.map +1 -0
- package/dist-es/getVisibleSlideIndexes.js +30 -0
- package/dist-es/getVisibleSlideIndexes.js.map +1 -0
- package/dist-es/index.js +0 -1
- package/dist-es/index.js.map +1 -1
- package/dist-es/usePrevNextButtons.js +3 -1
- package/dist-es/usePrevNextButtons.js.map +1 -1
- package/dist-types/Carousel.d.ts +7 -1
- package/dist-types/CarouselContext.d.ts +31 -0
- package/dist-types/CarouselTab.d.ts +0 -6
- package/dist-types/CarouselTabList.d.ts +4 -0
- package/dist-types/getSlideDescription.d.ts +7 -0
- package/dist-types/getVisibleSlideDescription.d.ts +7 -0
- package/dist-types/getVisibleSlideIndexes.d.ts +8 -0
- package/dist-types/index.d.ts +0 -1
- package/package.json +2 -2
- package/dist-cjs/CarouselAnnouncementPlugin.js +0 -52
- package/dist-cjs/CarouselAnnouncementPlugin.js.map +0 -1
- package/dist-cjs/CarouselProgressLabel.css.js +0 -6
- package/dist-cjs/CarouselProgressLabel.css.js.map +0 -1
- package/dist-es/CarouselAnnouncementPlugin.js +0 -49
- package/dist-es/CarouselAnnouncementPlugin.js.map +0 -1
- package/dist-es/CarouselProgressLabel.css.js +0 -4
- package/dist-es/CarouselProgressLabel.css.js.map +0 -1
- package/dist-types/CarouselAnnouncementPlugin.d.ts +0 -24
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useIcon, Button } from '@salt-ds/core';
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
|
+
import { useCarouselContext } from './CarouselContext.js';
|
|
4
5
|
import { usePrevNextButtons } from './usePrevNextButtons.js';
|
|
5
6
|
|
|
6
7
|
const CarouselNextButton = forwardRef(function CarouselNextButton2({ className, onClick, ...rest }, ref) {
|
|
7
8
|
const { NextIcon } = useIcon();
|
|
8
9
|
const { nextBtnDisabled, onNextButtonClick } = usePrevNextButtons();
|
|
10
|
+
const { carouselId } = useCarouselContext();
|
|
9
11
|
const handleClick = (event) => {
|
|
10
12
|
onNextButtonClick();
|
|
11
13
|
onClick == null ? void 0 : onClick(event);
|
|
@@ -16,7 +18,8 @@ const CarouselNextButton = forwardRef(function CarouselNextButton2({ className,
|
|
|
16
18
|
onClick: handleClick,
|
|
17
19
|
disabled: nextBtnDisabled,
|
|
18
20
|
focusableWhenDisabled: true,
|
|
19
|
-
appearance: "
|
|
21
|
+
appearance: "transparent",
|
|
22
|
+
"aria-controls": `${carouselId}-slides`,
|
|
20
23
|
sentiment: "neutral",
|
|
21
24
|
"aria-label": "Next slide",
|
|
22
25
|
ref,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CarouselNextButton.js","sources":["../src/CarouselNextButton.tsx"],"sourcesContent":["import { Button, type ButtonProps, useIcon } from \"@salt-ds/core\";\nimport { forwardRef, type MouseEventHandler } from \"react\";\nimport { usePrevNextButtons } from \"./usePrevNextButtons\";\n\n/**\n * Props for the CarouselNextButton component.\n */\nexport interface CarouselNextButtonProps\n extends Omit<ButtonProps, \"variant\" | \"loading\" | \"loadingAnnouncement\"> {}\n\nexport const CarouselNextButton = forwardRef<\n HTMLButtonElement,\n CarouselNextButtonProps\n>(function CarouselNextButton({ className, onClick, ...rest }, ref) {\n const { NextIcon } = useIcon();\n const { nextBtnDisabled, onNextButtonClick } = usePrevNextButtons();\n\n const handleClick: MouseEventHandler<HTMLButtonElement> = (event) => {\n onNextButtonClick();\n onClick?.(event);\n };\n\n return (\n <Button\n onClick={handleClick}\n disabled={nextBtnDisabled}\n focusableWhenDisabled\n appearance=\"
|
|
1
|
+
{"version":3,"file":"CarouselNextButton.js","sources":["../src/CarouselNextButton.tsx"],"sourcesContent":["import { Button, type ButtonProps, useIcon } from \"@salt-ds/core\";\nimport { forwardRef, type MouseEventHandler } from \"react\";\nimport { useCarouselContext } from \"./CarouselContext\";\nimport { usePrevNextButtons } from \"./usePrevNextButtons\";\n\n/**\n * Props for the CarouselNextButton component.\n */\nexport interface CarouselNextButtonProps\n extends Omit<ButtonProps, \"variant\" | \"loading\" | \"loadingAnnouncement\"> {}\n\nexport const CarouselNextButton = forwardRef<\n HTMLButtonElement,\n CarouselNextButtonProps\n>(function CarouselNextButton({ className, onClick, ...rest }, ref) {\n const { NextIcon } = useIcon();\n const { nextBtnDisabled, onNextButtonClick } = usePrevNextButtons();\n\n const { carouselId } = useCarouselContext();\n\n const handleClick: MouseEventHandler<HTMLButtonElement> = (event) => {\n onNextButtonClick();\n onClick?.(event);\n };\n\n return (\n <Button\n onClick={handleClick}\n disabled={nextBtnDisabled}\n focusableWhenDisabled\n appearance=\"transparent\"\n aria-controls={`${carouselId}-slides`}\n sentiment=\"neutral\"\n aria-label=\"Next slide\"\n ref={ref}\n {...rest}\n >\n <NextIcon aria-hidden />\n </Button>\n );\n});\n"],"names":["CarouselNextButton"],"mappings":";;;;;;AAWO,MAAM,kBAAA,GAAqB,UAAA,CAGhC,SAASA,mBAAAA,CAAmB,EAAE,WAAW,OAAA,EAAS,GAAG,IAAA,EAAK,EAAG,GAAA,EAAK;AAClE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,OAAA,EAAQ;AAC7B,EAAA,MAAM,EAAE,eAAA,EAAiB,iBAAA,EAAkB,GAAI,kBAAA,EAAmB;AAElE,EAAA,MAAM,EAAE,UAAA,EAAW,GAAI,kBAAA,EAAmB;AAE1C,EAAA,MAAM,WAAA,GAAoD,CAAC,KAAA,KAAU;AACnE,IAAA,iBAAA,EAAkB;AAClB,IAAA,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAU,KAAA,CAAA;AAAA,EACZ,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAS,WAAA;AAAA,MACT,QAAA,EAAU,eAAA;AAAA,MACV,qBAAA,EAAqB,IAAA;AAAA,MACrB,UAAA,EAAW,aAAA;AAAA,MACX,eAAA,EAAe,GAAG,UAAU,CAAA,OAAA,CAAA;AAAA,MAC5B,SAAA,EAAU,SAAA;AAAA,MACV,YAAA,EAAW,YAAA;AAAA,MACX,GAAA;AAAA,MACC,GAAG,IAAA;AAAA,MAEJ,QAAA,kBAAA,GAAA,CAAC,QAAA,EAAA,EAAS,aAAA,EAAW,IAAA,EAAC;AAAA;AAAA,GACxB;AAEJ,CAAC;;;;"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useIcon, Button } from '@salt-ds/core';
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
|
+
import { useCarouselContext } from './CarouselContext.js';
|
|
4
5
|
import { usePrevNextButtons } from './usePrevNextButtons.js';
|
|
5
6
|
|
|
6
7
|
const CarouselPreviousButton = forwardRef(function CarouselPreviousButton2({ className, onClick, ...rest }, ref) {
|
|
7
8
|
const { PreviousIcon } = useIcon();
|
|
8
9
|
const { prevBtnDisabled, onPrevButtonClick } = usePrevNextButtons();
|
|
10
|
+
const { carouselId } = useCarouselContext();
|
|
9
11
|
const handleClick = (event) => {
|
|
10
12
|
onPrevButtonClick();
|
|
11
13
|
onClick == null ? void 0 : onClick(event);
|
|
@@ -16,7 +18,8 @@ const CarouselPreviousButton = forwardRef(function CarouselPreviousButton2({ cla
|
|
|
16
18
|
onClick: handleClick,
|
|
17
19
|
disabled: prevBtnDisabled,
|
|
18
20
|
focusableWhenDisabled: true,
|
|
19
|
-
appearance: "
|
|
21
|
+
appearance: "transparent",
|
|
22
|
+
"aria-controls": `${carouselId}-slides`,
|
|
20
23
|
sentiment: "neutral",
|
|
21
24
|
"aria-label": "Previous slide",
|
|
22
25
|
ref,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CarouselPreviousButton.js","sources":["../src/CarouselPreviousButton.tsx"],"sourcesContent":["import { Button, type ButtonProps, useIcon } from \"@salt-ds/core\";\nimport { forwardRef, type MouseEventHandler } from \"react\";\nimport { usePrevNextButtons } from \"./usePrevNextButtons\";\n\n/**\n * Props for the CarouselPreviousButton component.\n */\nexport interface CarouselPreviousButtonProps\n extends Omit<ButtonProps, \"variant\" | \"loading\" | \"loadingAnnouncement\"> {}\n\nexport const CarouselPreviousButton = forwardRef<\n HTMLButtonElement,\n CarouselPreviousButtonProps\n>(function CarouselPreviousButton({ className, onClick, ...rest }, ref) {\n const { PreviousIcon } = useIcon();\n const { prevBtnDisabled, onPrevButtonClick } = usePrevNextButtons();\n\n const handleClick: MouseEventHandler<HTMLButtonElement> = (event) => {\n onPrevButtonClick();\n onClick?.(event);\n };\n\n return (\n <Button\n onClick={handleClick}\n disabled={prevBtnDisabled}\n focusableWhenDisabled\n appearance=\"
|
|
1
|
+
{"version":3,"file":"CarouselPreviousButton.js","sources":["../src/CarouselPreviousButton.tsx"],"sourcesContent":["import { Button, type ButtonProps, useIcon } from \"@salt-ds/core\";\nimport { forwardRef, type MouseEventHandler } from \"react\";\nimport { useCarouselContext } from \"./CarouselContext\";\nimport { usePrevNextButtons } from \"./usePrevNextButtons\";\n\n/**\n * Props for the CarouselPreviousButton component.\n */\nexport interface CarouselPreviousButtonProps\n extends Omit<ButtonProps, \"variant\" | \"loading\" | \"loadingAnnouncement\"> {}\n\nexport const CarouselPreviousButton = forwardRef<\n HTMLButtonElement,\n CarouselPreviousButtonProps\n>(function CarouselPreviousButton({ className, onClick, ...rest }, ref) {\n const { PreviousIcon } = useIcon();\n const { prevBtnDisabled, onPrevButtonClick } = usePrevNextButtons();\n\n const { carouselId } = useCarouselContext();\n\n const handleClick: MouseEventHandler<HTMLButtonElement> = (event) => {\n onPrevButtonClick();\n onClick?.(event);\n };\n\n return (\n <Button\n onClick={handleClick}\n disabled={prevBtnDisabled}\n focusableWhenDisabled\n appearance=\"transparent\"\n aria-controls={`${carouselId}-slides`}\n sentiment=\"neutral\"\n aria-label=\"Previous slide\"\n ref={ref}\n {...rest}\n >\n <PreviousIcon aria-hidden />\n </Button>\n );\n});\n"],"names":["CarouselPreviousButton"],"mappings":";;;;;;AAWO,MAAM,sBAAA,GAAyB,UAAA,CAGpC,SAASA,uBAAAA,CAAuB,EAAE,WAAW,OAAA,EAAS,GAAG,IAAA,EAAK,EAAG,GAAA,EAAK;AACtE,EAAA,MAAM,EAAE,YAAA,EAAa,GAAI,OAAA,EAAQ;AACjC,EAAA,MAAM,EAAE,eAAA,EAAiB,iBAAA,EAAkB,GAAI,kBAAA,EAAmB;AAElE,EAAA,MAAM,EAAE,UAAA,EAAW,GAAI,kBAAA,EAAmB;AAE1C,EAAA,MAAM,WAAA,GAAoD,CAAC,KAAA,KAAU;AACnE,IAAA,iBAAA,EAAkB;AAClB,IAAA,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAU,KAAA,CAAA;AAAA,EACZ,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAS,WAAA;AAAA,MACT,QAAA,EAAU,eAAA;AAAA,MACV,qBAAA,EAAqB,IAAA;AAAA,MACrB,UAAA,EAAW,aAAA;AAAA,MACX,eAAA,EAAe,GAAG,UAAU,CAAA,OAAA,CAAA;AAAA,MAC5B,SAAA,EAAU,SAAA;AAAA,MACV,YAAA,EAAW,gBAAA;AAAA,MACX,GAAA;AAAA,MACC,GAAG,IAAA;AAAA,MAEJ,QAAA,kBAAA,GAAA,CAAC,YAAA,EAAA,EAAa,aAAA,EAAW,IAAA,EAAC;AAAA;AAAA,GAC5B;AAEJ,CAAC;;;;"}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { makePrefixer, Text } from '@salt-ds/core';
|
|
3
|
-
import { useComponentCssInjection } from '@salt-ds/styles';
|
|
4
|
-
import { useWindow } from '@salt-ds/window';
|
|
5
3
|
import { clsx } from 'clsx';
|
|
6
4
|
import { useState, useEffect } from 'react';
|
|
7
5
|
import { useCarouselContext } from './CarouselContext.js';
|
|
8
|
-
import
|
|
6
|
+
import { getVisibleSlideIndexes } from './getVisibleSlideIndexes.js';
|
|
9
7
|
|
|
10
8
|
const withBaseName = makePrefixer("saltCarouselTabList");
|
|
11
9
|
function CarouselProgressLabel({
|
|
@@ -14,35 +12,20 @@ function CarouselProgressLabel({
|
|
|
14
12
|
children,
|
|
15
13
|
...props
|
|
16
14
|
}) {
|
|
17
|
-
const targetWindow = useWindow();
|
|
18
|
-
useComponentCssInjection({
|
|
19
|
-
testId: "salt-carousel-progress-label",
|
|
20
|
-
css: css_248z,
|
|
21
|
-
window: targetWindow
|
|
22
|
-
});
|
|
23
15
|
const { emblaApi } = useCarouselContext();
|
|
24
|
-
const [
|
|
25
|
-
const [totalSlides, setTotalSlides] = useState(0);
|
|
16
|
+
const [progress, setProgress] = useState("");
|
|
26
17
|
useEffect(() => {
|
|
27
18
|
const handleSelect = (emblaApi2) => {
|
|
28
|
-
const
|
|
19
|
+
const selectedScrollSnap = (emblaApi2 == null ? void 0 : emblaApi2.selectedScrollSnap()) ?? 0;
|
|
29
20
|
const numberOfSlides = (emblaApi2 == null ? void 0 : emblaApi2.slideNodes().length) ?? 0;
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
slideIndexInView * slidesPerTransition + 1,
|
|
34
|
-
numberOfSlides - (slidesPerTransition - 1)
|
|
21
|
+
const visibleSlides = getVisibleSlideIndexes(
|
|
22
|
+
emblaApi2,
|
|
23
|
+
selectedScrollSnap
|
|
35
24
|
);
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
);
|
|
40
|
-
if (startSlideNumber === endSlideNumber) {
|
|
41
|
-
setCurrentSlide(startSlideNumber.toString(10));
|
|
42
|
-
} else {
|
|
43
|
-
setCurrentSlide(`${startSlideNumber}-${endSlideNumber}`);
|
|
44
|
-
}
|
|
45
|
-
setTotalSlides(numberOfSlides);
|
|
25
|
+
const startSlideNumber = visibleSlides.length >= 1 ? visibleSlides[0] : 0;
|
|
26
|
+
const endSlideNumber = visibleSlides.length > 1 ? visibleSlides[visibleSlides.length - 1] : 0;
|
|
27
|
+
const slidePosition = endSlideNumber ? `${startSlideNumber}-${endSlideNumber}` : startSlideNumber;
|
|
28
|
+
setProgress(`Slide ${slidePosition} of ${numberOfSlides}`);
|
|
46
29
|
};
|
|
47
30
|
if (!emblaApi) return;
|
|
48
31
|
emblaApi.on("init", handleSelect).on("reInit", handleSelect).on("select", handleSelect);
|
|
@@ -51,12 +34,7 @@ function CarouselProgressLabel({
|
|
|
51
34
|
emblaApi.off("init", handleSelect).off("reInit", handleSelect).off("select", handleSelect);
|
|
52
35
|
};
|
|
53
36
|
}, [emblaApi]);
|
|
54
|
-
return /* @__PURE__ */
|
|
55
|
-
"Slide ",
|
|
56
|
-
currentSlide,
|
|
57
|
-
" of ",
|
|
58
|
-
totalSlides
|
|
59
|
-
] });
|
|
37
|
+
return /* @__PURE__ */ jsx(Text, { className: clsx(withBaseName(), className), ...props, children: progress });
|
|
60
38
|
}
|
|
61
39
|
|
|
62
40
|
export { CarouselProgressLabel };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CarouselProgressLabel.js","sources":["../src/CarouselProgressLabel.tsx"],"sourcesContent":["import { makePrefixer, Text, type TextProps } from \"@salt-ds/core\";\nimport {
|
|
1
|
+
{"version":3,"file":"CarouselProgressLabel.js","sources":["../src/CarouselProgressLabel.tsx"],"sourcesContent":["import { makePrefixer, Text, type TextProps } from \"@salt-ds/core\";\nimport { clsx } from \"clsx\";\nimport type { EmblaCarouselType } from \"embla-carousel\";\nimport { useEffect, useState } from \"react\";\nimport { useCarouselContext } from \"./CarouselContext\";\nimport { getVisibleSlideIndexes } from \"./getVisibleSlideIndexes\";\n\n/**\n * Props for the CarouselProgressLabel component.\n */\nexport interface CarouselProgressLabelProps extends TextProps<\"div\"> {}\n\nconst withBaseName = makePrefixer(\"saltCarouselTabList\");\n\nexport function CarouselProgressLabel({\n className,\n styleAs = \"label\",\n children,\n ...props\n}: CarouselProgressLabelProps) {\n const { emblaApi } = useCarouselContext();\n\n const [progress, setProgress] = useState(\"\");\n\n useEffect(() => {\n const handleSelect = (emblaApi: EmblaCarouselType) => {\n const selectedScrollSnap = emblaApi?.selectedScrollSnap() ?? 0;\n const numberOfSlides = emblaApi?.slideNodes().length ?? 0;\n const visibleSlides = getVisibleSlideIndexes(\n emblaApi,\n selectedScrollSnap,\n );\n const startSlideNumber = visibleSlides.length >= 1 ? visibleSlides[0] : 0;\n const endSlideNumber =\n visibleSlides.length > 1 ? visibleSlides[visibleSlides.length - 1] : 0;\n const slidePosition = endSlideNumber\n ? `${startSlideNumber}-${endSlideNumber}`\n : startSlideNumber;\n setProgress(`Slide ${slidePosition} of ${numberOfSlides}`);\n };\n\n if (!emblaApi) return;\n emblaApi\n .on(\"init\", handleSelect)\n .on(\"reInit\", handleSelect)\n .on(\"select\", handleSelect);\n handleSelect(emblaApi);\n // Cleanup listener on component unmount\n return () => {\n emblaApi\n .off(\"init\", handleSelect)\n .off(\"reInit\", handleSelect)\n .off(\"select\", handleSelect);\n };\n }, [emblaApi]);\n\n return (\n <Text className={clsx(withBaseName(), className)} {...props}>\n {progress}\n </Text>\n );\n}\n"],"names":["emblaApi"],"mappings":";;;;;;;AAYA,MAAM,YAAA,GAAe,aAAa,qBAAqB,CAAA;AAEhD,SAAS,qBAAA,CAAsB;AAAA,EACpC,SAAA;AAAA,EACA,OAAA,GAAU,OAAA;AAAA,EACV,QAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA+B;AAC7B,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,kBAAA,EAAmB;AAExC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,EAAE,CAAA;AAE3C,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,YAAA,GAAe,CAACA,SAAAA,KAAgC;AACpD,MAAA,MAAM,kBAAA,GAAA,CAAqBA,SAAAA,IAAA,IAAA,GAAA,MAAA,GAAAA,SAAAA,CAAU,kBAAA,EAAA,KAAwB,CAAA;AAC7D,MAAA,MAAM,cAAA,GAAA,CAAiBA,SAAAA,IAAA,IAAA,GAAA,MAAA,GAAAA,SAAAA,CAAU,aAAa,MAAA,KAAU,CAAA;AACxD,MAAA,MAAM,aAAA,GAAgB,sBAAA;AAAA,QACpBA,SAAAA;AAAA,QACA;AAAA,OACF;AACA,MAAA,MAAM,mBAAmB,aAAA,CAAc,MAAA,IAAU,CAAA,GAAI,aAAA,CAAc,CAAC,CAAA,GAAI,CAAA;AACxE,MAAA,MAAM,cAAA,GACJ,cAAc,MAAA,GAAS,CAAA,GAAI,cAAc,aAAA,CAAc,MAAA,GAAS,CAAC,CAAA,GAAI,CAAA;AACvE,MAAA,MAAM,gBAAgB,cAAA,GAClB,CAAA,EAAG,gBAAgB,CAAA,CAAA,EAAI,cAAc,CAAA,CAAA,GACrC,gBAAA;AACJ,MAAA,WAAA,CAAY,CAAA,MAAA,EAAS,aAAa,CAAA,IAAA,EAAO,cAAc,CAAA,CAAE,CAAA;AAAA,IAC3D,CAAA;AAEA,IAAA,IAAI,CAAC,QAAA,EAAU;AACf,IAAA,QAAA,CACG,EAAA,CAAG,MAAA,EAAQ,YAAY,CAAA,CACvB,EAAA,CAAG,UAAU,YAAY,CAAA,CACzB,EAAA,CAAG,QAAA,EAAU,YAAY,CAAA;AAC5B,IAAA,YAAA,CAAa,QAAQ,CAAA;AAErB,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,CACG,GAAA,CAAI,MAAA,EAAQ,YAAY,CAAA,CACxB,GAAA,CAAI,UAAU,YAAY,CAAA,CAC1B,GAAA,CAAI,QAAA,EAAU,YAAY,CAAA;AAAA,IAC/B,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,uBACE,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAW,IAAA,CAAK,YAAA,IAAgB,SAAS,CAAA,EAAI,GAAG,KAAA,EACnD,QAAA,EAAA,QAAA,EACH,CAAA;AAEJ;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var css_248z = ".saltCarouselSlides {\n overflow: hidden;\n}\n.saltCarouselSlides-container {\n display: flex;\n touch-action: pan-y pinch-zoom;\n margin-left: calc(var(--carousel-slide-spacing) * -1);\n}\n";
|
|
1
|
+
var css_248z = ".saltCarouselSlides {\n overflow: hidden;\n cursor: var(--salt-cursor-grab);\n}\n\n.saltCarouselSlides-container {\n display: flex;\n touch-action: pan-y pinch-zoom;\n margin-left: calc(var(--carousel-slide-spacing) * -1);\n}\n\n.saltCarouselSlides-sr-only {\n position: absolute;\n height: 1px;\n width: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n\n.saltCarouselSlides-dragging {\n cursor: var(--salt-cursor-grab-active);\n}\n";
|
|
2
2
|
|
|
3
3
|
export { css_248z as default };
|
|
4
4
|
//# sourceMappingURL=CarouselSlides.css.js.map
|
|
@@ -1,79 +1,202 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { makePrefixer, useForkRef } from '@salt-ds/core';
|
|
2
|
+
import { makePrefixer, useForkRef, useAriaAnnouncer } from '@salt-ds/core';
|
|
3
3
|
import { useComponentCssInjection } from '@salt-ds/styles';
|
|
4
4
|
import { useWindow } from '@salt-ds/window';
|
|
5
5
|
import { clsx } from 'clsx';
|
|
6
|
-
import { forwardRef, useRef, useEffect } from 'react';
|
|
6
|
+
import { forwardRef, useRef, useState, useEffect, Children, cloneElement } from 'react';
|
|
7
7
|
import { useCarouselContext } from './CarouselContext.js';
|
|
8
8
|
import css_248z from './CarouselSlides.css.js';
|
|
9
9
|
import { createCustomSettle } from './createCustomSettle.js';
|
|
10
|
+
import { getVisibleSlideDescription } from './getVisibleSlideDescription.js';
|
|
11
|
+
import { getVisibleSlideIndexes } from './getVisibleSlideIndexes.js';
|
|
10
12
|
|
|
13
|
+
const ANNOUNCEMENT_DURATION = 1200;
|
|
11
14
|
const withBaseName = makePrefixer("saltCarouselSlides");
|
|
15
|
+
const announceSlideChangesFrom = [
|
|
16
|
+
"drag",
|
|
17
|
+
"focus",
|
|
18
|
+
"navigation"
|
|
19
|
+
];
|
|
12
20
|
const CarouselSlides = forwardRef(
|
|
13
|
-
function CarouselSlides2({ children, className, onKeyDown, ...rest }, ref) {
|
|
21
|
+
function CarouselSlides2({ children, className, id, onKeyDown, ...rest }, ref) {
|
|
14
22
|
const targetWindow = useWindow();
|
|
15
23
|
useComponentCssInjection({
|
|
16
24
|
testId: "salt-carousel-slides",
|
|
17
25
|
css: css_248z,
|
|
18
26
|
window: targetWindow
|
|
19
27
|
});
|
|
20
|
-
const {
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
const {
|
|
29
|
+
disableSlideAnnouncements,
|
|
30
|
+
announcementState,
|
|
31
|
+
setAnnouncementState,
|
|
32
|
+
emblaApi,
|
|
33
|
+
emblaRef,
|
|
34
|
+
carouselId
|
|
35
|
+
} = useCarouselContext();
|
|
36
|
+
const containerRef = useRef(null);
|
|
37
|
+
const forkedEmblaRef = useForkRef(ref, emblaRef);
|
|
38
|
+
const carouselRef = useForkRef(
|
|
39
|
+
forkedEmblaRef,
|
|
40
|
+
containerRef
|
|
41
|
+
);
|
|
42
|
+
const slideRefs = useRef([]);
|
|
43
|
+
const [focusedSlideIndex, setFocusedSlideIndex] = useState(-1);
|
|
44
|
+
const [dragging, setDragging] = useState(false);
|
|
45
|
+
const focusOnSettle = useRef(false);
|
|
46
|
+
const [stableScrollSnap, setStableScrollSnap] = useState(void 0);
|
|
47
|
+
const visibleSlideIndexes = getVisibleSlideIndexes(
|
|
48
|
+
emblaApi,
|
|
49
|
+
stableScrollSnap ?? 0
|
|
50
|
+
);
|
|
51
|
+
const { announce } = useAriaAnnouncer();
|
|
23
52
|
useEffect(() => {
|
|
24
53
|
const handleSettle = (emblaApi2) => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
);
|
|
34
|
-
if (focusableElements.length > 0) {
|
|
35
|
-
focusableElements[0].focus();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
usingArrowNavigation.current = false;
|
|
54
|
+
const selectedScrollSnap = (emblaApi2 == null ? void 0 : emblaApi2.selectedScrollSnap()) ?? 0;
|
|
55
|
+
setStableScrollSnap(selectedScrollSnap);
|
|
56
|
+
const numberOfSnaps = (emblaApi2 == null ? void 0 : emblaApi2.scrollSnapList().length) ?? 1;
|
|
57
|
+
const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;
|
|
58
|
+
const settledSlideIndex = Math.floor(
|
|
59
|
+
selectedScrollSnap * numberOfSlidesPerSnap
|
|
60
|
+
);
|
|
61
|
+
setFocusedSlideIndex(settledSlideIndex);
|
|
39
62
|
};
|
|
40
63
|
if (!emblaApi) {
|
|
41
64
|
return;
|
|
42
65
|
}
|
|
43
66
|
const scrollCallback = createCustomSettle(handleSettle);
|
|
67
|
+
const pointerDownCallback = () => {
|
|
68
|
+
setAnnouncementState("drag");
|
|
69
|
+
};
|
|
44
70
|
emblaApi.on("scroll", scrollCallback);
|
|
71
|
+
emblaApi.on("pointerDown", pointerDownCallback);
|
|
45
72
|
return () => {
|
|
46
73
|
emblaApi.off("scroll", scrollCallback);
|
|
74
|
+
emblaApi.off("pointerDown", pointerDownCallback);
|
|
47
75
|
};
|
|
48
|
-
}, [emblaApi]);
|
|
76
|
+
}, [emblaApi, setAnnouncementState]);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
var _a;
|
|
79
|
+
if (!focusOnSettle.current || stableScrollSnap === void 0) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
(_a = slideRefs.current[focusedSlideIndex]) == null ? void 0 : _a.focus();
|
|
83
|
+
setAnnouncementState("focus");
|
|
84
|
+
focusOnSettle.current = false;
|
|
85
|
+
}, [stableScrollSnap, focusedSlideIndex, setAnnouncementState]);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
const numberOfSnaps = (emblaApi == null ? void 0 : emblaApi.scrollSnapList().length) ?? 1;
|
|
88
|
+
const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;
|
|
89
|
+
if (focusedSlideIndex >= 0) {
|
|
90
|
+
const nearestScrollSnap = Math.floor(
|
|
91
|
+
focusedSlideIndex / numberOfSlidesPerSnap
|
|
92
|
+
);
|
|
93
|
+
if ((emblaApi == null ? void 0 : emblaApi.selectedScrollSnap()) !== nearestScrollSnap) {
|
|
94
|
+
emblaApi == null ? void 0 : emblaApi.scrollTo(nearestScrollSnap);
|
|
95
|
+
focusOnSettle.current = true;
|
|
96
|
+
}
|
|
97
|
+
} else if (focusedSlideIndex === -1) {
|
|
98
|
+
const initialSnap = emblaApi == null ? void 0 : emblaApi.selectedScrollSnap();
|
|
99
|
+
const initialSlideIndex = initialSnap !== void 0 ? Math.floor(initialSnap * numberOfSlidesPerSnap) : 0;
|
|
100
|
+
setFocusedSlideIndex(initialSlideIndex);
|
|
101
|
+
setStableScrollSnap(initialSnap);
|
|
102
|
+
}
|
|
103
|
+
}, [focusedSlideIndex, emblaApi]);
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (disableSlideAnnouncements === false) {
|
|
106
|
+
setAnnouncementState(void 0);
|
|
107
|
+
}
|
|
108
|
+
}, [disableSlideAnnouncements, setAnnouncementState]);
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
if (stableScrollSnap === void 0 || disableSlideAnnouncements || !announcementState || announceSlideChangesFrom.indexOf(announcementState) === -1) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const announcement = getVisibleSlideDescription(
|
|
114
|
+
emblaApi,
|
|
115
|
+
stableScrollSnap
|
|
116
|
+
);
|
|
117
|
+
announce(announcement, ANNOUNCEMENT_DURATION);
|
|
118
|
+
}, [
|
|
119
|
+
announce,
|
|
120
|
+
announcementState,
|
|
121
|
+
disableSlideAnnouncements,
|
|
122
|
+
stableScrollSnap,
|
|
123
|
+
emblaApi
|
|
124
|
+
]);
|
|
49
125
|
const handleKeyDown = (event) => {
|
|
50
126
|
if (event.repeat) {
|
|
51
127
|
return;
|
|
52
128
|
}
|
|
129
|
+
const numberOfSnaps = (emblaApi == null ? void 0 : emblaApi.scrollSnapList().length) ?? 1;
|
|
130
|
+
const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;
|
|
131
|
+
const currentSnap = Math.floor(focusedSlideIndex / numberOfSlidesPerSnap);
|
|
53
132
|
switch (event.key) {
|
|
54
133
|
case "ArrowLeft": {
|
|
55
134
|
event.preventDefault();
|
|
56
|
-
|
|
57
|
-
|
|
135
|
+
const prevSnap = Math.max(currentSnap - 1, 0);
|
|
136
|
+
const newIndex = prevSnap * numberOfSlidesPerSnap;
|
|
137
|
+
setFocusedSlideIndex(newIndex);
|
|
58
138
|
break;
|
|
59
139
|
}
|
|
60
140
|
case "ArrowRight": {
|
|
61
141
|
event.preventDefault();
|
|
62
|
-
|
|
63
|
-
|
|
142
|
+
const nextSnap = Math.min(currentSnap + 1, numberOfSnaps - 1);
|
|
143
|
+
const newIndex = nextSnap * numberOfSlidesPerSnap;
|
|
144
|
+
setFocusedSlideIndex(newIndex);
|
|
64
145
|
break;
|
|
65
146
|
}
|
|
66
147
|
}
|
|
67
148
|
onKeyDown == null ? void 0 : onKeyDown(event);
|
|
68
149
|
};
|
|
150
|
+
const handleMouseDown = (event) => {
|
|
151
|
+
var _a;
|
|
152
|
+
setDragging(true);
|
|
153
|
+
(_a = rest.onMouseDown) == null ? void 0 : _a.call(rest, event);
|
|
154
|
+
};
|
|
155
|
+
const handleMouseUp = (event) => {
|
|
156
|
+
var _a;
|
|
157
|
+
setDragging(false);
|
|
158
|
+
(_a = rest.onMouseUp) == null ? void 0 : _a.call(rest, event);
|
|
159
|
+
};
|
|
69
160
|
return /* @__PURE__ */ jsx(
|
|
70
161
|
"div",
|
|
71
162
|
{
|
|
72
163
|
onKeyDown: handleKeyDown,
|
|
164
|
+
onMouseDown: handleMouseDown,
|
|
165
|
+
onMouseUp: handleMouseUp,
|
|
73
166
|
ref: carouselRef,
|
|
74
|
-
className: clsx(
|
|
167
|
+
className: clsx(
|
|
168
|
+
withBaseName(),
|
|
169
|
+
{ [withBaseName("dragging")]: dragging },
|
|
170
|
+
className
|
|
171
|
+
),
|
|
75
172
|
...rest,
|
|
76
|
-
children: /* @__PURE__ */ jsx(
|
|
173
|
+
children: /* @__PURE__ */ jsx(
|
|
174
|
+
"div",
|
|
175
|
+
{
|
|
176
|
+
className: withBaseName("container"),
|
|
177
|
+
id: id ?? `${carouselId}-slides`,
|
|
178
|
+
children: Children.map(children, (child, index) => {
|
|
179
|
+
const childElement = child;
|
|
180
|
+
const existingId = childElement.props.id;
|
|
181
|
+
const isFocused = focusedSlideIndex === index;
|
|
182
|
+
const isHidden = !visibleSlideIndexes.includes(index + 1) && !isFocused;
|
|
183
|
+
const element = child;
|
|
184
|
+
return cloneElement(element, {
|
|
185
|
+
"aria-hidden": isHidden,
|
|
186
|
+
id: existingId ?? `${carouselId}-slide${index + 1}`,
|
|
187
|
+
onFocus: (event) => {
|
|
188
|
+
var _a, _b;
|
|
189
|
+
setFocusedSlideIndex(index);
|
|
190
|
+
(_b = (_a = element.props) == null ? void 0 : _a.onFocus) == null ? void 0 : _b.call(_a, event);
|
|
191
|
+
},
|
|
192
|
+
tabIndex: !isHidden ? 0 : -1,
|
|
193
|
+
ref: (el) => {
|
|
194
|
+
slideRefs.current[index] = el;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
)
|
|
77
200
|
}
|
|
78
201
|
);
|
|
79
202
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CarouselSlides.js","sources":["../src/CarouselSlides.tsx"],"sourcesContent":["import { makePrefixer, useForkRef } from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport type { EmblaCarouselType } from \"embla-carousel\";\nimport {\n type ComponentPropsWithoutRef,\n forwardRef,\n type KeyboardEvent,\n useEffect,\n useRef,\n} from \"react\";\nimport { useCarouselContext } from \"./CarouselContext\";\nimport carouselSlidesCss from \"./CarouselSlides.css\";\nimport { createCustomSettle } from \"./createCustomSettle\";\n\n/**\n * Props for the CarouselSlides component.\n */\nexport interface CarouselSlidesProps extends ComponentPropsWithoutRef<\"div\"> {}\n\nconst withBaseName = makePrefixer(\"saltCarouselSlides\");\n\nexport const CarouselSlides = forwardRef<HTMLDivElement, CarouselSlidesProps>(\n function CarouselSlides({ children, className, onKeyDown, ...rest }, ref) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-carousel-slides\",\n css: carouselSlidesCss,\n window: targetWindow,\n });\n const { emblaApi, emblaRef } = useCarouselContext();\n\n const carouselRef = useForkRef<HTMLDivElement>(ref, emblaRef);\n\n const usingArrowNavigation = useRef<boolean>();\n\n useEffect(() => {\n const handleSettle = (emblaApi: EmblaCarouselType) => {\n if (!usingArrowNavigation.current) {\n return;\n }\n const slideIndexInView = emblaApi?.selectedScrollSnap() ?? 0;\n const snappedSlide = emblaApi.slideNodes()[slideIndexInView];\n if (snappedSlide) {\n const focusableElements = snappedSlide.querySelectorAll<HTMLElement>(\n 'a, button, input, textarea, select, [tabindex]:not([tabindex=\"-1\"])',\n );\n if (focusableElements.length > 0) {\n focusableElements[0].focus();\n }\n }\n usingArrowNavigation.current = false;\n };\n\n if (!emblaApi) {\n return;\n }\n const scrollCallback = createCustomSettle(handleSettle);\n emblaApi.on(\"scroll\", scrollCallback);\n // Cleanup listener on component unmount\n return () => {\n emblaApi.off(\"scroll\", scrollCallback);\n };\n }, [emblaApi]);\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.repeat) {\n return;\n }\n switch (event.key) {\n case \"ArrowLeft\": {\n event.preventDefault();\n emblaApi?.scrollPrev();\n usingArrowNavigation.current = true;\n break;\n }\n case \"ArrowRight\": {\n event.preventDefault();\n emblaApi?.scrollNext();\n usingArrowNavigation.current = true;\n break;\n }\n }\n onKeyDown?.(event);\n };\n\n return (\n <div\n onKeyDown={handleKeyDown}\n ref={carouselRef}\n className={clsx(withBaseName(), className)}\n {...rest}\n >\n <div className={withBaseName(\"container\")}>{children}</div>\n </div>\n );\n },\n);\n"],"names":["CarouselSlides","carouselSlidesCss","emblaApi"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CarouselSlides.js","sources":["../src/CarouselSlides.tsx"],"sourcesContent":["import { makePrefixer, useAriaAnnouncer, useForkRef } from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport type { EmblaCarouselType } from \"embla-carousel\";\nimport {\n Children,\n type ComponentPropsWithoutRef,\n cloneElement,\n type FocusEvent,\n forwardRef,\n type KeyboardEvent,\n type MouseEventHandler,\n type ReactElement,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport {\n type CarouselAnnouncementTrigger,\n useCarouselContext,\n} from \"./CarouselContext\";\nimport carouselSlidesCss from \"./CarouselSlides.css\";\nimport { createCustomSettle } from \"./createCustomSettle\";\nimport { getVisibleSlideDescription } from \"./getVisibleSlideDescription\";\nimport { getVisibleSlideIndexes } from \"./getVisibleSlideIndexes\";\n\nconst ANNOUNCEMENT_DURATION = 1200;\n\n/**\n * Props for the CarouselSlides component.\n */\nexport interface CarouselSlidesProps extends ComponentPropsWithoutRef<\"div\"> {}\n\nconst withBaseName = makePrefixer(\"saltCarouselSlides\");\n\nconst announceSlideChangesFrom: CarouselAnnouncementTrigger[] = [\n \"drag\",\n \"focus\",\n \"navigation\",\n];\n\nexport const CarouselSlides = forwardRef<HTMLDivElement, CarouselSlidesProps>(\n function CarouselSlides(\n { children, className, id, onKeyDown, ...rest },\n ref,\n ) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-carousel-slides\",\n css: carouselSlidesCss,\n window: targetWindow,\n });\n const {\n disableSlideAnnouncements,\n announcementState,\n setAnnouncementState,\n emblaApi,\n emblaRef,\n carouselId,\n } = useCarouselContext();\n\n const containerRef = useRef<HTMLDivElement>(null);\n const forkedEmblaRef = useForkRef<HTMLDivElement>(ref, emblaRef);\n const carouselRef = useForkRef<HTMLDivElement>(\n forkedEmblaRef,\n containerRef,\n );\n\n const slideRefs = useRef<(HTMLDivElement | null)[]>([]);\n const [focusedSlideIndex, setFocusedSlideIndex] = useState<number>(-1);\n const [dragging, setDragging] = useState(false);\n const focusOnSettle = useRef<boolean>(false);\n\n const [stableScrollSnap, setStableScrollSnap] = useState<\n number | undefined\n >(undefined);\n\n const visibleSlideIndexes = getVisibleSlideIndexes(\n emblaApi,\n stableScrollSnap ?? 0,\n );\n const { announce } = useAriaAnnouncer();\n\n useEffect(() => {\n const handleSettle = (emblaApi: EmblaCarouselType) => {\n const selectedScrollSnap = emblaApi?.selectedScrollSnap() ?? 0;\n setStableScrollSnap(selectedScrollSnap);\n const numberOfSnaps = emblaApi?.scrollSnapList().length ?? 1;\n const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;\n const settledSlideIndex = Math.floor(\n selectedScrollSnap * numberOfSlidesPerSnap,\n );\n setFocusedSlideIndex(settledSlideIndex);\n };\n\n if (!emblaApi) {\n return;\n }\n\n const scrollCallback = createCustomSettle(handleSettle);\n const pointerDownCallback = () => {\n setAnnouncementState(\"drag\");\n };\n emblaApi.on(\"scroll\", scrollCallback);\n emblaApi.on(\"pointerDown\", pointerDownCallback);\n // Cleanup listener on component unmount\n return () => {\n emblaApi.off(\"scroll\", scrollCallback);\n emblaApi.off(\"pointerDown\", pointerDownCallback);\n };\n }, [emblaApi, setAnnouncementState]);\n\n useEffect(() => {\n if (!focusOnSettle.current || stableScrollSnap === undefined) {\n return;\n }\n slideRefs.current[focusedSlideIndex]?.focus();\n setAnnouncementState(\"focus\");\n focusOnSettle.current = false;\n }, [stableScrollSnap, focusedSlideIndex, setAnnouncementState]);\n\n useEffect(() => {\n const numberOfSnaps = emblaApi?.scrollSnapList().length ?? 1;\n const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;\n if (focusedSlideIndex >= 0) {\n const nearestScrollSnap = Math.floor(\n focusedSlideIndex / numberOfSlidesPerSnap,\n );\n if (emblaApi?.selectedScrollSnap() !== nearestScrollSnap) {\n emblaApi?.scrollTo(nearestScrollSnap);\n focusOnSettle.current = true;\n }\n } else if (focusedSlideIndex === -1) {\n const initialSnap = emblaApi?.selectedScrollSnap();\n const initialSlideIndex =\n initialSnap !== undefined\n ? Math.floor(initialSnap * numberOfSlidesPerSnap)\n : 0;\n setFocusedSlideIndex(initialSlideIndex);\n setStableScrollSnap(initialSnap);\n }\n }, [focusedSlideIndex, emblaApi]);\n\n useEffect(() => {\n if (disableSlideAnnouncements === false) {\n setAnnouncementState(undefined);\n }\n }, [disableSlideAnnouncements, setAnnouncementState]);\n\n useEffect(() => {\n if (\n stableScrollSnap === undefined ||\n disableSlideAnnouncements ||\n !announcementState ||\n announceSlideChangesFrom.indexOf(announcementState) === -1\n ) {\n return;\n }\n const announcement = getVisibleSlideDescription(\n emblaApi,\n stableScrollSnap,\n );\n announce(announcement, ANNOUNCEMENT_DURATION);\n }, [\n announce,\n announcementState,\n disableSlideAnnouncements,\n stableScrollSnap,\n emblaApi,\n ]);\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.repeat) {\n return;\n }\n const numberOfSnaps = emblaApi?.scrollSnapList().length ?? 1;\n const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;\n\n // Find the current snap\n const currentSnap = Math.floor(focusedSlideIndex / numberOfSlidesPerSnap);\n\n switch (event.key) {\n case \"ArrowLeft\": {\n event.preventDefault();\n const prevSnap = Math.max(currentSnap - 1, 0);\n const newIndex = prevSnap * numberOfSlidesPerSnap;\n setFocusedSlideIndex(newIndex);\n break;\n }\n case \"ArrowRight\": {\n event.preventDefault();\n const nextSnap = Math.min(currentSnap + 1, numberOfSnaps - 1);\n const newIndex = nextSnap * numberOfSlidesPerSnap;\n setFocusedSlideIndex(newIndex);\n break;\n }\n }\n onKeyDown?.(event);\n };\n\n const handleMouseDown: MouseEventHandler<HTMLDivElement> = (event) => {\n setDragging(true);\n rest.onMouseDown?.(event);\n };\n const handleMouseUp: MouseEventHandler<HTMLDivElement> = (event) => {\n setDragging(false);\n rest.onMouseUp?.(event);\n };\n\n return (\n <div\n onKeyDown={handleKeyDown}\n onMouseDown={handleMouseDown}\n onMouseUp={handleMouseUp}\n ref={carouselRef}\n className={clsx(\n withBaseName(),\n { [withBaseName(\"dragging\")]: dragging },\n className,\n )}\n {...rest}\n >\n <div\n className={withBaseName(\"container\")}\n id={id ?? `${carouselId}-slides`}\n >\n {Children.map(children, (child, index) => {\n const childElement = child as ReactElement;\n const existingId = childElement.props.id;\n const isFocused = focusedSlideIndex === index;\n const isHidden =\n !visibleSlideIndexes.includes(index + 1) && !isFocused;\n const element = child as ReactElement;\n return cloneElement(element, {\n \"aria-hidden\": isHidden,\n id: existingId ?? `${carouselId}-slide${index + 1}`,\n onFocus: (event: FocusEvent) => {\n setFocusedSlideIndex(index);\n element.props?.onFocus?.(event);\n },\n tabIndex: !isHidden ? 0 : -1,\n ref: (el: HTMLDivElement) => {\n slideRefs.current[index] = el;\n },\n });\n })}\n </div>\n </div>\n );\n },\n);\n"],"names":["CarouselSlides","carouselSlidesCss","emblaApi"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var css_248z = ".saltCarouselTab {\n position: relative;\n width: var(--salt-size-selectable);\n height: var(--salt-size-selectable);\n border: none;\n box-shadow: inset 0 0 0 var(--salt-size-fixed-100) var(--salt-selectable-borderColor);\n -webkit-tap-highlight-color: var(--salt-selectable-borderColor-hover);\n -webkit-appearance: none;\n appearance: none;\n background-color: transparent;\n touch-action: manipulation;\n display: inline-flex;\n text-decoration: none;\n cursor: pointer;\n padding: 0;\n margin: 0;\n align-items: center;\n justify-content: center;\n border-radius: var(--salt-palette-corner-strongest);\n transition: box-shadow var(--salt-duration-perceptible) ease-in-out;\n}\n\n.saltCarouselTab:after {\n content: \"\";\n position: absolute;\n width: var(--salt-size-selectable);\n height: var(--salt-size-selectable);\n border-radius: 50%;\n background-color: transparent;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n}\n\n.saltCarouselTab:focus-visible {\n outline
|
|
1
|
+
var css_248z = ".saltCarouselTab {\n position: relative;\n width: var(--salt-size-selectable);\n height: var(--salt-size-selectable);\n border: none;\n box-shadow: inset 0 0 0 var(--salt-size-fixed-100) var(--salt-selectable-borderColor);\n -webkit-tap-highlight-color: var(--salt-selectable-borderColor-hover);\n -webkit-appearance: none;\n appearance: none;\n background-color: transparent;\n touch-action: manipulation;\n display: inline-flex;\n text-decoration: none;\n cursor: pointer;\n padding: 0;\n margin: 0;\n align-items: center;\n justify-content: center;\n border-radius: var(--salt-palette-corner-strongest);\n transition: box-shadow var(--salt-duration-perceptible) ease-in-out;\n}\n\n.saltCarouselTab:after {\n content: \"\";\n position: absolute;\n width: var(--salt-size-selectable);\n height: var(--salt-size-selectable);\n border-radius: 50%;\n background-color: transparent;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n}\n\n.saltCarouselTab:focus-visible {\n outline: var(--saltRadioButton-outline, var(--salt-focused-outline));\n outline-offset: var(--salt-spacing-fixed-100);\n}\n\n.saltCarouselTab-selected {\n box-shadow: inset 0 0 0 var(--salt-size-selectable) var(--salt-selectable-borderColor-selected);\n}\n";
|
|
2
2
|
|
|
3
3
|
export { css_248z as default };
|
|
4
4
|
//# sourceMappingURL=CarouselTab.css.js.map
|
package/dist-es/CarouselTab.js
CHANGED
|
@@ -10,9 +10,6 @@ const withBaseName = makePrefixer("saltCarouselTab");
|
|
|
10
10
|
const useCarouselTab = (emblaApi) => {
|
|
11
11
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
12
12
|
const [scrollSnaps, setScrollSnaps] = useState([]);
|
|
13
|
-
const handleClick = (index) => {
|
|
14
|
-
emblaApi == null ? void 0 : emblaApi.scrollTo(index);
|
|
15
|
-
};
|
|
16
13
|
useEffect(() => {
|
|
17
14
|
const handleInit = (emblaApi2) => {
|
|
18
15
|
setScrollSnaps(emblaApi2.scrollSnapList());
|
|
@@ -32,8 +29,7 @@ const useCarouselTab = (emblaApi) => {
|
|
|
32
29
|
}, [emblaApi]);
|
|
33
30
|
return {
|
|
34
31
|
selectedIndex,
|
|
35
|
-
scrollSnaps
|
|
36
|
-
onClick: handleClick
|
|
32
|
+
scrollSnaps
|
|
37
33
|
};
|
|
38
34
|
};
|
|
39
35
|
const CarouselTab = forwardRef(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CarouselTab.js","sources":["../src/CarouselTab.tsx"],"sourcesContent":["import { makePrefixer } from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport type { EmblaCarouselType } from \"embla-carousel\";\nimport {\n type ComponentPropsWithRef,\n forwardRef,\n useEffect,\n useState,\n} from \"react\";\nimport carouselTabCss from \"./CarouselTab.css\";\n\n/**\n * Type definition for the UseCarouselTab hook.\n * Provides state and handlers for tablist navigation in a carousel.\n */\ntype UseCarouselTabProps = {\n /**\n * The index of the currently selected slide.\n */\n selectedIndex: number;\n\n /**\n * An array of scroll snap positions for the carousel slides.\n */\n scrollSnaps: number[];\n
|
|
1
|
+
{"version":3,"file":"CarouselTab.js","sources":["../src/CarouselTab.tsx"],"sourcesContent":["import { makePrefixer } from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport type { EmblaCarouselType } from \"embla-carousel\";\nimport {\n type ComponentPropsWithRef,\n forwardRef,\n useEffect,\n useState,\n} from \"react\";\nimport carouselTabCss from \"./CarouselTab.css\";\n\n/**\n * Type definition for the UseCarouselTab hook.\n * Provides state and handlers for tablist navigation in a carousel.\n */\ntype UseCarouselTabProps = {\n /**\n * The index of the currently selected slide.\n */\n selectedIndex: number;\n\n /**\n * An array of scroll snap positions for the carousel slides.\n */\n scrollSnaps: number[];\n};\n\nconst withBaseName = makePrefixer(\"saltCarouselTab\");\n\nexport const useCarouselTab = (\n emblaApi: EmblaCarouselType | undefined,\n): UseCarouselTabProps => {\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [scrollSnaps, setScrollSnaps] = useState<number[]>([]);\n\n useEffect(() => {\n const handleInit = (emblaApi: EmblaCarouselType) => {\n setScrollSnaps(emblaApi.scrollSnapList());\n };\n\n const handleSelect = (emblaApi: EmblaCarouselType) => {\n setSelectedIndex(emblaApi.selectedScrollSnap());\n };\n\n if (!emblaApi) return;\n\n handleInit(emblaApi);\n handleSelect(emblaApi);\n emblaApi\n .on(\"init\", handleInit)\n .on(\"reInit\", handleInit)\n .on(\"select\", handleSelect);\n // Cleanup listener on component unmount\n return () => {\n emblaApi.off(\"init\", handleInit);\n emblaApi.off(\"reInit\", handleInit);\n emblaApi.off(\"select\", handleSelect);\n };\n }, [emblaApi]);\n\n return {\n selectedIndex,\n scrollSnaps,\n };\n};\n\n/**\n * Props for the CarouselTab component.\n */\nexport interface CarouselTabProps extends ComponentPropsWithRef<\"button\"> {\n /**\n * Is the selected slide\n */\n selected?: boolean;\n}\n\nexport const CarouselTab = forwardRef<HTMLButtonElement, CarouselTabProps>(\n function CarouselTab(\n { children, className, selected = false, ...rest },\n ref,\n ) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-carousel-tab\",\n css: carouselTabCss,\n window: targetWindow,\n });\n\n return (\n <button\n className={clsx(\n withBaseName(),\n { [withBaseName(\"selected\")]: selected },\n className,\n )}\n ref={ref}\n {...rest}\n >\n {children}\n </button>\n );\n },\n);\n"],"names":["emblaApi","CarouselTab","carouselTabCss"],"mappings":";;;;;;;;AA6BA,MAAM,YAAA,GAAe,aAAa,iBAAiB,CAAA;AAE5C,MAAM,cAAA,GAAiB,CAC5B,QAAA,KACwB;AACxB,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,CAAC,CAAA;AACpD,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,QAAA,CAAmB,EAAE,CAAA;AAE3D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAA,GAAa,CAACA,SAAAA,KAAgC;AAClD,MAAA,cAAA,CAAeA,SAAAA,CAAS,gBAAgB,CAAA;AAAA,IAC1C,CAAA;AAEA,IAAA,MAAM,YAAA,GAAe,CAACA,SAAAA,KAAgC;AACpD,MAAA,gBAAA,CAAiBA,SAAAA,CAAS,oBAAoB,CAAA;AAAA,IAChD,CAAA;AAEA,IAAA,IAAI,CAAC,QAAA,EAAU;AAEf,IAAA,UAAA,CAAW,QAAQ,CAAA;AACnB,IAAA,YAAA,CAAa,QAAQ,CAAA;AACrB,IAAA,QAAA,CACG,EAAA,CAAG,MAAA,EAAQ,UAAU,CAAA,CACrB,EAAA,CAAG,UAAU,UAAU,CAAA,CACvB,EAAA,CAAG,QAAA,EAAU,YAAY,CAAA;AAE5B,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,CAAS,GAAA,CAAI,QAAQ,UAAU,CAAA;AAC/B,MAAA,QAAA,CAAS,GAAA,CAAI,UAAU,UAAU,CAAA;AACjC,MAAA,QAAA,CAAS,GAAA,CAAI,UAAU,YAAY,CAAA;AAAA,IACrC,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,OAAO;AAAA,IACL,aAAA;AAAA,IACA;AAAA,GACF;AACF;AAYO,MAAM,WAAA,GAAc,UAAA;AAAA,EACzB,SAASC,YAAAA,CACP,EAAE,QAAA,EAAU,SAAA,EAAW,WAAW,KAAA,EAAO,GAAG,IAAA,EAAK,EACjD,GAAA,EACA;AACA,IAAA,MAAM,eAAe,SAAA,EAAU;AAC/B,IAAA,wBAAA,CAAyB;AAAA,MACvB,MAAA,EAAQ,mBAAA;AAAA,MACR,GAAA,EAAKC,QAAA;AAAA,MACL,MAAA,EAAQ;AAAA,KACT,CAAA;AAED,IAAA,uBACE,GAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,IAAA;AAAA,UACT,YAAA,EAAa;AAAA,UACb,EAAE,CAAC,YAAA,CAAa,UAAU,CAAC,GAAG,QAAA,EAAS;AAAA,UACvC;AAAA,SACF;AAAA,QACA,GAAA;AAAA,QACC,GAAG,IAAA;AAAA,QAEH;AAAA;AAAA,KACH;AAAA,EAEJ;AACF;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var css_248z = ".saltCarouselTabList {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n align-items: center;\n gap: var(--salt-spacing-200);\n}\n
|
|
1
|
+
var css_248z = ".saltCarouselTabList {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n align-items: center;\n gap: var(--salt-spacing-200);\n}\n";
|
|
2
2
|
|
|
3
3
|
export { css_248z as default };
|
|
4
4
|
//# sourceMappingURL=CarouselTabList.css.js.map
|