@salt-ds/embla-carousel 0.1.6 → 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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # @salt-ds/embla-carousel
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - da42e9a: Carousel is now a stable API.
8
+
9
+ Features include:
10
+
11
+ - Composable Salt controls for navigation and progress.
12
+ - Composable with custom content within slides.
13
+ - Built on the popular `embla-carousel` library, with Salt-specific accessibility and theming enhancements.
14
+
15
+ Refer to [example](https://saltdesignsystem.com/salt/components/carousel/usage#sequential-with-slide-group) for basic usage.
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [61c2bad]
20
+ - Updated dependencies [fef411f]
21
+ - @salt-ds/core@1.51.0
22
+
3
23
  ## 0.1.6
4
24
 
5
25
  ### Patch Changes
@@ -18,10 +18,6 @@
18
18
  .saltCarouselAutoplayIndicatorSVG {
19
19
  transform: rotate(-90deg);
20
20
  }
21
- .saltCarouselAutoplayIndicatorSVG-track {
22
- fill: none;
23
- stroke: var(--salt-accent-foreground);
24
- }
25
21
  .saltCarouselAutoplayIndicatorSVG-bar {
26
22
  fill: none;
27
23
  stroke: var(--salt-accent-background);
@@ -59,7 +55,18 @@
59
55
  .saltCarouselCard:focus-visible {
60
56
  outline: none;
61
57
  }
62
- .saltCarouselCard:focus-visible::after {
58
+ .saltCarouselCard:not(.saltCarouselCard-bordered):focus-visible::after {
59
+ content: "";
60
+ position: absolute;
61
+ top: 0;
62
+ bottom: 0;
63
+ left: var(--carousel-slide-spacing);
64
+ right: 0;
65
+ outline: var(--salt-focused-outline);
66
+ outline-offset: calc(var(--salt-focused-outlineWidth) * -1);
67
+ cursor: var(--salt-cursor-hover);
68
+ }
69
+ .saltCarouselCard-bordered:focus-visible::after {
63
70
  content: "";
64
71
  position: absolute;
65
72
  top: 0;
@@ -91,7 +98,7 @@
91
98
  .saltCarouselCard-content h3 {
92
99
  margin: 0;
93
100
  }
94
- .saltCarouselCard-bordered {
101
+ .saltCarouselCard-bordered-content {
95
102
  background: var(--salt-container-primary-background);
96
103
  border: var(--salt-size-fixed-100) var(--salt-container-borderStyle) var(--salt-container-primary-borderColor);
97
104
  border-radius: var(--salt-palette-corner, 0);
@@ -172,4 +179,4 @@
172
179
  gap: var(--salt-spacing-200);
173
180
  }
174
181
 
175
- /* src/28b4a5f8-340b-4ae4-bd4b-182f53df6997.css */
182
+ /* src/1a62c25b-2a44-4f89-9546-cfbe130f6c2f.css */
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var css_248z = ".saltCarouselAutoplayIndicator {\n --carousel-svg-circumference: 0;\n display: flex;\n align-self: center;\n}\n\n.saltCarouselAutoplayIndicatorSVG {\n transform: rotate(-90deg);\n}\n\n.saltCarouselAutoplayIndicatorSVG-track {\n fill: none;\n stroke: var(--salt-accent-foreground);\n}\n\n.saltCarouselAutoplayIndicatorSVG-bar {\n fill: none;\n stroke: var(--salt-accent-background);\n stroke-dasharray: var(--carousel-svg-circumference);\n stroke-dashoffset: var(--carousel-svg-circumference);\n animation: indicatorAnimation 0.5s linear;\n animation-play-state: paused;\n}\n\n@keyframes indicatorAnimation {\n from {\n stroke-dashoffset: var(--carousel-svg-circumference);\n }\n to {\n stroke-dashoffset: 0;\n }\n}\n";
3
+ var css_248z = ".saltCarouselAutoplayIndicator {\n --carousel-svg-circumference: 0;\n display: flex;\n align-self: center;\n}\n\n.saltCarouselAutoplayIndicatorSVG {\n transform: rotate(-90deg);\n}\n\n.saltCarouselAutoplayIndicatorSVG-bar {\n fill: none;\n stroke: var(--salt-accent-background);\n stroke-dasharray: var(--carousel-svg-circumference);\n stroke-dashoffset: var(--carousel-svg-circumference);\n animation: indicatorAnimation 0.5s linear;\n animation-play-state: paused;\n}\n\n@keyframes indicatorAnimation {\n from {\n stroke-dashoffset: var(--carousel-svg-circumference);\n }\n to {\n stroke-dashoffset: 0;\n }\n}\n";
4
4
 
5
5
  module.exports = css_248z;
6
6
  //# sourceMappingURL=CarouselAutoplayIndicator.css.js.map
@@ -14,7 +14,7 @@ const CarouselAutoplayIndicatorSVG = react.forwardRef(
14
14
  size,
15
15
  strokeWidth
16
16
  }, ref) => {
17
- return /* @__PURE__ */ jsxRuntime.jsxs(
17
+ return /* @__PURE__ */ jsxRuntime.jsx(
18
18
  "svg",
19
19
  {
20
20
  className: clsx.clsx(withBaseName(), className),
@@ -22,29 +22,17 @@ const CarouselAutoplayIndicatorSVG = react.forwardRef(
22
22
  height: size,
23
23
  "aria-hidden": true,
24
24
  ref,
25
- children: [
26
- /* @__PURE__ */ jsxRuntime.jsx(
27
- "circle",
28
- {
29
- className: withBaseName("track"),
30
- cx: size / 2,
31
- cy: size / 2,
32
- r: radius,
33
- strokeWidth
34
- }
35
- ),
36
- /* @__PURE__ */ jsxRuntime.jsx(
37
- "circle",
38
- {
39
- ref: barRef,
40
- className: withBaseName("bar"),
41
- cx: size / 2,
42
- cy: size / 2,
43
- r: radius,
44
- strokeWidth
45
- }
46
- )
47
- ]
25
+ children: /* @__PURE__ */ jsxRuntime.jsx(
26
+ "circle",
27
+ {
28
+ ref: barRef,
29
+ className: withBaseName("bar"),
30
+ cx: size / 2,
31
+ cy: size / 2,
32
+ r: radius,
33
+ strokeWidth
34
+ }
35
+ )
48
36
  }
49
37
  );
50
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CarouselAutoplayIndicatorSVG.js","sources":["../src/CarouselAutoplayIndicatorSVG.tsx"],"sourcesContent":["import { makePrefixer } from \"@salt-ds/core\";\nimport { clsx } from \"clsx\";\nimport { forwardRef, type Ref, type SVGAttributes } from \"react\";\n\nconst withBaseName = makePrefixer(\"saltCarouselAutoplayIndicatorSVG\");\n\nexport interface CarouselAutoplayIndicatorSVGProps\n extends SVGAttributes<SVGSVGElement> {\n /**\n * Class name to apply to the SVG element.\n */\n className?: string;\n /**\n * Ref to attach to progress indicator element\n */\n barRef: Ref<SVGCircleElement>;\n /**\n * Size of the SVG in pixels.\n */\n size: number;\n /**\n * Stroke width of the progress indicator in pixels.\n */\n strokeWidth: number;\n /**\n * Radius of the progress indicator circle.\n */\n radius: number;\n}\n\nexport const CarouselAutoplayIndicatorSVG = forwardRef<\n SVGSVGElement,\n CarouselAutoplayIndicatorSVGProps\n>(\n (\n {\n barRef,\n className,\n radius,\n size,\n strokeWidth,\n }: CarouselAutoplayIndicatorSVGProps,\n ref,\n ) => {\n return (\n <svg\n className={clsx(withBaseName(), className)}\n width={size}\n height={size}\n aria-hidden\n ref={ref}\n >\n <circle\n className={withBaseName(\"track\")}\n cx={size / 2}\n cy={size / 2}\n r={radius}\n strokeWidth={strokeWidth}\n />\n <circle\n ref={barRef}\n className={withBaseName(\"bar\")}\n cx={size / 2}\n cy={size / 2}\n r={radius}\n strokeWidth={strokeWidth}\n />\n </svg>\n );\n },\n);\n"],"names":["makePrefixer","forwardRef","jsxs","clsx","jsx"],"mappings":";;;;;;;AAIA,MAAM,YAAA,GAAeA,kBAAa,kCAAkC,CAAA;AA0B7D,MAAM,4BAAA,GAA+BC,gBAAA;AAAA,EAI1C,CACE;AAAA,IACE,MAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA;AAAA,IACA;AAAA,KAEF,GAAA,KACG;AACH,IAAA,uBACEC,eAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAWC,SAAA,CAAK,YAAA,EAAa,EAAG,SAAS,CAAA;AAAA,QACzC,KAAA,EAAO,IAAA;AAAA,QACP,MAAA,EAAQ,IAAA;AAAA,QACR,aAAA,EAAW,IAAA;AAAA,QACX,GAAA;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAAC,cAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAW,aAAa,OAAO,CAAA;AAAA,cAC/B,IAAI,IAAA,GAAO,CAAA;AAAA,cACX,IAAI,IAAA,GAAO,CAAA;AAAA,cACX,CAAA,EAAG,MAAA;AAAA,cACH;AAAA;AAAA,WACF;AAAA,0BACAA,cAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,GAAA,EAAK,MAAA;AAAA,cACL,SAAA,EAAW,aAAa,KAAK,CAAA;AAAA,cAC7B,IAAI,IAAA,GAAO,CAAA;AAAA,cACX,IAAI,IAAA,GAAO,CAAA;AAAA,cACX,CAAA,EAAG,MAAA;AAAA,cACH;AAAA;AAAA;AACF;AAAA;AAAA,KACF;AAAA,EAEJ;AACF;;;;"}
1
+ {"version":3,"file":"CarouselAutoplayIndicatorSVG.js","sources":["../src/CarouselAutoplayIndicatorSVG.tsx"],"sourcesContent":["import { makePrefixer } from \"@salt-ds/core\";\nimport { clsx } from \"clsx\";\nimport { forwardRef, type Ref, type SVGAttributes } from \"react\";\n\nconst withBaseName = makePrefixer(\"saltCarouselAutoplayIndicatorSVG\");\n\nexport interface CarouselAutoplayIndicatorSVGProps\n extends SVGAttributes<SVGSVGElement> {\n /**\n * Class name to apply to the SVG element.\n */\n className?: string;\n /**\n * Ref to attach to progress indicator element\n */\n barRef: Ref<SVGCircleElement>;\n /**\n * Size of the SVG in pixels.\n */\n size: number;\n /**\n * Stroke width of the progress indicator in pixels.\n */\n strokeWidth: number;\n /**\n * Radius of the progress indicator circle.\n */\n radius: number;\n}\n\nexport const CarouselAutoplayIndicatorSVG = forwardRef<\n SVGSVGElement,\n CarouselAutoplayIndicatorSVGProps\n>(\n (\n {\n barRef,\n className,\n radius,\n size,\n strokeWidth,\n }: CarouselAutoplayIndicatorSVGProps,\n ref,\n ) => {\n return (\n <svg\n className={clsx(withBaseName(), className)}\n width={size}\n height={size}\n aria-hidden\n ref={ref}\n >\n <circle\n ref={barRef}\n className={withBaseName(\"bar\")}\n cx={size / 2}\n cy={size / 2}\n r={radius}\n strokeWidth={strokeWidth}\n />\n </svg>\n );\n },\n);\n"],"names":["makePrefixer","forwardRef","jsx","clsx"],"mappings":";;;;;;;AAIA,MAAM,YAAA,GAAeA,kBAAa,kCAAkC,CAAA;AA0B7D,MAAM,4BAAA,GAA+BC,gBAAA;AAAA,EAI1C,CACE;AAAA,IACE,MAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA;AAAA,IACA;AAAA,KAEF,GAAA,KACG;AACH,IAAA,uBACEC,cAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAWC,SAAA,CAAK,YAAA,EAAa,EAAG,SAAS,CAAA;AAAA,QACzC,KAAA,EAAO,IAAA;AAAA,QACP,MAAA,EAAQ,IAAA;AAAA,QACR,aAAA,EAAW,IAAA;AAAA,QACX,GAAA;AAAA,QAEA,QAAA,kBAAAD,cAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,MAAA;AAAA,YACL,SAAA,EAAW,aAAa,KAAK,CAAA;AAAA,YAC7B,IAAI,IAAA,GAAO,CAAA;AAAA,YACX,IAAI,IAAA,GAAO,CAAA;AAAA,YACX,CAAA,EAAG,MAAA;AAAA,YACH;AAAA;AAAA;AACF;AAAA,KACF;AAAA,EAEJ;AACF;;;;"}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var css_248z = ".saltCarouselCard {\n display: flex;\n transform: translate3d(0, 0, 0);\n flex: 0 0 var(--carousel-slide-size);\n min-width: 0;\n padding-left: var(--carousel-slide-spacing);\n box-sizing: border-box;\n}\n\n.saltCarouselCard-content {\n display: flex;\n user-select: none;\n overflow: hidden;\n flex-direction: column;\n gap: var(--salt-spacing-200);\n flex-grow: 1;\n}\n\n.saltCarouselCard:focus-visible {\n outline: none;\n}\n\n.saltCarouselCard:focus-visible::after {\n content: \"\";\n position: absolute;\n top: 0;\n bottom: 0;\n left: var(--carousel-slide-spacing);\n right: 0;\n outline: var(--salt-focused-outline);\n outline-offset: calc(var(--salt-focused-outlineWidth) * -1);\n cursor: var(--salt-cursor-hover);\n border-radius: var(--salt-palette-corner, 0);\n}\n\n.saltCarouselCard-body {\n display: flex;\n overflow: hidden;\n flex-direction: column;\n flex-grow: 1;\n padding-bottom: var(--salt-spacing-200);\n}\n\n.saltCarouselCard-header {\n padding-bottom: var(--salt-spacing-100);\n}\n.saltCarouselCard-actions {\n padding-top: var(--salt-spacing-200);\n}\n\n.saltCarouselCard-body.saltCarouselCard-bordered-body {\n padding: 0 var(--salt-spacing-200) var(--salt-spacing-200) var(--salt-spacing-200);\n}\n\n.saltCarouselCard-content h2,\n.saltCarouselCard-content h3 {\n margin: 0;\n}\n\n.saltCarouselCard-bordered {\n background: var(--salt-container-primary-background);\n border: var(--salt-size-fixed-100) var(--salt-container-borderStyle) var(--salt-container-primary-borderColor);\n border-radius: var(--salt-palette-corner, 0);\n}\n";
3
+ var css_248z = ".saltCarouselCard {\n display: flex;\n transform: translate3d(0, 0, 0);\n flex: 0 0 var(--carousel-slide-size);\n min-width: 0;\n padding-left: var(--carousel-slide-spacing);\n box-sizing: border-box;\n}\n\n.saltCarouselCard-content {\n display: flex;\n user-select: none;\n overflow: hidden;\n flex-direction: column;\n gap: var(--salt-spacing-200);\n flex-grow: 1;\n}\n\n.saltCarouselCard:focus-visible {\n outline: none;\n}\n\n.saltCarouselCard:not(.saltCarouselCard-bordered):focus-visible::after {\n content: \"\";\n position: absolute;\n top: 0;\n bottom: 0;\n left: var(--carousel-slide-spacing);\n right: 0;\n outline: var(--salt-focused-outline);\n outline-offset: calc(var(--salt-focused-outlineWidth) * -1);\n cursor: var(--salt-cursor-hover);\n}\n\n.saltCarouselCard-bordered:focus-visible::after {\n content: \"\";\n position: absolute;\n top: 0;\n bottom: 0;\n left: var(--carousel-slide-spacing);\n right: 0;\n outline: var(--salt-focused-outline);\n outline-offset: calc(var(--salt-focused-outlineWidth) * -1);\n cursor: var(--salt-cursor-hover);\n border-radius: var(--salt-palette-corner, 0);\n}\n\n.saltCarouselCard-body {\n display: flex;\n overflow: hidden;\n flex-direction: column;\n flex-grow: 1;\n padding-bottom: var(--salt-spacing-200);\n}\n\n.saltCarouselCard-header {\n padding-bottom: var(--salt-spacing-100);\n}\n.saltCarouselCard-actions {\n padding-top: var(--salt-spacing-200);\n}\n\n.saltCarouselCard-body.saltCarouselCard-bordered-body {\n padding: 0 var(--salt-spacing-200) var(--salt-spacing-200) var(--salt-spacing-200);\n}\n\n.saltCarouselCard-content h2,\n.saltCarouselCard-content h3 {\n margin: 0;\n}\n\n.saltCarouselCard-bordered-content {\n background: var(--salt-container-primary-background);\n border: var(--salt-size-fixed-100) var(--salt-container-borderStyle) var(--salt-container-primary-borderColor);\n border-radius: var(--salt-palette-corner, 0);\n}\n";
4
4
 
5
5
  module.exports = css_248z;
6
6
  //# sourceMappingURL=CarouselCard.css.js.map
@@ -23,7 +23,11 @@ const CarouselCard = react.forwardRef(
23
23
  "div",
24
24
  {
25
25
  "aria-roledescription": "slide",
26
- className: clsx.clsx([withBaseName(), className]),
26
+ className: clsx.clsx([
27
+ withBaseName(),
28
+ { [withBaseName("bordered")]: appearance === "bordered" },
29
+ className
30
+ ]),
27
31
  ref,
28
32
  role: ariaVariant,
29
33
  ...rest,
@@ -31,7 +35,7 @@ const CarouselCard = react.forwardRef(
31
35
  "div",
32
36
  {
33
37
  className: clsx.clsx(withBaseName("content"), {
34
- [withBaseName("bordered")]: appearance === "bordered"
38
+ [withBaseName("bordered-content")]: appearance === "bordered"
35
39
  }),
36
40
  children: [
37
41
  media,
@@ -1 +1 @@
1
- {"version":3,"file":"CarouselCard.js","sources":["../src/CarouselCard.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 ComponentProps, forwardRef, type ReactNode } from \"react\";\nimport saltCarouselCardCss from \"./CarouselCard.css\";\nimport { useCarouselContext } from \"./CarouselContext\";\n\nconst withBaseName = makePrefixer(\"saltCarouselCard\");\n\n/**\n * Props for the CarouselCard component.\n */\nexport interface CarouselCardProps extends ComponentProps<\"div\"> {\n /**\n * Actions to be displayed in the content footer.\n * This can include buttons or any other interactive elements.\n */\n actions?: ReactNode;\n\n /**\n * Media content to be displayed inside the slide.\n * This could include images, videos, etc., that are visually prominent.\n * It differs from children in that media is intended to be the main visual element of the slide.\n */\n media?: ReactNode;\n\n /**\n * The appearance of the slide. Options are 'bordered', and 'transparent'.\n * 'transparent' is the default value.\n **/\n appearance?: \"bordered\" | \"transparent\";\n\n /**\n * Header content to be displayed at the top of the slide.\n * This can be text or any other React node.\n */\n header?: ReactNode;\n\n /**\n * Carousel slide id.\n * This can be used to uniquely identify the slide.\n */\n id?: string;\n}\n\nexport const CarouselCard = forwardRef<HTMLDivElement, CarouselCardProps>(\n function CarouselCard(\n { actions, appearance, children, className, header, media, ...rest },\n ref,\n ) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-carousel-card\",\n css: saltCarouselCardCss,\n window: targetWindow,\n });\n\n const { ariaVariant } = useCarouselContext();\n\n return (\n <div\n aria-roledescription=\"slide\"\n className={clsx([withBaseName(), className])}\n ref={ref}\n role={ariaVariant}\n {...rest}\n >\n <div\n className={clsx(withBaseName(\"content\"), {\n [withBaseName(\"bordered\")]: appearance === \"bordered\",\n })}\n >\n {media}\n {children && (\n <div\n className={clsx(withBaseName(\"body\"), {\n [withBaseName(\"bordered-body\")]: appearance === \"bordered\",\n })}\n >\n {header ? (\n <div className={withBaseName(\"header\")}>{header}</div>\n ) : null}\n {children}\n {actions ? (\n <div className={withBaseName(\"actions\")}>{actions}</div>\n ) : null}\n </div>\n )}\n </div>\n </div>\n );\n },\n);\n"],"names":["makePrefixer","forwardRef","CarouselCard","useWindow","useComponentCssInjection","saltCarouselCardCss","useCarouselContext","jsx","clsx","jsxs"],"mappings":";;;;;;;;;;;AAQA,MAAM,YAAA,GAAeA,kBAAa,kBAAkB,CAAA;AAsC7C,MAAM,YAAA,GAAeC,gBAAA;AAAA,EAC1B,SAASC,aAAAA,CACP,EAAE,OAAA,EAAS,UAAA,EAAY,QAAA,EAAU,SAAA,EAAW,MAAA,EAAQ,KAAA,EAAO,GAAG,IAAA,EAAK,EACnE,GAAA,EACA;AACA,IAAA,MAAM,eAAeC,gBAAA,EAAU;AAC/B,IAAAC,+BAAA,CAAyB;AAAA,MACvB,MAAA,EAAQ,oBAAA;AAAA,MACR,GAAA,EAAKC,cAAA;AAAA,MACL,MAAA,EAAQ;AAAA,KACT,CAAA;AAED,IAAA,MAAM,EAAE,WAAA,EAAY,GAAIC,kCAAA,EAAmB;AAE3C,IAAA,uBACEC,cAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,sBAAA,EAAqB,OAAA;AAAA,QACrB,WAAWC,SAAA,CAAK,CAAC,YAAA,EAAa,EAAG,SAAS,CAAC,CAAA;AAAA,QAC3C,GAAA;AAAA,QACA,IAAA,EAAM,WAAA;AAAA,QACL,GAAG,IAAA;AAAA,QAEJ,QAAA,kBAAAC,eAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAWD,SAAA,CAAK,YAAA,CAAa,SAAS,CAAA,EAAG;AAAA,cACvC,CAAC,YAAA,CAAa,UAAU,CAAC,GAAG,UAAA,KAAe;AAAA,aAC5C,CAAA;AAAA,YAEA,QAAA,EAAA;AAAA,cAAA,KAAA;AAAA,cACA,QAAA,oBACCC,eAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAWD,SAAA,CAAK,YAAA,CAAa,MAAM,CAAA,EAAG;AAAA,oBACpC,CAAC,YAAA,CAAa,eAAe,CAAC,GAAG,UAAA,KAAe;AAAA,mBACjD,CAAA;AAAA,kBAEA,QAAA,EAAA;AAAA,oBAAA,MAAA,kCACE,KAAA,EAAA,EAAI,SAAA,EAAW,aAAa,QAAQ,CAAA,EAAI,kBAAO,CAAA,GAC9C,IAAA;AAAA,oBACH,QAAA;AAAA,oBACA,OAAA,kCACE,KAAA,EAAA,EAAI,SAAA,EAAW,aAAa,SAAS,CAAA,EAAI,mBAAQ,CAAA,GAChD;AAAA;AAAA;AAAA;AACN;AAAA;AAAA;AAEJ;AAAA,KACF;AAAA,EAEJ;AACF;;;;"}
1
+ {"version":3,"file":"CarouselCard.js","sources":["../src/CarouselCard.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 ComponentProps, forwardRef, type ReactNode } from \"react\";\nimport saltCarouselCardCss from \"./CarouselCard.css\";\nimport { useCarouselContext } from \"./CarouselContext\";\n\nconst withBaseName = makePrefixer(\"saltCarouselCard\");\n\n/**\n * Props for the CarouselCard component.\n */\nexport interface CarouselCardProps extends ComponentProps<\"div\"> {\n /**\n * Actions to be displayed in the content footer.\n * This can include buttons or any other interactive elements.\n */\n actions?: ReactNode;\n\n /**\n * Media content to be displayed inside the slide.\n * This could include images, videos, etc., that are visually prominent.\n * It differs from children in that media is intended to be the main visual element of the slide.\n */\n media?: ReactNode;\n\n /**\n * The appearance of the slide. Options are 'bordered', and 'transparent'.\n * 'transparent' is the default value.\n **/\n appearance?: \"bordered\" | \"transparent\";\n\n /**\n * Header content to be displayed at the top of the slide.\n * This can be text or any other React node.\n */\n header?: ReactNode;\n\n /**\n * Carousel slide id.\n * This can be used to uniquely identify the slide.\n */\n id?: string;\n}\n\nexport const CarouselCard = forwardRef<HTMLDivElement, CarouselCardProps>(\n function CarouselCard(\n { actions, appearance, children, className, header, media, ...rest },\n ref,\n ) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-carousel-card\",\n css: saltCarouselCardCss,\n window: targetWindow,\n });\n\n const { ariaVariant } = useCarouselContext();\n\n return (\n <div\n aria-roledescription=\"slide\"\n className={clsx([\n withBaseName(),\n { [withBaseName(\"bordered\")]: appearance === \"bordered\" },\n className,\n ])}\n ref={ref}\n role={ariaVariant}\n {...rest}\n >\n <div\n className={clsx(withBaseName(\"content\"), {\n [withBaseName(\"bordered-content\")]: appearance === \"bordered\",\n })}\n >\n {media}\n {children && (\n <div\n className={clsx(withBaseName(\"body\"), {\n [withBaseName(\"bordered-body\")]: appearance === \"bordered\",\n })}\n >\n {header ? (\n <div className={withBaseName(\"header\")}>{header}</div>\n ) : null}\n {children}\n {actions ? (\n <div className={withBaseName(\"actions\")}>{actions}</div>\n ) : null}\n </div>\n )}\n </div>\n </div>\n );\n },\n);\n"],"names":["makePrefixer","forwardRef","CarouselCard","useWindow","useComponentCssInjection","saltCarouselCardCss","useCarouselContext","jsx","clsx","jsxs"],"mappings":";;;;;;;;;;;AAQA,MAAM,YAAA,GAAeA,kBAAa,kBAAkB,CAAA;AAsC7C,MAAM,YAAA,GAAeC,gBAAA;AAAA,EAC1B,SAASC,aAAAA,CACP,EAAE,OAAA,EAAS,UAAA,EAAY,QAAA,EAAU,SAAA,EAAW,MAAA,EAAQ,KAAA,EAAO,GAAG,IAAA,EAAK,EACnE,GAAA,EACA;AACA,IAAA,MAAM,eAAeC,gBAAA,EAAU;AAC/B,IAAAC,+BAAA,CAAyB;AAAA,MACvB,MAAA,EAAQ,oBAAA;AAAA,MACR,GAAA,EAAKC,cAAA;AAAA,MACL,MAAA,EAAQ;AAAA,KACT,CAAA;AAED,IAAA,MAAM,EAAE,WAAA,EAAY,GAAIC,kCAAA,EAAmB;AAE3C,IAAA,uBACEC,cAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,sBAAA,EAAqB,OAAA;AAAA,QACrB,WAAWC,SAAA,CAAK;AAAA,UACd,YAAA,EAAa;AAAA,UACb,EAAE,CAAC,YAAA,CAAa,UAAU,CAAC,GAAG,eAAe,UAAA,EAAW;AAAA,UACxD;AAAA,SACD,CAAA;AAAA,QACD,GAAA;AAAA,QACA,IAAA,EAAM,WAAA;AAAA,QACL,GAAG,IAAA;AAAA,QAEJ,QAAA,kBAAAC,eAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAWD,SAAA,CAAK,YAAA,CAAa,SAAS,CAAA,EAAG;AAAA,cACvC,CAAC,YAAA,CAAa,kBAAkB,CAAC,GAAG,UAAA,KAAe;AAAA,aACpD,CAAA;AAAA,YAEA,QAAA,EAAA;AAAA,cAAA,KAAA;AAAA,cACA,QAAA,oBACCC,eAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAWD,SAAA,CAAK,YAAA,CAAa,MAAM,CAAA,EAAG;AAAA,oBACpC,CAAC,YAAA,CAAa,eAAe,CAAC,GAAG,UAAA,KAAe;AAAA,mBACjD,CAAA;AAAA,kBAEA,QAAA,EAAA;AAAA,oBAAA,MAAA,kCACE,KAAA,EAAA,EAAI,SAAA,EAAW,aAAa,QAAQ,CAAA,EAAI,kBAAO,CAAA,GAC9C,IAAA;AAAA,oBACH,QAAA;AAAA,oBACA,OAAA,kCACE,KAAA,EAAA,EAAI,SAAA,EAAW,aAAa,SAAS,CAAA,EAAI,mBAAQ,CAAA,GAChD;AAAA;AAAA;AAAA;AACN;AAAA;AAAA;AAEJ;AAAA,KACF;AAAA,EAEJ;AACF;;;;"}
@@ -16,6 +16,7 @@ const ANNOUNCEMENT_DURATION = 1200;
16
16
  const withBaseName = core.makePrefixer("saltCarouselSlides");
17
17
  const announceSlideChangesFrom = [
18
18
  "drag",
19
+ "focus",
19
20
  "navigation"
20
21
  ];
21
22
  const CarouselSlides = react.forwardRef(
@@ -34,10 +35,16 @@ const CarouselSlides = react.forwardRef(
34
35
  emblaRef,
35
36
  carouselId
36
37
  } = CarouselContext.useCarouselContext();
37
- const carouselRef = core.useForkRef(ref, emblaRef);
38
+ const containerRef = react.useRef(null);
39
+ const forkedEmblaRef = core.useForkRef(ref, emblaRef);
40
+ const carouselRef = core.useForkRef(
41
+ forkedEmblaRef,
42
+ containerRef
43
+ );
38
44
  const slideRefs = react.useRef([]);
39
- const [focusSlideIndex, setFocusedSlideIndex] = react.useState(-1);
45
+ const [focusedSlideIndex, setFocusedSlideIndex] = react.useState(-1);
40
46
  const [dragging, setDragging] = react.useState(false);
47
+ const focusOnSettle = react.useRef(false);
41
48
  const [stableScrollSnap, setStableScrollSnap] = react.useState(void 0);
42
49
  const visibleSlideIndexes = getVisibleSlideIndexes.getVisibleSlideIndexes(
43
50
  emblaApi,
@@ -48,6 +55,12 @@ const CarouselSlides = react.forwardRef(
48
55
  const handleSettle = (emblaApi2) => {
49
56
  const selectedScrollSnap = (emblaApi2 == null ? void 0 : emblaApi2.selectedScrollSnap()) ?? 0;
50
57
  setStableScrollSnap(selectedScrollSnap);
58
+ const numberOfSnaps = (emblaApi2 == null ? void 0 : emblaApi2.scrollSnapList().length) ?? 1;
59
+ const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;
60
+ const settledSlideIndex = Math.floor(
61
+ selectedScrollSnap * numberOfSlidesPerSnap
62
+ );
63
+ setFocusedSlideIndex(settledSlideIndex);
51
64
  };
52
65
  if (!emblaApi) {
53
66
  return;
@@ -62,29 +75,39 @@ const CarouselSlides = react.forwardRef(
62
75
  emblaApi.off("scroll", scrollCallback);
63
76
  emblaApi.off("pointerDown", pointerDownCallback);
64
77
  };
65
- }, [emblaApi]);
66
- react.useLayoutEffect(() => {
67
- if (focusSlideIndex >= 0) {
68
- const numberOfSnaps = (emblaApi == null ? void 0 : emblaApi.scrollSnapList().length) ?? 1;
69
- const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;
78
+ }, [emblaApi, setAnnouncementState]);
79
+ react.useEffect(() => {
80
+ var _a;
81
+ if (!focusOnSettle.current || stableScrollSnap === void 0) {
82
+ return;
83
+ }
84
+ (_a = slideRefs.current[focusedSlideIndex]) == null ? void 0 : _a.focus();
85
+ setAnnouncementState("focus");
86
+ focusOnSettle.current = false;
87
+ }, [stableScrollSnap, focusedSlideIndex, setAnnouncementState]);
88
+ react.useEffect(() => {
89
+ const numberOfSnaps = (emblaApi == null ? void 0 : emblaApi.scrollSnapList().length) ?? 1;
90
+ const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;
91
+ if (focusedSlideIndex >= 0) {
70
92
  const nearestScrollSnap = Math.floor(
71
- focusSlideIndex / numberOfSlidesPerSnap
93
+ focusedSlideIndex / numberOfSlidesPerSnap
72
94
  );
73
95
  if ((emblaApi == null ? void 0 : emblaApi.selectedScrollSnap()) !== nearestScrollSnap) {
74
- setAnnouncementState("focus");
75
96
  emblaApi == null ? void 0 : emblaApi.scrollTo(nearestScrollSnap);
97
+ focusOnSettle.current = true;
76
98
  }
77
- setTimeout(() => {
78
- var _a;
79
- (_a = slideRefs.current[focusSlideIndex]) == null ? void 0 : _a.focus();
80
- }, 0);
99
+ } else if (focusedSlideIndex === -1) {
100
+ const initialSnap = emblaApi == null ? void 0 : emblaApi.selectedScrollSnap();
101
+ const initialSlideIndex = initialSnap !== void 0 ? Math.floor(initialSnap * numberOfSlidesPerSnap) : 0;
102
+ setFocusedSlideIndex(initialSlideIndex);
103
+ setStableScrollSnap(initialSnap);
81
104
  }
82
- }, [focusSlideIndex, emblaApi, setAnnouncementState]);
105
+ }, [focusedSlideIndex, emblaApi]);
83
106
  react.useEffect(() => {
84
107
  if (disableSlideAnnouncements === false) {
85
108
  setAnnouncementState(void 0);
86
109
  }
87
- }, [disableSlideAnnouncements]);
110
+ }, [disableSlideAnnouncements, setAnnouncementState]);
88
111
  react.useEffect(() => {
89
112
  if (stableScrollSnap === void 0 || disableSlideAnnouncements || !announcementState || announceSlideChangesFrom.indexOf(announcementState) === -1) {
90
113
  return;
@@ -105,17 +128,22 @@ const CarouselSlides = react.forwardRef(
105
128
  if (event.repeat) {
106
129
  return;
107
130
  }
131
+ const numberOfSnaps = (emblaApi == null ? void 0 : emblaApi.scrollSnapList().length) ?? 1;
132
+ const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;
133
+ const currentSnap = Math.floor(focusedSlideIndex / numberOfSlidesPerSnap);
108
134
  switch (event.key) {
109
135
  case "ArrowLeft": {
110
136
  event.preventDefault();
111
- setFocusedSlideIndex((prevState) => Math.max(prevState - 1, 0));
137
+ const prevSnap = Math.max(currentSnap - 1, 0);
138
+ const newIndex = prevSnap * numberOfSlidesPerSnap;
139
+ setFocusedSlideIndex(newIndex);
112
140
  break;
113
141
  }
114
142
  case "ArrowRight": {
115
143
  event.preventDefault();
116
- setFocusedSlideIndex(
117
- (prevState) => Math.min(prevState + 1, slideRefs.current.length - 1)
118
- );
144
+ const nextSnap = Math.min(currentSnap + 1, numberOfSnaps - 1);
145
+ const newIndex = nextSnap * numberOfSlidesPerSnap;
146
+ setFocusedSlideIndex(newIndex);
119
147
  break;
120
148
  }
121
149
  }
@@ -152,15 +180,14 @@ const CarouselSlides = react.forwardRef(
152
180
  children: react.Children.map(children, (child, index) => {
153
181
  const childElement = child;
154
182
  const existingId = childElement.props.id;
155
- const isHidden = !visibleSlideIndexes.includes(index + 1);
183
+ const isFocused = focusedSlideIndex === index;
184
+ const isHidden = !visibleSlideIndexes.includes(index + 1) && !isFocused;
156
185
  const element = child;
157
186
  return react.cloneElement(element, {
158
187
  "aria-hidden": isHidden,
159
188
  id: existingId ?? `${carouselId}-slide${index + 1}`,
160
- onMouseDown: (event) => event.preventDefault(),
161
189
  onFocus: (event) => {
162
190
  var _a, _b;
163
- event.preventDefault();
164
191
  setFocusedSlideIndex(index);
165
192
  (_b = (_a = element.props) == null ? void 0 : _a.onFocus) == null ? void 0 : _b.call(_a, event);
166
193
  },
@@ -1 +1 @@
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 forwardRef,\n type KeyboardEvent,\n type MouseEventHandler,\n type SyntheticEvent,\n useEffect,\n useLayoutEffect,\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 \"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 carouselRef = useForkRef<HTMLDivElement>(ref, emblaRef);\n\n const slideRefs = useRef<(HTMLDivElement | null)[]>([]);\n const [focusSlideIndex, setFocusedSlideIndex] = useState<number>(-1);\n const [dragging, setDragging] = useState(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 };\n\n if (!emblaApi) {\n return;\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]);\n\n useLayoutEffect(() => {\n if (focusSlideIndex >= 0) {\n const numberOfSnaps = emblaApi?.scrollSnapList().length ?? 1;\n const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;\n const nearestScrollSnap = Math.floor(\n focusSlideIndex / numberOfSlidesPerSnap,\n );\n if (emblaApi?.selectedScrollSnap() !== nearestScrollSnap) {\n setAnnouncementState(\"focus\");\n emblaApi?.scrollTo(nearestScrollSnap);\n }\n setTimeout(() => {\n slideRefs.current[focusSlideIndex]?.focus();\n }, 0);\n }\n }, [focusSlideIndex, emblaApi, setAnnouncementState]);\n\n useEffect(() => {\n if (disableSlideAnnouncements === false) {\n setAnnouncementState(undefined);\n }\n }, [disableSlideAnnouncements]);\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 switch (event.key) {\n case \"ArrowLeft\": {\n event.preventDefault();\n setFocusedSlideIndex((prevState) => Math.max(prevState - 1, 0));\n break;\n }\n case \"ArrowRight\": {\n event.preventDefault();\n setFocusedSlideIndex((prevState) =>\n Math.min(prevState + 1, slideRefs.current.length - 1),\n );\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 React.ReactElement;\n const existingId = childElement.props.id;\n const isHidden = !visibleSlideIndexes.includes(index + 1);\n const element = child as React.ReactElement;\n return cloneElement(element, {\n \"aria-hidden\": isHidden,\n id: existingId ?? `${carouselId}-slide${index + 1}`,\n onMouseDown: (event: SyntheticEvent) => event.preventDefault(),\n onFocus: (event: FocusEvent) => {\n event.preventDefault();\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":["makePrefixer","forwardRef","CarouselSlides","useWindow","useComponentCssInjection","carouselSlidesCss","useCarouselContext","useForkRef","useRef","useState","getVisibleSlideIndexes","useAriaAnnouncer","useEffect","emblaApi","createCustomSettle","useLayoutEffect","getVisibleSlideDescription","jsx","clsx","Children","cloneElement"],"mappings":";;;;;;;;;;;;;;AA2BA,MAAM,qBAAA,GAAwB,IAAA;AAO9B,MAAM,YAAA,GAAeA,kBAAa,oBAAoB,CAAA;AAEtD,MAAM,wBAAA,GAA0D;AAAA,EAC9D,MAAA;AAAA,EACA;AACF,CAAA;AAEO,MAAM,cAAA,GAAiBC,gBAAA;AAAA,EAC5B,SAASC,eAAAA,CACP,EAAE,QAAA,EAAU,SAAA,EAAW,IAAI,SAAA,EAAW,GAAG,IAAA,EAAK,EAC9C,GAAA,EACA;AACA,IAAA,MAAM,eAAeC,gBAAA,EAAU;AAC/B,IAAAC,+BAAA,CAAyB;AAAA,MACvB,MAAA,EAAQ,sBAAA;AAAA,MACR,GAAA,EAAKC,gBAAA;AAAA,MACL,MAAA,EAAQ;AAAA,KACT,CAAA;AACD,IAAA,MAAM;AAAA,MACJ,yBAAA;AAAA,MACA,iBAAA;AAAA,MACA,oBAAA;AAAA,MACA,QAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,QACEC,kCAAA,EAAmB;AAEvB,IAAA,MAAM,WAAA,GAAcC,eAAA,CAA2B,GAAA,EAAK,QAAQ,CAAA;AAE5D,IAAA,MAAM,SAAA,GAAYC,YAAA,CAAkC,EAAE,CAAA;AACtD,IAAA,MAAM,CAAC,eAAA,EAAiB,oBAAoB,CAAA,GAAIC,eAAiB,EAAE,CAAA;AACnE,IAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,eAAS,KAAK,CAAA;AAE9C,IAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAIA,eAE9C,MAAS,CAAA;AAEX,IAAA,MAAM,mBAAA,GAAsBC,6CAAA;AAAA,MAC1B,QAAA;AAAA,MACA,gBAAA,IAAoB;AAAA,KACtB;AACA,IAAA,MAAM,EAAE,QAAA,EAAS,GAAIC,qBAAA,EAAiB;AAEtC,IAAAC,eAAA,CAAU,MAAM;AACd,MAAA,MAAM,YAAA,GAAe,CAACC,SAAAA,KAAgC;AACpD,QAAA,MAAM,kBAAA,GAAA,CAAqBA,SAAAA,IAAA,IAAA,GAAA,MAAA,GAAAA,SAAAA,CAAU,kBAAA,EAAA,KAAwB,CAAA;AAC7D,QAAA,mBAAA,CAAoB,kBAAkB,CAAA;AAAA,MACxC,CAAA;AAEA,MAAA,IAAI,CAAC,QAAA,EAAU;AACb,QAAA;AAAA,MACF;AACA,MAAA,MAAM,cAAA,GAAiBC,sCAAmB,YAAY,CAAA;AACtD,MAAA,MAAM,sBAAsB,MAAM;AAChC,QAAA,oBAAA,CAAqB,MAAM,CAAA;AAAA,MAC7B,CAAA;AACA,MAAA,QAAA,CAAS,EAAA,CAAG,UAAU,cAAc,CAAA;AACpC,MAAA,QAAA,CAAS,EAAA,CAAG,eAAe,mBAAmB,CAAA;AAE9C,MAAA,OAAO,MAAM;AACX,QAAA,QAAA,CAAS,GAAA,CAAI,UAAU,cAAc,CAAA;AACrC,QAAA,QAAA,CAAS,GAAA,CAAI,eAAe,mBAAmB,CAAA;AAAA,MACjD,CAAA;AAAA,IACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,IAAAC,qBAAA,CAAgB,MAAM;AACpB,MAAA,IAAI,mBAAmB,CAAA,EAAG;AACxB,QAAA,MAAM,aAAA,GAAA,CAAgB,QAAA,IAAA,IAAA,GAAA,MAAA,GAAA,QAAA,CAAU,cAAA,EAAA,CAAiB,MAAA,KAAU,CAAA;AAC3D,QAAA,MAAM,qBAAA,GAAwB,SAAA,CAAU,OAAA,CAAQ,MAAA,GAAS,aAAA;AACzD,QAAA,MAAM,oBAAoB,IAAA,CAAK,KAAA;AAAA,UAC7B,eAAA,GAAkB;AAAA,SACpB;AACA,QAAA,IAAA,CAAI,QAAA,IAAA,IAAA,GAAA,MAAA,GAAA,QAAA,CAAU,0BAAyB,iBAAA,EAAmB;AACxD,UAAA,oBAAA,CAAqB,OAAO,CAAA;AAC5B,UAAA,QAAA,IAAA,IAAA,GAAA,MAAA,GAAA,QAAA,CAAU,QAAA,CAAS,iBAAA,CAAA;AAAA,QACrB;AACA,QAAA,UAAA,CAAW,MAAM;AA9GzB,UAAA,IAAA,EAAA;AA+GU,UAAA,CAAA,EAAA,GAAA,SAAA,CAAU,OAAA,CAAQ,eAAe,CAAA,KAAjC,IAAA,GAAA,MAAA,GAAA,EAAA,CAAoC,KAAA,EAAA;AAAA,QACtC,GAAG,CAAC,CAAA;AAAA,MACN;AAAA,IACF,CAAA,EAAG,CAAC,eAAA,EAAiB,QAAA,EAAU,oBAAoB,CAAC,CAAA;AAEpD,IAAAH,eAAA,CAAU,MAAM;AACd,MAAA,IAAI,8BAA8B,KAAA,EAAO;AACvC,QAAA,oBAAA,CAAqB,MAAS,CAAA;AAAA,MAChC;AAAA,IACF,CAAA,EAAG,CAAC,yBAAyB,CAAC,CAAA;AAE9B,IAAAA,eAAA,CAAU,MAAM;AACd,MAAA,IACE,gBAAA,KAAqB,UACrB,yBAAA,IACA,CAAC,qBACD,wBAAA,CAAyB,OAAA,CAAQ,iBAAiB,CAAA,KAAM,EAAA,EACxD;AACA,QAAA;AAAA,MACF;AACA,MAAA,MAAM,YAAA,GAAeI,qDAAA;AAAA,QACnB,QAAA;AAAA,QACA;AAAA,OACF;AACA,MAAA,QAAA,CAAS,cAAc,qBAAqB,CAAA;AAAA,IAC9C,CAAA,EAAG;AAAA,MACD,QAAA;AAAA,MACA,iBAAA;AAAA,MACA,yBAAA;AAAA,MACA,gBAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,MAAM,aAAA,GAAgB,CAAC,KAAA,KAAyC;AAC9D,MAAA,IAAI,MAAM,MAAA,EAAQ;AAChB,QAAA;AAAA,MACF;AACA,MAAA,QAAQ,MAAM,GAAA;AAAK,QACjB,KAAK,WAAA,EAAa;AAChB,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,oBAAA,CAAqB,CAAC,SAAA,KAAc,IAAA,CAAK,IAAI,SAAA,GAAY,CAAA,EAAG,CAAC,CAAC,CAAA;AAC9D,UAAA;AAAA,QACF;AAAA,QACA,KAAK,YAAA,EAAc;AACjB,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,oBAAA;AAAA,YAAqB,CAAC,cACpB,IAAA,CAAK,GAAA,CAAI,YAAY,CAAA,EAAG,SAAA,CAAU,OAAA,CAAQ,MAAA,GAAS,CAAC;AAAA,WACtD;AACA,UAAA;AAAA,QACF;AAAA;AAEF,MAAA,SAAA,IAAA,IAAA,GAAA,MAAA,GAAA,SAAA,CAAY,KAAA,CAAA;AAAA,IACd,CAAA;AAEA,IAAA,MAAM,eAAA,GAAqD,CAAC,KAAA,KAAU;AArK1E,MAAA,IAAA,EAAA;AAsKM,MAAA,WAAA,CAAY,IAAI,CAAA;AAChB,MAAA,CAAA,EAAA,GAAA,IAAA,CAAK,gBAAL,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,IAAA,EAAmB,KAAA,CAAA;AAAA,IACrB,CAAA;AACA,IAAA,MAAM,aAAA,GAAmD,CAAC,KAAA,KAAU;AAzKxE,MAAA,IAAA,EAAA;AA0KM,MAAA,WAAA,CAAY,KAAK,CAAA;AACjB,MAAA,CAAA,EAAA,GAAA,IAAA,CAAK,cAAL,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,IAAA,EAAiB,KAAA,CAAA;AAAA,IACnB,CAAA;AAEA,IAAA,uBACEC,cAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,aAAA;AAAA,QACX,WAAA,EAAa,eAAA;AAAA,QACb,SAAA,EAAW,aAAA;AAAA,QACX,GAAA,EAAK,WAAA;AAAA,QACL,SAAA,EAAWC,SAAA;AAAA,UACT,YAAA,EAAa;AAAA,UACb,EAAE,CAAC,YAAA,CAAa,UAAU,CAAC,GAAG,QAAA,EAAS;AAAA,UACvC;AAAA,SACF;AAAA,QACC,GAAG,IAAA;AAAA,QAEJ,QAAA,kBAAAD,cAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAW,aAAa,WAAW,CAAA;AAAA,YACnC,EAAA,EAAI,EAAA,IAAM,CAAA,EAAG,UAAU,CAAA,OAAA,CAAA;AAAA,YAEtB,QAAA,EAAAE,cAAA,CAAS,GAAA,CAAI,QAAA,EAAU,CAAC,OAAO,KAAA,KAAU;AACxC,cAAA,MAAM,YAAA,GAAe,KAAA;AACrB,cAAA,MAAM,UAAA,GAAa,aAAa,KAAA,CAAM,EAAA;AACtC,cAAA,MAAM,QAAA,GAAW,CAAC,mBAAA,CAAoB,QAAA,CAAS,QAAQ,CAAC,CAAA;AACxD,cAAA,MAAM,OAAA,GAAU,KAAA;AAChB,cAAA,OAAOC,mBAAa,OAAA,EAAS;AAAA,gBAC3B,aAAA,EAAe,QAAA;AAAA,gBACf,IAAI,UAAA,IAAc,CAAA,EAAG,UAAU,CAAA,MAAA,EAAS,QAAQ,CAAC,CAAA,CAAA;AAAA,gBACjD,WAAA,EAAa,CAAC,KAAA,KAA0B,KAAA,CAAM,cAAA,EAAe;AAAA,gBAC7D,OAAA,EAAS,CAAC,KAAA,KAAsB;AAxM9C,kBAAA,IAAA,EAAA,EAAA,EAAA;AAyMgB,kBAAA,KAAA,CAAM,cAAA,EAAe;AACrB,kBAAA,oBAAA,CAAqB,KAAK,CAAA;AAC1B,kBAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,OAAA,CAAQ,KAAA,KAAR,IAAA,GAAA,MAAA,GAAA,EAAA,CAAe,OAAA,KAAf,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAAyB,KAAA,CAAA;AAAA,gBAC3B,CAAA;AAAA,gBACA,QAAA,EAAU,CAAC,QAAA,GAAW,CAAA,GAAI,EAAA;AAAA,gBAC1B,GAAA,EAAK,CAAC,EAAA,KAAuB;AAC3B,kBAAA,SAAA,CAAU,OAAA,CAAQ,KAAK,CAAA,GAAI,EAAA;AAAA,gBAC7B;AAAA,eACD,CAAA;AAAA,YACH,CAAC;AAAA;AAAA;AACH;AAAA,KACF;AAAA,EAEJ;AACF;;;;"}
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":["makePrefixer","forwardRef","CarouselSlides","useWindow","useComponentCssInjection","carouselSlidesCss","useCarouselContext","useRef","useForkRef","useState","getVisibleSlideIndexes","useAriaAnnouncer","useEffect","emblaApi","createCustomSettle","getVisibleSlideDescription","jsx","clsx","Children","cloneElement"],"mappings":"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"}
@@ -32,11 +32,17 @@ const CarouselTabList = react.forwardRef(
32
32
  var _a;
33
33
  let newIndex = focusedTabIndex ?? selectedIndex;
34
34
  if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
35
+ event.preventDefault();
35
36
  const direction = event.key === "ArrowLeft" ? -1 : 1;
36
- newIndex = (newIndex + direction + scrollSnaps.length) % scrollSnaps.length;
37
+ newIndex = Math.max(
38
+ 0,
39
+ Math.min(newIndex + direction, scrollSnaps.length - 1)
40
+ );
37
41
  } else if (event.key === "Home") {
42
+ event.preventDefault();
38
43
  newIndex = 0;
39
44
  } else if (event.key === "End") {
45
+ event.preventDefault();
40
46
  newIndex = scrollSnaps.length - 1;
41
47
  }
42
48
  if (newIndex !== focusedTabIndex) {
@@ -1 +1 @@
1
- {"version":3,"file":"CarouselTabList.js","sources":["../src/CarouselTabList.tsx"],"sourcesContent":["import { makePrefixer, type RenderPropsType, renderProps } from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport {\n forwardRef,\n type HTMLAttributes,\n type KeyboardEventHandler,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { useCarouselContext } from \"./CarouselContext\";\nimport {\n CarouselTab,\n type CarouselTabProps,\n useCarouselTab,\n} from \"./CarouselTab\";\nimport carouselControlsCss from \"./CarouselTabList.css\";\nimport { getSlideDescription } from \"./getSlideDescription\";\nimport { getVisibleSlideIndexes } from \"./getVisibleSlideIndexes\";\n\nconst withBaseName = makePrefixer(\"saltCarouselTabList\");\n\n/**\n * Props for the CarouselTabList component.\n */\nexport interface CarouselTabListProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Render prop to enable customisation of tab button.\n */\n render?: RenderPropsType[\"render\"];\n}\n\nexport interface CarouselTabRendererProps extends CarouselTabProps {\n render?: CarouselTabListProps[\"render\"];\n}\n\nconst CarouselTabRenderer = forwardRef<\n HTMLButtonElement,\n CarouselTabRendererProps\n>((props, ref) => {\n return renderProps(CarouselTab, { ...props, ref });\n});\n\nexport const CarouselTabList = forwardRef<HTMLDivElement, CarouselTabListProps>(\n function CarouselTabList({ className, render, onKeyDown, ...rest }, ref) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-carousel-controls\",\n css: carouselControlsCss,\n window: targetWindow,\n });\n\n const { emblaApi, setAriaVariant, setAnnouncementState } =\n useCarouselContext();\n const { selectedIndex, scrollSnaps } = useCarouselTab(emblaApi);\n\n const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\n const [focusedTabIndex, setFocusedTabIndex] = useState<number | null>(null);\n\n const handleKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {\n let newIndex = focusedTabIndex ?? selectedIndex;\n\n if (event.key === \"ArrowLeft\" || event.key === \"ArrowRight\") {\n const direction = event.key === \"ArrowLeft\" ? -1 : 1;\n newIndex =\n (newIndex + direction + scrollSnaps.length) % scrollSnaps.length;\n } else if (event.key === \"Home\") {\n newIndex = 0;\n } else if (event.key === \"End\") {\n newIndex = scrollSnaps.length - 1;\n }\n\n if (newIndex !== focusedTabIndex) {\n buttonRefs.current[newIndex]?.focus();\n setFocusedTabIndex(newIndex);\n }\n onKeyDown?.(event);\n };\n\n useEffect(() => {\n setAriaVariant(\"tabpanel\");\n }, [setAriaVariant]);\n\n return (\n <div\n role=\"tablist\"\n className={clsx(withBaseName(), className)}\n onKeyDown={handleKeyDown}\n ref={ref}\n {...rest}\n >\n {scrollSnaps.map((_, scrollSnapIndex) => {\n const visibleSlides = getVisibleSlideIndexes(\n emblaApi,\n scrollSnapIndex,\n );\n const startSlideNumber =\n visibleSlides.length >= 1 ? visibleSlides[0] : 0;\n const endSlideNumber =\n visibleSlides.length > 1\n ? visibleSlides[visibleSlides.length - 1]\n : 0;\n const slideNodes = emblaApi?.slideNodes();\n const numberOfSlides = slideNodes?.length;\n\n let ariaLabel: string;\n if (endSlideNumber >= 1) {\n ariaLabel = `Slide ${startSlideNumber} to ${endSlideNumber} of ${numberOfSlides}`;\n } else {\n const description = getSlideDescription(emblaApi, startSlideNumber);\n ariaLabel = `${description}`;\n }\n\n const selected = selectedIndex === scrollSnapIndex;\n const ariaControls = slideNodes?.length\n ? slideNodes[startSlideNumber - 1].id\n : undefined;\n\n return (\n <CarouselTabRenderer\n key={`carouselTab-${scrollSnapIndex}}`}\n ref={(element: HTMLButtonElement) => {\n buttonRefs.current[scrollSnapIndex] = element;\n }}\n render={render}\n role={\"tab\"}\n selected={selected}\n onBlur={() => {\n setFocusedTabIndex(null);\n }}\n onFocus={() => {\n setFocusedTabIndex(scrollSnapIndex);\n setAnnouncementState(\"tab\");\n emblaApi?.scrollTo(scrollSnapIndex);\n }}\n aria-label={ariaLabel}\n aria-selected={selected}\n tabIndex={selected && focusedTabIndex === null ? 0 : -1}\n aria-controls={ariaControls}\n />\n );\n })}\n </div>\n );\n },\n);\n"],"names":["makePrefixer","forwardRef","renderProps","CarouselTab","CarouselTabList","useWindow","useComponentCssInjection","carouselControlsCss","useCarouselContext","useCarouselTab","useRef","useState","useEffect","jsx","clsx","getVisibleSlideIndexes","getSlideDescription"],"mappings":"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"}
1
+ {"version":3,"file":"CarouselTabList.js","sources":["../src/CarouselTabList.tsx"],"sourcesContent":["import { makePrefixer, type RenderPropsType, renderProps } from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport {\n forwardRef,\n type HTMLAttributes,\n type KeyboardEventHandler,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { useCarouselContext } from \"./CarouselContext\";\nimport {\n CarouselTab,\n type CarouselTabProps,\n useCarouselTab,\n} from \"./CarouselTab\";\nimport carouselControlsCss from \"./CarouselTabList.css\";\nimport { getSlideDescription } from \"./getSlideDescription\";\nimport { getVisibleSlideIndexes } from \"./getVisibleSlideIndexes\";\n\nconst withBaseName = makePrefixer(\"saltCarouselTabList\");\n\n/**\n * Props for the CarouselTabList component.\n */\nexport interface CarouselTabListProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Render prop to enable customisation of tab button.\n */\n render?: RenderPropsType[\"render\"];\n}\n\nexport interface CarouselTabRendererProps extends CarouselTabProps {\n render?: CarouselTabListProps[\"render\"];\n}\n\nconst CarouselTabRenderer = forwardRef<\n HTMLButtonElement,\n CarouselTabRendererProps\n>((props, ref) => {\n return renderProps(CarouselTab, { ...props, ref });\n});\n\nexport const CarouselTabList = forwardRef<HTMLDivElement, CarouselTabListProps>(\n function CarouselTabList({ className, render, onKeyDown, ...rest }, ref) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-carousel-controls\",\n css: carouselControlsCss,\n window: targetWindow,\n });\n\n const { emblaApi, setAriaVariant, setAnnouncementState } =\n useCarouselContext();\n const { selectedIndex, scrollSnaps } = useCarouselTab(emblaApi);\n\n const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\n const [focusedTabIndex, setFocusedTabIndex] = useState<number | null>(null);\n\n const handleKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {\n let newIndex = focusedTabIndex ?? selectedIndex;\n\n if (event.key === \"ArrowLeft\" || event.key === \"ArrowRight\") {\n event.preventDefault();\n const direction = event.key === \"ArrowLeft\" ? -1 : 1;\n newIndex = Math.max(\n 0,\n Math.min(newIndex + direction, scrollSnaps.length - 1),\n );\n } else if (event.key === \"Home\") {\n event.preventDefault();\n newIndex = 0;\n } else if (event.key === \"End\") {\n event.preventDefault();\n newIndex = scrollSnaps.length - 1;\n }\n\n if (newIndex !== focusedTabIndex) {\n buttonRefs.current[newIndex]?.focus();\n setFocusedTabIndex(newIndex);\n }\n onKeyDown?.(event);\n };\n\n useEffect(() => {\n setAriaVariant(\"tabpanel\");\n }, [setAriaVariant]);\n\n return (\n <div\n role=\"tablist\"\n className={clsx(withBaseName(), className)}\n onKeyDown={handleKeyDown}\n ref={ref}\n {...rest}\n >\n {scrollSnaps.map((_, scrollSnapIndex) => {\n const visibleSlides = getVisibleSlideIndexes(\n emblaApi,\n scrollSnapIndex,\n );\n const startSlideNumber =\n visibleSlides.length >= 1 ? visibleSlides[0] : 0;\n const endSlideNumber =\n visibleSlides.length > 1\n ? visibleSlides[visibleSlides.length - 1]\n : 0;\n const slideNodes = emblaApi?.slideNodes();\n const numberOfSlides = slideNodes?.length;\n\n let ariaLabel: string;\n if (endSlideNumber >= 1) {\n ariaLabel = `Slide ${startSlideNumber} to ${endSlideNumber} of ${numberOfSlides}`;\n } else {\n const description = getSlideDescription(emblaApi, startSlideNumber);\n ariaLabel = `${description}`;\n }\n\n const selected = selectedIndex === scrollSnapIndex;\n const ariaControls = slideNodes?.length\n ? slideNodes[startSlideNumber - 1].id\n : undefined;\n\n return (\n <CarouselTabRenderer\n key={`carouselTab-${scrollSnapIndex}}`}\n ref={(element: HTMLButtonElement) => {\n buttonRefs.current[scrollSnapIndex] = element;\n }}\n render={render}\n role={\"tab\"}\n selected={selected}\n onBlur={() => {\n setFocusedTabIndex(null);\n }}\n onFocus={() => {\n setFocusedTabIndex(scrollSnapIndex);\n setAnnouncementState(\"tab\");\n emblaApi?.scrollTo(scrollSnapIndex);\n }}\n aria-label={ariaLabel}\n aria-selected={selected}\n tabIndex={selected && focusedTabIndex === null ? 0 : -1}\n aria-controls={ariaControls}\n />\n );\n })}\n </div>\n );\n },\n);\n"],"names":["makePrefixer","forwardRef","renderProps","CarouselTab","CarouselTabList","useWindow","useComponentCssInjection","carouselControlsCss","useCarouselContext","useCarouselTab","useRef","useState","useEffect","jsx","clsx","getVisibleSlideIndexes","getSlideDescription"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const settlePixelThreshold = 10;
3
+ const settlePixelThreshold = 25;
4
4
  const createCustomSettle = (callback) => function fireCustomSettle(emblaApi) {
5
5
  const { dragHandler, location, target } = emblaApi.internalEngine();
6
6
  if (dragHandler.pointerDown()) return;
@@ -1 +1 @@
1
- {"version":3,"file":"createCustomSettle.js","sources":["../src/createCustomSettle.ts"],"sourcesContent":["import type { EmblaCarouselType } from \"embla-carousel\";\n\nconst settlePixelThreshold = 10;\n\nexport type SettleCallback = (emblaApi: EmblaCarouselType) => void;\nexport type CreateCustomSettle = (callback: SettleCallback) => SettleCallback;\n\nexport const createCustomSettle: CreateCustomSettle = (callback) =>\n function fireCustomSettle(emblaApi: EmblaCarouselType) {\n const { dragHandler, location, target } = emblaApi.internalEngine();\n if (dragHandler.pointerDown()) return;\n const displacement = target.get() - location.get();\n if (Math.abs(displacement) < settlePixelThreshold) {\n callback(emblaApi);\n }\n };\n"],"names":[],"mappings":";;AAEA,MAAM,oBAAA,GAAuB,EAAA;AAKtB,MAAM,kBAAA,GAAyC,CAAC,QAAA,KACrD,SAAS,iBAAiB,QAAA,EAA6B;AACrD,EAAA,MAAM,EAAE,WAAA,EAAa,QAAA,EAAU,MAAA,EAAO,GAAI,SAAS,cAAA,EAAe;AAClE,EAAA,IAAI,WAAA,CAAY,aAAY,EAAG;AAC/B,EAAA,MAAM,YAAA,GAAe,MAAA,CAAO,GAAA,EAAI,GAAI,SAAS,GAAA,EAAI;AACjD,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,YAAY,CAAA,GAAI,oBAAA,EAAsB;AACjD,IAAA,QAAA,CAAS,QAAQ,CAAA;AAAA,EACnB;AACF;;;;"}
1
+ {"version":3,"file":"createCustomSettle.js","sources":["../src/createCustomSettle.ts"],"sourcesContent":["import type { EmblaCarouselType } from \"embla-carousel\";\n\nconst settlePixelThreshold = 25;\n\nexport type SettleCallback = (emblaApi: EmblaCarouselType) => void;\nexport type CreateCustomSettle = (callback: SettleCallback) => SettleCallback;\n\nexport const createCustomSettle: CreateCustomSettle = (callback) =>\n function fireCustomSettle(emblaApi: EmblaCarouselType) {\n const { dragHandler, location, target } = emblaApi.internalEngine();\n if (dragHandler.pointerDown()) return;\n const displacement = target.get() - location.get();\n if (Math.abs(displacement) < settlePixelThreshold) {\n callback(emblaApi);\n }\n };\n"],"names":[],"mappings":";;AAEA,MAAM,oBAAA,GAAuB,EAAA;AAKtB,MAAM,kBAAA,GAAyC,CAAC,QAAA,KACrD,SAAS,iBAAiB,QAAA,EAA6B;AACrD,EAAA,MAAM,EAAE,WAAA,EAAa,QAAA,EAAU,MAAA,EAAO,GAAI,SAAS,cAAA,EAAe;AAClE,EAAA,IAAI,WAAA,CAAY,aAAY,EAAG;AAC/B,EAAA,MAAM,YAAA,GAAe,MAAA,CAAO,GAAA,EAAI,GAAI,SAAS,GAAA,EAAI;AACjD,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,YAAY,CAAA,GAAI,oBAAA,EAAsB;AACjD,IAAA,QAAA,CAAS,QAAQ,CAAA;AAAA,EACnB;AACF;;;;"}
@@ -1,4 +1,4 @@
1
- var css_248z = ".saltCarouselAutoplayIndicator {\n --carousel-svg-circumference: 0;\n display: flex;\n align-self: center;\n}\n\n.saltCarouselAutoplayIndicatorSVG {\n transform: rotate(-90deg);\n}\n\n.saltCarouselAutoplayIndicatorSVG-track {\n fill: none;\n stroke: var(--salt-accent-foreground);\n}\n\n.saltCarouselAutoplayIndicatorSVG-bar {\n fill: none;\n stroke: var(--salt-accent-background);\n stroke-dasharray: var(--carousel-svg-circumference);\n stroke-dashoffset: var(--carousel-svg-circumference);\n animation: indicatorAnimation 0.5s linear;\n animation-play-state: paused;\n}\n\n@keyframes indicatorAnimation {\n from {\n stroke-dashoffset: var(--carousel-svg-circumference);\n }\n to {\n stroke-dashoffset: 0;\n }\n}\n";
1
+ var css_248z = ".saltCarouselAutoplayIndicator {\n --carousel-svg-circumference: 0;\n display: flex;\n align-self: center;\n}\n\n.saltCarouselAutoplayIndicatorSVG {\n transform: rotate(-90deg);\n}\n\n.saltCarouselAutoplayIndicatorSVG-bar {\n fill: none;\n stroke: var(--salt-accent-background);\n stroke-dasharray: var(--carousel-svg-circumference);\n stroke-dashoffset: var(--carousel-svg-circumference);\n animation: indicatorAnimation 0.5s linear;\n animation-play-state: paused;\n}\n\n@keyframes indicatorAnimation {\n from {\n stroke-dashoffset: var(--carousel-svg-circumference);\n }\n to {\n stroke-dashoffset: 0;\n }\n}\n";
2
2
 
3
3
  export { css_248z as default };
4
4
  //# sourceMappingURL=CarouselAutoplayIndicator.css.js.map
@@ -1,4 +1,4 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsx } from 'react/jsx-runtime';
2
2
  import { makePrefixer } from '@salt-ds/core';
3
3
  import { clsx } from 'clsx';
4
4
  import { forwardRef } from 'react';
@@ -12,7 +12,7 @@ const CarouselAutoplayIndicatorSVG = forwardRef(
12
12
  size,
13
13
  strokeWidth
14
14
  }, ref) => {
15
- return /* @__PURE__ */ jsxs(
15
+ return /* @__PURE__ */ jsx(
16
16
  "svg",
17
17
  {
18
18
  className: clsx(withBaseName(), className),
@@ -20,29 +20,17 @@ const CarouselAutoplayIndicatorSVG = forwardRef(
20
20
  height: size,
21
21
  "aria-hidden": true,
22
22
  ref,
23
- children: [
24
- /* @__PURE__ */ jsx(
25
- "circle",
26
- {
27
- className: withBaseName("track"),
28
- cx: size / 2,
29
- cy: size / 2,
30
- r: radius,
31
- strokeWidth
32
- }
33
- ),
34
- /* @__PURE__ */ jsx(
35
- "circle",
36
- {
37
- ref: barRef,
38
- className: withBaseName("bar"),
39
- cx: size / 2,
40
- cy: size / 2,
41
- r: radius,
42
- strokeWidth
43
- }
44
- )
45
- ]
23
+ children: /* @__PURE__ */ jsx(
24
+ "circle",
25
+ {
26
+ ref: barRef,
27
+ className: withBaseName("bar"),
28
+ cx: size / 2,
29
+ cy: size / 2,
30
+ r: radius,
31
+ strokeWidth
32
+ }
33
+ )
46
34
  }
47
35
  );
48
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CarouselAutoplayIndicatorSVG.js","sources":["../src/CarouselAutoplayIndicatorSVG.tsx"],"sourcesContent":["import { makePrefixer } from \"@salt-ds/core\";\nimport { clsx } from \"clsx\";\nimport { forwardRef, type Ref, type SVGAttributes } from \"react\";\n\nconst withBaseName = makePrefixer(\"saltCarouselAutoplayIndicatorSVG\");\n\nexport interface CarouselAutoplayIndicatorSVGProps\n extends SVGAttributes<SVGSVGElement> {\n /**\n * Class name to apply to the SVG element.\n */\n className?: string;\n /**\n * Ref to attach to progress indicator element\n */\n barRef: Ref<SVGCircleElement>;\n /**\n * Size of the SVG in pixels.\n */\n size: number;\n /**\n * Stroke width of the progress indicator in pixels.\n */\n strokeWidth: number;\n /**\n * Radius of the progress indicator circle.\n */\n radius: number;\n}\n\nexport const CarouselAutoplayIndicatorSVG = forwardRef<\n SVGSVGElement,\n CarouselAutoplayIndicatorSVGProps\n>(\n (\n {\n barRef,\n className,\n radius,\n size,\n strokeWidth,\n }: CarouselAutoplayIndicatorSVGProps,\n ref,\n ) => {\n return (\n <svg\n className={clsx(withBaseName(), className)}\n width={size}\n height={size}\n aria-hidden\n ref={ref}\n >\n <circle\n className={withBaseName(\"track\")}\n cx={size / 2}\n cy={size / 2}\n r={radius}\n strokeWidth={strokeWidth}\n />\n <circle\n ref={barRef}\n className={withBaseName(\"bar\")}\n cx={size / 2}\n cy={size / 2}\n r={radius}\n strokeWidth={strokeWidth}\n />\n </svg>\n );\n },\n);\n"],"names":[],"mappings":";;;;;AAIA,MAAM,YAAA,GAAe,aAAa,kCAAkC,CAAA;AA0B7D,MAAM,4BAAA,GAA+B,UAAA;AAAA,EAI1C,CACE;AAAA,IACE,MAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA;AAAA,IACA;AAAA,KAEF,GAAA,KACG;AACH,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,IAAA,CAAK,YAAA,EAAa,EAAG,SAAS,CAAA;AAAA,QACzC,KAAA,EAAO,IAAA;AAAA,QACP,MAAA,EAAQ,IAAA;AAAA,QACR,aAAA,EAAW,IAAA;AAAA,QACX,GAAA;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAW,aAAa,OAAO,CAAA;AAAA,cAC/B,IAAI,IAAA,GAAO,CAAA;AAAA,cACX,IAAI,IAAA,GAAO,CAAA;AAAA,cACX,CAAA,EAAG,MAAA;AAAA,cACH;AAAA;AAAA,WACF;AAAA,0BACA,GAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,GAAA,EAAK,MAAA;AAAA,cACL,SAAA,EAAW,aAAa,KAAK,CAAA;AAAA,cAC7B,IAAI,IAAA,GAAO,CAAA;AAAA,cACX,IAAI,IAAA,GAAO,CAAA;AAAA,cACX,CAAA,EAAG,MAAA;AAAA,cACH;AAAA;AAAA;AACF;AAAA;AAAA,KACF;AAAA,EAEJ;AACF;;;;"}
1
+ {"version":3,"file":"CarouselAutoplayIndicatorSVG.js","sources":["../src/CarouselAutoplayIndicatorSVG.tsx"],"sourcesContent":["import { makePrefixer } from \"@salt-ds/core\";\nimport { clsx } from \"clsx\";\nimport { forwardRef, type Ref, type SVGAttributes } from \"react\";\n\nconst withBaseName = makePrefixer(\"saltCarouselAutoplayIndicatorSVG\");\n\nexport interface CarouselAutoplayIndicatorSVGProps\n extends SVGAttributes<SVGSVGElement> {\n /**\n * Class name to apply to the SVG element.\n */\n className?: string;\n /**\n * Ref to attach to progress indicator element\n */\n barRef: Ref<SVGCircleElement>;\n /**\n * Size of the SVG in pixels.\n */\n size: number;\n /**\n * Stroke width of the progress indicator in pixels.\n */\n strokeWidth: number;\n /**\n * Radius of the progress indicator circle.\n */\n radius: number;\n}\n\nexport const CarouselAutoplayIndicatorSVG = forwardRef<\n SVGSVGElement,\n CarouselAutoplayIndicatorSVGProps\n>(\n (\n {\n barRef,\n className,\n radius,\n size,\n strokeWidth,\n }: CarouselAutoplayIndicatorSVGProps,\n ref,\n ) => {\n return (\n <svg\n className={clsx(withBaseName(), className)}\n width={size}\n height={size}\n aria-hidden\n ref={ref}\n >\n <circle\n ref={barRef}\n className={withBaseName(\"bar\")}\n cx={size / 2}\n cy={size / 2}\n r={radius}\n strokeWidth={strokeWidth}\n />\n </svg>\n );\n },\n);\n"],"names":[],"mappings":";;;;;AAIA,MAAM,YAAA,GAAe,aAAa,kCAAkC,CAAA;AA0B7D,MAAM,4BAAA,GAA+B,UAAA;AAAA,EAI1C,CACE;AAAA,IACE,MAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA;AAAA,IACA;AAAA,KAEF,GAAA,KACG;AACH,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,IAAA,CAAK,YAAA,EAAa,EAAG,SAAS,CAAA;AAAA,QACzC,KAAA,EAAO,IAAA;AAAA,QACP,MAAA,EAAQ,IAAA;AAAA,QACR,aAAA,EAAW,IAAA;AAAA,QACX,GAAA;AAAA,QAEA,QAAA,kBAAA,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,MAAA;AAAA,YACL,SAAA,EAAW,aAAa,KAAK,CAAA;AAAA,YAC7B,IAAI,IAAA,GAAO,CAAA;AAAA,YACX,IAAI,IAAA,GAAO,CAAA;AAAA,YACX,CAAA,EAAG,MAAA;AAAA,YACH;AAAA;AAAA;AACF;AAAA,KACF;AAAA,EAEJ;AACF;;;;"}
@@ -1,4 +1,4 @@
1
- var css_248z = ".saltCarouselCard {\n display: flex;\n transform: translate3d(0, 0, 0);\n flex: 0 0 var(--carousel-slide-size);\n min-width: 0;\n padding-left: var(--carousel-slide-spacing);\n box-sizing: border-box;\n}\n\n.saltCarouselCard-content {\n display: flex;\n user-select: none;\n overflow: hidden;\n flex-direction: column;\n gap: var(--salt-spacing-200);\n flex-grow: 1;\n}\n\n.saltCarouselCard:focus-visible {\n outline: none;\n}\n\n.saltCarouselCard:focus-visible::after {\n content: \"\";\n position: absolute;\n top: 0;\n bottom: 0;\n left: var(--carousel-slide-spacing);\n right: 0;\n outline: var(--salt-focused-outline);\n outline-offset: calc(var(--salt-focused-outlineWidth) * -1);\n cursor: var(--salt-cursor-hover);\n border-radius: var(--salt-palette-corner, 0);\n}\n\n.saltCarouselCard-body {\n display: flex;\n overflow: hidden;\n flex-direction: column;\n flex-grow: 1;\n padding-bottom: var(--salt-spacing-200);\n}\n\n.saltCarouselCard-header {\n padding-bottom: var(--salt-spacing-100);\n}\n.saltCarouselCard-actions {\n padding-top: var(--salt-spacing-200);\n}\n\n.saltCarouselCard-body.saltCarouselCard-bordered-body {\n padding: 0 var(--salt-spacing-200) var(--salt-spacing-200) var(--salt-spacing-200);\n}\n\n.saltCarouselCard-content h2,\n.saltCarouselCard-content h3 {\n margin: 0;\n}\n\n.saltCarouselCard-bordered {\n background: var(--salt-container-primary-background);\n border: var(--salt-size-fixed-100) var(--salt-container-borderStyle) var(--salt-container-primary-borderColor);\n border-radius: var(--salt-palette-corner, 0);\n}\n";
1
+ var css_248z = ".saltCarouselCard {\n display: flex;\n transform: translate3d(0, 0, 0);\n flex: 0 0 var(--carousel-slide-size);\n min-width: 0;\n padding-left: var(--carousel-slide-spacing);\n box-sizing: border-box;\n}\n\n.saltCarouselCard-content {\n display: flex;\n user-select: none;\n overflow: hidden;\n flex-direction: column;\n gap: var(--salt-spacing-200);\n flex-grow: 1;\n}\n\n.saltCarouselCard:focus-visible {\n outline: none;\n}\n\n.saltCarouselCard:not(.saltCarouselCard-bordered):focus-visible::after {\n content: \"\";\n position: absolute;\n top: 0;\n bottom: 0;\n left: var(--carousel-slide-spacing);\n right: 0;\n outline: var(--salt-focused-outline);\n outline-offset: calc(var(--salt-focused-outlineWidth) * -1);\n cursor: var(--salt-cursor-hover);\n}\n\n.saltCarouselCard-bordered:focus-visible::after {\n content: \"\";\n position: absolute;\n top: 0;\n bottom: 0;\n left: var(--carousel-slide-spacing);\n right: 0;\n outline: var(--salt-focused-outline);\n outline-offset: calc(var(--salt-focused-outlineWidth) * -1);\n cursor: var(--salt-cursor-hover);\n border-radius: var(--salt-palette-corner, 0);\n}\n\n.saltCarouselCard-body {\n display: flex;\n overflow: hidden;\n flex-direction: column;\n flex-grow: 1;\n padding-bottom: var(--salt-spacing-200);\n}\n\n.saltCarouselCard-header {\n padding-bottom: var(--salt-spacing-100);\n}\n.saltCarouselCard-actions {\n padding-top: var(--salt-spacing-200);\n}\n\n.saltCarouselCard-body.saltCarouselCard-bordered-body {\n padding: 0 var(--salt-spacing-200) var(--salt-spacing-200) var(--salt-spacing-200);\n}\n\n.saltCarouselCard-content h2,\n.saltCarouselCard-content h3 {\n margin: 0;\n}\n\n.saltCarouselCard-bordered-content {\n background: var(--salt-container-primary-background);\n border: var(--salt-size-fixed-100) var(--salt-container-borderStyle) var(--salt-container-primary-borderColor);\n border-radius: var(--salt-palette-corner, 0);\n}\n";
2
2
 
3
3
  export { css_248z as default };
4
4
  //# sourceMappingURL=CarouselCard.css.js.map
@@ -21,7 +21,11 @@ const CarouselCard = forwardRef(
21
21
  "div",
22
22
  {
23
23
  "aria-roledescription": "slide",
24
- className: clsx([withBaseName(), className]),
24
+ className: clsx([
25
+ withBaseName(),
26
+ { [withBaseName("bordered")]: appearance === "bordered" },
27
+ className
28
+ ]),
25
29
  ref,
26
30
  role: ariaVariant,
27
31
  ...rest,
@@ -29,7 +33,7 @@ const CarouselCard = forwardRef(
29
33
  "div",
30
34
  {
31
35
  className: clsx(withBaseName("content"), {
32
- [withBaseName("bordered")]: appearance === "bordered"
36
+ [withBaseName("bordered-content")]: appearance === "bordered"
33
37
  }),
34
38
  children: [
35
39
  media,
@@ -1 +1 @@
1
- {"version":3,"file":"CarouselCard.js","sources":["../src/CarouselCard.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 ComponentProps, forwardRef, type ReactNode } from \"react\";\nimport saltCarouselCardCss from \"./CarouselCard.css\";\nimport { useCarouselContext } from \"./CarouselContext\";\n\nconst withBaseName = makePrefixer(\"saltCarouselCard\");\n\n/**\n * Props for the CarouselCard component.\n */\nexport interface CarouselCardProps extends ComponentProps<\"div\"> {\n /**\n * Actions to be displayed in the content footer.\n * This can include buttons or any other interactive elements.\n */\n actions?: ReactNode;\n\n /**\n * Media content to be displayed inside the slide.\n * This could include images, videos, etc., that are visually prominent.\n * It differs from children in that media is intended to be the main visual element of the slide.\n */\n media?: ReactNode;\n\n /**\n * The appearance of the slide. Options are 'bordered', and 'transparent'.\n * 'transparent' is the default value.\n **/\n appearance?: \"bordered\" | \"transparent\";\n\n /**\n * Header content to be displayed at the top of the slide.\n * This can be text or any other React node.\n */\n header?: ReactNode;\n\n /**\n * Carousel slide id.\n * This can be used to uniquely identify the slide.\n */\n id?: string;\n}\n\nexport const CarouselCard = forwardRef<HTMLDivElement, CarouselCardProps>(\n function CarouselCard(\n { actions, appearance, children, className, header, media, ...rest },\n ref,\n ) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-carousel-card\",\n css: saltCarouselCardCss,\n window: targetWindow,\n });\n\n const { ariaVariant } = useCarouselContext();\n\n return (\n <div\n aria-roledescription=\"slide\"\n className={clsx([withBaseName(), className])}\n ref={ref}\n role={ariaVariant}\n {...rest}\n >\n <div\n className={clsx(withBaseName(\"content\"), {\n [withBaseName(\"bordered\")]: appearance === \"bordered\",\n })}\n >\n {media}\n {children && (\n <div\n className={clsx(withBaseName(\"body\"), {\n [withBaseName(\"bordered-body\")]: appearance === \"bordered\",\n })}\n >\n {header ? (\n <div className={withBaseName(\"header\")}>{header}</div>\n ) : null}\n {children}\n {actions ? (\n <div className={withBaseName(\"actions\")}>{actions}</div>\n ) : null}\n </div>\n )}\n </div>\n </div>\n );\n },\n);\n"],"names":["CarouselCard","saltCarouselCardCss"],"mappings":";;;;;;;;;AAQA,MAAM,YAAA,GAAe,aAAa,kBAAkB,CAAA;AAsC7C,MAAM,YAAA,GAAe,UAAA;AAAA,EAC1B,SAASA,aAAAA,CACP,EAAE,OAAA,EAAS,UAAA,EAAY,QAAA,EAAU,SAAA,EAAW,MAAA,EAAQ,KAAA,EAAO,GAAG,IAAA,EAAK,EACnE,GAAA,EACA;AACA,IAAA,MAAM,eAAe,SAAA,EAAU;AAC/B,IAAA,wBAAA,CAAyB;AAAA,MACvB,MAAA,EAAQ,oBAAA;AAAA,MACR,GAAA,EAAKC,QAAA;AAAA,MACL,MAAA,EAAQ;AAAA,KACT,CAAA;AAED,IAAA,MAAM,EAAE,WAAA,EAAY,GAAI,kBAAA,EAAmB;AAE3C,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,sBAAA,EAAqB,OAAA;AAAA,QACrB,WAAW,IAAA,CAAK,CAAC,YAAA,EAAa,EAAG,SAAS,CAAC,CAAA;AAAA,QAC3C,GAAA;AAAA,QACA,IAAA,EAAM,WAAA;AAAA,QACL,GAAG,IAAA;AAAA,QAEJ,QAAA,kBAAA,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAW,IAAA,CAAK,YAAA,CAAa,SAAS,CAAA,EAAG;AAAA,cACvC,CAAC,YAAA,CAAa,UAAU,CAAC,GAAG,UAAA,KAAe;AAAA,aAC5C,CAAA;AAAA,YAEA,QAAA,EAAA;AAAA,cAAA,KAAA;AAAA,cACA,QAAA,oBACC,IAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAW,IAAA,CAAK,YAAA,CAAa,MAAM,CAAA,EAAG;AAAA,oBACpC,CAAC,YAAA,CAAa,eAAe,CAAC,GAAG,UAAA,KAAe;AAAA,mBACjD,CAAA;AAAA,kBAEA,QAAA,EAAA;AAAA,oBAAA,MAAA,uBACE,KAAA,EAAA,EAAI,SAAA,EAAW,aAAa,QAAQ,CAAA,EAAI,kBAAO,CAAA,GAC9C,IAAA;AAAA,oBACH,QAAA;AAAA,oBACA,OAAA,uBACE,KAAA,EAAA,EAAI,SAAA,EAAW,aAAa,SAAS,CAAA,EAAI,mBAAQ,CAAA,GAChD;AAAA;AAAA;AAAA;AACN;AAAA;AAAA;AAEJ;AAAA,KACF;AAAA,EAEJ;AACF;;;;"}
1
+ {"version":3,"file":"CarouselCard.js","sources":["../src/CarouselCard.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 ComponentProps, forwardRef, type ReactNode } from \"react\";\nimport saltCarouselCardCss from \"./CarouselCard.css\";\nimport { useCarouselContext } from \"./CarouselContext\";\n\nconst withBaseName = makePrefixer(\"saltCarouselCard\");\n\n/**\n * Props for the CarouselCard component.\n */\nexport interface CarouselCardProps extends ComponentProps<\"div\"> {\n /**\n * Actions to be displayed in the content footer.\n * This can include buttons or any other interactive elements.\n */\n actions?: ReactNode;\n\n /**\n * Media content to be displayed inside the slide.\n * This could include images, videos, etc., that are visually prominent.\n * It differs from children in that media is intended to be the main visual element of the slide.\n */\n media?: ReactNode;\n\n /**\n * The appearance of the slide. Options are 'bordered', and 'transparent'.\n * 'transparent' is the default value.\n **/\n appearance?: \"bordered\" | \"transparent\";\n\n /**\n * Header content to be displayed at the top of the slide.\n * This can be text or any other React node.\n */\n header?: ReactNode;\n\n /**\n * Carousel slide id.\n * This can be used to uniquely identify the slide.\n */\n id?: string;\n}\n\nexport const CarouselCard = forwardRef<HTMLDivElement, CarouselCardProps>(\n function CarouselCard(\n { actions, appearance, children, className, header, media, ...rest },\n ref,\n ) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-carousel-card\",\n css: saltCarouselCardCss,\n window: targetWindow,\n });\n\n const { ariaVariant } = useCarouselContext();\n\n return (\n <div\n aria-roledescription=\"slide\"\n className={clsx([\n withBaseName(),\n { [withBaseName(\"bordered\")]: appearance === \"bordered\" },\n className,\n ])}\n ref={ref}\n role={ariaVariant}\n {...rest}\n >\n <div\n className={clsx(withBaseName(\"content\"), {\n [withBaseName(\"bordered-content\")]: appearance === \"bordered\",\n })}\n >\n {media}\n {children && (\n <div\n className={clsx(withBaseName(\"body\"), {\n [withBaseName(\"bordered-body\")]: appearance === \"bordered\",\n })}\n >\n {header ? (\n <div className={withBaseName(\"header\")}>{header}</div>\n ) : null}\n {children}\n {actions ? (\n <div className={withBaseName(\"actions\")}>{actions}</div>\n ) : null}\n </div>\n )}\n </div>\n </div>\n );\n },\n);\n"],"names":["CarouselCard","saltCarouselCardCss"],"mappings":";;;;;;;;;AAQA,MAAM,YAAA,GAAe,aAAa,kBAAkB,CAAA;AAsC7C,MAAM,YAAA,GAAe,UAAA;AAAA,EAC1B,SAASA,aAAAA,CACP,EAAE,OAAA,EAAS,UAAA,EAAY,QAAA,EAAU,SAAA,EAAW,MAAA,EAAQ,KAAA,EAAO,GAAG,IAAA,EAAK,EACnE,GAAA,EACA;AACA,IAAA,MAAM,eAAe,SAAA,EAAU;AAC/B,IAAA,wBAAA,CAAyB;AAAA,MACvB,MAAA,EAAQ,oBAAA;AAAA,MACR,GAAA,EAAKC,QAAA;AAAA,MACL,MAAA,EAAQ;AAAA,KACT,CAAA;AAED,IAAA,MAAM,EAAE,WAAA,EAAY,GAAI,kBAAA,EAAmB;AAE3C,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,sBAAA,EAAqB,OAAA;AAAA,QACrB,WAAW,IAAA,CAAK;AAAA,UACd,YAAA,EAAa;AAAA,UACb,EAAE,CAAC,YAAA,CAAa,UAAU,CAAC,GAAG,eAAe,UAAA,EAAW;AAAA,UACxD;AAAA,SACD,CAAA;AAAA,QACD,GAAA;AAAA,QACA,IAAA,EAAM,WAAA;AAAA,QACL,GAAG,IAAA;AAAA,QAEJ,QAAA,kBAAA,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAW,IAAA,CAAK,YAAA,CAAa,SAAS,CAAA,EAAG;AAAA,cACvC,CAAC,YAAA,CAAa,kBAAkB,CAAC,GAAG,UAAA,KAAe;AAAA,aACpD,CAAA;AAAA,YAEA,QAAA,EAAA;AAAA,cAAA,KAAA;AAAA,cACA,QAAA,oBACC,IAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAW,IAAA,CAAK,YAAA,CAAa,MAAM,CAAA,EAAG;AAAA,oBACpC,CAAC,YAAA,CAAa,eAAe,CAAC,GAAG,UAAA,KAAe;AAAA,mBACjD,CAAA;AAAA,kBAEA,QAAA,EAAA;AAAA,oBAAA,MAAA,uBACE,KAAA,EAAA,EAAI,SAAA,EAAW,aAAa,QAAQ,CAAA,EAAI,kBAAO,CAAA,GAC9C,IAAA;AAAA,oBACH,QAAA;AAAA,oBACA,OAAA,uBACE,KAAA,EAAA,EAAI,SAAA,EAAW,aAAa,SAAS,CAAA,EAAI,mBAAQ,CAAA,GAChD;AAAA;AAAA;AAAA;AACN;AAAA;AAAA;AAEJ;AAAA,KACF;AAAA,EAEJ;AACF;;;;"}
@@ -3,7 +3,7 @@ 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, useState, useEffect, useLayoutEffect, Children, cloneElement } 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';
@@ -14,6 +14,7 @@ const ANNOUNCEMENT_DURATION = 1200;
14
14
  const withBaseName = makePrefixer("saltCarouselSlides");
15
15
  const announceSlideChangesFrom = [
16
16
  "drag",
17
+ "focus",
17
18
  "navigation"
18
19
  ];
19
20
  const CarouselSlides = forwardRef(
@@ -32,10 +33,16 @@ const CarouselSlides = forwardRef(
32
33
  emblaRef,
33
34
  carouselId
34
35
  } = useCarouselContext();
35
- const carouselRef = useForkRef(ref, emblaRef);
36
+ const containerRef = useRef(null);
37
+ const forkedEmblaRef = useForkRef(ref, emblaRef);
38
+ const carouselRef = useForkRef(
39
+ forkedEmblaRef,
40
+ containerRef
41
+ );
36
42
  const slideRefs = useRef([]);
37
- const [focusSlideIndex, setFocusedSlideIndex] = useState(-1);
43
+ const [focusedSlideIndex, setFocusedSlideIndex] = useState(-1);
38
44
  const [dragging, setDragging] = useState(false);
45
+ const focusOnSettle = useRef(false);
39
46
  const [stableScrollSnap, setStableScrollSnap] = useState(void 0);
40
47
  const visibleSlideIndexes = getVisibleSlideIndexes(
41
48
  emblaApi,
@@ -46,6 +53,12 @@ const CarouselSlides = forwardRef(
46
53
  const handleSettle = (emblaApi2) => {
47
54
  const selectedScrollSnap = (emblaApi2 == null ? void 0 : emblaApi2.selectedScrollSnap()) ?? 0;
48
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);
49
62
  };
50
63
  if (!emblaApi) {
51
64
  return;
@@ -60,29 +73,39 @@ const CarouselSlides = forwardRef(
60
73
  emblaApi.off("scroll", scrollCallback);
61
74
  emblaApi.off("pointerDown", pointerDownCallback);
62
75
  };
63
- }, [emblaApi]);
64
- useLayoutEffect(() => {
65
- if (focusSlideIndex >= 0) {
66
- const numberOfSnaps = (emblaApi == null ? void 0 : emblaApi.scrollSnapList().length) ?? 1;
67
- const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;
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) {
68
90
  const nearestScrollSnap = Math.floor(
69
- focusSlideIndex / numberOfSlidesPerSnap
91
+ focusedSlideIndex / numberOfSlidesPerSnap
70
92
  );
71
93
  if ((emblaApi == null ? void 0 : emblaApi.selectedScrollSnap()) !== nearestScrollSnap) {
72
- setAnnouncementState("focus");
73
94
  emblaApi == null ? void 0 : emblaApi.scrollTo(nearestScrollSnap);
95
+ focusOnSettle.current = true;
74
96
  }
75
- setTimeout(() => {
76
- var _a;
77
- (_a = slideRefs.current[focusSlideIndex]) == null ? void 0 : _a.focus();
78
- }, 0);
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);
79
102
  }
80
- }, [focusSlideIndex, emblaApi, setAnnouncementState]);
103
+ }, [focusedSlideIndex, emblaApi]);
81
104
  useEffect(() => {
82
105
  if (disableSlideAnnouncements === false) {
83
106
  setAnnouncementState(void 0);
84
107
  }
85
- }, [disableSlideAnnouncements]);
108
+ }, [disableSlideAnnouncements, setAnnouncementState]);
86
109
  useEffect(() => {
87
110
  if (stableScrollSnap === void 0 || disableSlideAnnouncements || !announcementState || announceSlideChangesFrom.indexOf(announcementState) === -1) {
88
111
  return;
@@ -103,17 +126,22 @@ const CarouselSlides = forwardRef(
103
126
  if (event.repeat) {
104
127
  return;
105
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);
106
132
  switch (event.key) {
107
133
  case "ArrowLeft": {
108
134
  event.preventDefault();
109
- setFocusedSlideIndex((prevState) => Math.max(prevState - 1, 0));
135
+ const prevSnap = Math.max(currentSnap - 1, 0);
136
+ const newIndex = prevSnap * numberOfSlidesPerSnap;
137
+ setFocusedSlideIndex(newIndex);
110
138
  break;
111
139
  }
112
140
  case "ArrowRight": {
113
141
  event.preventDefault();
114
- setFocusedSlideIndex(
115
- (prevState) => Math.min(prevState + 1, slideRefs.current.length - 1)
116
- );
142
+ const nextSnap = Math.min(currentSnap + 1, numberOfSnaps - 1);
143
+ const newIndex = nextSnap * numberOfSlidesPerSnap;
144
+ setFocusedSlideIndex(newIndex);
117
145
  break;
118
146
  }
119
147
  }
@@ -150,15 +178,14 @@ const CarouselSlides = forwardRef(
150
178
  children: Children.map(children, (child, index) => {
151
179
  const childElement = child;
152
180
  const existingId = childElement.props.id;
153
- const isHidden = !visibleSlideIndexes.includes(index + 1);
181
+ const isFocused = focusedSlideIndex === index;
182
+ const isHidden = !visibleSlideIndexes.includes(index + 1) && !isFocused;
154
183
  const element = child;
155
184
  return cloneElement(element, {
156
185
  "aria-hidden": isHidden,
157
186
  id: existingId ?? `${carouselId}-slide${index + 1}`,
158
- onMouseDown: (event) => event.preventDefault(),
159
187
  onFocus: (event) => {
160
188
  var _a, _b;
161
- event.preventDefault();
162
189
  setFocusedSlideIndex(index);
163
190
  (_b = (_a = element.props) == null ? void 0 : _a.onFocus) == null ? void 0 : _b.call(_a, event);
164
191
  },
@@ -1 +1 @@
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 forwardRef,\n type KeyboardEvent,\n type MouseEventHandler,\n type SyntheticEvent,\n useEffect,\n useLayoutEffect,\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 \"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 carouselRef = useForkRef<HTMLDivElement>(ref, emblaRef);\n\n const slideRefs = useRef<(HTMLDivElement | null)[]>([]);\n const [focusSlideIndex, setFocusedSlideIndex] = useState<number>(-1);\n const [dragging, setDragging] = useState(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 };\n\n if (!emblaApi) {\n return;\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]);\n\n useLayoutEffect(() => {\n if (focusSlideIndex >= 0) {\n const numberOfSnaps = emblaApi?.scrollSnapList().length ?? 1;\n const numberOfSlidesPerSnap = slideRefs.current.length / numberOfSnaps;\n const nearestScrollSnap = Math.floor(\n focusSlideIndex / numberOfSlidesPerSnap,\n );\n if (emblaApi?.selectedScrollSnap() !== nearestScrollSnap) {\n setAnnouncementState(\"focus\");\n emblaApi?.scrollTo(nearestScrollSnap);\n }\n setTimeout(() => {\n slideRefs.current[focusSlideIndex]?.focus();\n }, 0);\n }\n }, [focusSlideIndex, emblaApi, setAnnouncementState]);\n\n useEffect(() => {\n if (disableSlideAnnouncements === false) {\n setAnnouncementState(undefined);\n }\n }, [disableSlideAnnouncements]);\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 switch (event.key) {\n case \"ArrowLeft\": {\n event.preventDefault();\n setFocusedSlideIndex((prevState) => Math.max(prevState - 1, 0));\n break;\n }\n case \"ArrowRight\": {\n event.preventDefault();\n setFocusedSlideIndex((prevState) =>\n Math.min(prevState + 1, slideRefs.current.length - 1),\n );\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 React.ReactElement;\n const existingId = childElement.props.id;\n const isHidden = !visibleSlideIndexes.includes(index + 1);\n const element = child as React.ReactElement;\n return cloneElement(element, {\n \"aria-hidden\": isHidden,\n id: existingId ?? `${carouselId}-slide${index + 1}`,\n onMouseDown: (event: SyntheticEvent) => event.preventDefault(),\n onFocus: (event: FocusEvent) => {\n event.preventDefault();\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":";;;;;;;;;;;;AA2BA,MAAM,qBAAA,GAAwB,IAAA;AAO9B,MAAM,YAAA,GAAe,aAAa,oBAAoB,CAAA;AAEtD,MAAM,wBAAA,GAA0D;AAAA,EAC9D,MAAA;AAAA,EACA;AACF,CAAA;AAEO,MAAM,cAAA,GAAiB,UAAA;AAAA,EAC5B,SAASA,eAAAA,CACP,EAAE,QAAA,EAAU,SAAA,EAAW,IAAI,SAAA,EAAW,GAAG,IAAA,EAAK,EAC9C,GAAA,EACA;AACA,IAAA,MAAM,eAAe,SAAA,EAAU;AAC/B,IAAA,wBAAA,CAAyB;AAAA,MACvB,MAAA,EAAQ,sBAAA;AAAA,MACR,GAAA,EAAKC,QAAA;AAAA,MACL,MAAA,EAAQ;AAAA,KACT,CAAA;AACD,IAAA,MAAM;AAAA,MACJ,yBAAA;AAAA,MACA,iBAAA;AAAA,MACA,oBAAA;AAAA,MACA,QAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,QACE,kBAAA,EAAmB;AAEvB,IAAA,MAAM,WAAA,GAAc,UAAA,CAA2B,GAAA,EAAK,QAAQ,CAAA;AAE5D,IAAA,MAAM,SAAA,GAAY,MAAA,CAAkC,EAAE,CAAA;AACtD,IAAA,MAAM,CAAC,eAAA,EAAiB,oBAAoB,CAAA,GAAI,SAAiB,EAAE,CAAA;AACnE,IAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,KAAK,CAAA;AAE9C,IAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAI,SAE9C,MAAS,CAAA;AAEX,IAAA,MAAM,mBAAA,GAAsB,sBAAA;AAAA,MAC1B,QAAA;AAAA,MACA,gBAAA,IAAoB;AAAA,KACtB;AACA,IAAA,MAAM,EAAE,QAAA,EAAS,GAAI,gBAAA,EAAiB;AAEtC,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,MAAM,YAAA,GAAe,CAACC,SAAAA,KAAgC;AACpD,QAAA,MAAM,kBAAA,GAAA,CAAqBA,SAAAA,IAAA,IAAA,GAAA,MAAA,GAAAA,SAAAA,CAAU,kBAAA,EAAA,KAAwB,CAAA;AAC7D,QAAA,mBAAA,CAAoB,kBAAkB,CAAA;AAAA,MACxC,CAAA;AAEA,MAAA,IAAI,CAAC,QAAA,EAAU;AACb,QAAA;AAAA,MACF;AACA,MAAA,MAAM,cAAA,GAAiB,mBAAmB,YAAY,CAAA;AACtD,MAAA,MAAM,sBAAsB,MAAM;AAChC,QAAA,oBAAA,CAAqB,MAAM,CAAA;AAAA,MAC7B,CAAA;AACA,MAAA,QAAA,CAAS,EAAA,CAAG,UAAU,cAAc,CAAA;AACpC,MAAA,QAAA,CAAS,EAAA,CAAG,eAAe,mBAAmB,CAAA;AAE9C,MAAA,OAAO,MAAM;AACX,QAAA,QAAA,CAAS,GAAA,CAAI,UAAU,cAAc,CAAA;AACrC,QAAA,QAAA,CAAS,GAAA,CAAI,eAAe,mBAAmB,CAAA;AAAA,MACjD,CAAA;AAAA,IACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,IAAA,eAAA,CAAgB,MAAM;AACpB,MAAA,IAAI,mBAAmB,CAAA,EAAG;AACxB,QAAA,MAAM,aAAA,GAAA,CAAgB,QAAA,IAAA,IAAA,GAAA,MAAA,GAAA,QAAA,CAAU,cAAA,EAAA,CAAiB,MAAA,KAAU,CAAA;AAC3D,QAAA,MAAM,qBAAA,GAAwB,SAAA,CAAU,OAAA,CAAQ,MAAA,GAAS,aAAA;AACzD,QAAA,MAAM,oBAAoB,IAAA,CAAK,KAAA;AAAA,UAC7B,eAAA,GAAkB;AAAA,SACpB;AACA,QAAA,IAAA,CAAI,QAAA,IAAA,IAAA,GAAA,MAAA,GAAA,QAAA,CAAU,0BAAyB,iBAAA,EAAmB;AACxD,UAAA,oBAAA,CAAqB,OAAO,CAAA;AAC5B,UAAA,QAAA,IAAA,IAAA,GAAA,MAAA,GAAA,QAAA,CAAU,QAAA,CAAS,iBAAA,CAAA;AAAA,QACrB;AACA,QAAA,UAAA,CAAW,MAAM;AA9GzB,UAAA,IAAA,EAAA;AA+GU,UAAA,CAAA,EAAA,GAAA,SAAA,CAAU,OAAA,CAAQ,eAAe,CAAA,KAAjC,IAAA,GAAA,MAAA,GAAA,EAAA,CAAoC,KAAA,EAAA;AAAA,QACtC,GAAG,CAAC,CAAA;AAAA,MACN;AAAA,IACF,CAAA,EAAG,CAAC,eAAA,EAAiB,QAAA,EAAU,oBAAoB,CAAC,CAAA;AAEpD,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,IAAI,8BAA8B,KAAA,EAAO;AACvC,QAAA,oBAAA,CAAqB,MAAS,CAAA;AAAA,MAChC;AAAA,IACF,CAAA,EAAG,CAAC,yBAAyB,CAAC,CAAA;AAE9B,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,IACE,gBAAA,KAAqB,UACrB,yBAAA,IACA,CAAC,qBACD,wBAAA,CAAyB,OAAA,CAAQ,iBAAiB,CAAA,KAAM,EAAA,EACxD;AACA,QAAA;AAAA,MACF;AACA,MAAA,MAAM,YAAA,GAAe,0BAAA;AAAA,QACnB,QAAA;AAAA,QACA;AAAA,OACF;AACA,MAAA,QAAA,CAAS,cAAc,qBAAqB,CAAA;AAAA,IAC9C,CAAA,EAAG;AAAA,MACD,QAAA;AAAA,MACA,iBAAA;AAAA,MACA,yBAAA;AAAA,MACA,gBAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,MAAM,aAAA,GAAgB,CAAC,KAAA,KAAyC;AAC9D,MAAA,IAAI,MAAM,MAAA,EAAQ;AAChB,QAAA;AAAA,MACF;AACA,MAAA,QAAQ,MAAM,GAAA;AAAK,QACjB,KAAK,WAAA,EAAa;AAChB,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,oBAAA,CAAqB,CAAC,SAAA,KAAc,IAAA,CAAK,IAAI,SAAA,GAAY,CAAA,EAAG,CAAC,CAAC,CAAA;AAC9D,UAAA;AAAA,QACF;AAAA,QACA,KAAK,YAAA,EAAc;AACjB,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,oBAAA;AAAA,YAAqB,CAAC,cACpB,IAAA,CAAK,GAAA,CAAI,YAAY,CAAA,EAAG,SAAA,CAAU,OAAA,CAAQ,MAAA,GAAS,CAAC;AAAA,WACtD;AACA,UAAA;AAAA,QACF;AAAA;AAEF,MAAA,SAAA,IAAA,IAAA,GAAA,MAAA,GAAA,SAAA,CAAY,KAAA,CAAA;AAAA,IACd,CAAA;AAEA,IAAA,MAAM,eAAA,GAAqD,CAAC,KAAA,KAAU;AArK1E,MAAA,IAAA,EAAA;AAsKM,MAAA,WAAA,CAAY,IAAI,CAAA;AAChB,MAAA,CAAA,EAAA,GAAA,IAAA,CAAK,gBAAL,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,IAAA,EAAmB,KAAA,CAAA;AAAA,IACrB,CAAA;AACA,IAAA,MAAM,aAAA,GAAmD,CAAC,KAAA,KAAU;AAzKxE,MAAA,IAAA,EAAA;AA0KM,MAAA,WAAA,CAAY,KAAK,CAAA;AACjB,MAAA,CAAA,EAAA,GAAA,IAAA,CAAK,cAAL,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,IAAA,EAAiB,KAAA,CAAA;AAAA,IACnB,CAAA;AAEA,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,aAAA;AAAA,QACX,WAAA,EAAa,eAAA;AAAA,QACb,SAAA,EAAW,aAAA;AAAA,QACX,GAAA,EAAK,WAAA;AAAA,QACL,SAAA,EAAW,IAAA;AAAA,UACT,YAAA,EAAa;AAAA,UACb,EAAE,CAAC,YAAA,CAAa,UAAU,CAAC,GAAG,QAAA,EAAS;AAAA,UACvC;AAAA,SACF;AAAA,QACC,GAAG,IAAA;AAAA,QAEJ,QAAA,kBAAA,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAW,aAAa,WAAW,CAAA;AAAA,YACnC,EAAA,EAAI,EAAA,IAAM,CAAA,EAAG,UAAU,CAAA,OAAA,CAAA;AAAA,YAEtB,QAAA,EAAA,QAAA,CAAS,GAAA,CAAI,QAAA,EAAU,CAAC,OAAO,KAAA,KAAU;AACxC,cAAA,MAAM,YAAA,GAAe,KAAA;AACrB,cAAA,MAAM,UAAA,GAAa,aAAa,KAAA,CAAM,EAAA;AACtC,cAAA,MAAM,QAAA,GAAW,CAAC,mBAAA,CAAoB,QAAA,CAAS,QAAQ,CAAC,CAAA;AACxD,cAAA,MAAM,OAAA,GAAU,KAAA;AAChB,cAAA,OAAO,aAAa,OAAA,EAAS;AAAA,gBAC3B,aAAA,EAAe,QAAA;AAAA,gBACf,IAAI,UAAA,IAAc,CAAA,EAAG,UAAU,CAAA,MAAA,EAAS,QAAQ,CAAC,CAAA,CAAA;AAAA,gBACjD,WAAA,EAAa,CAAC,KAAA,KAA0B,KAAA,CAAM,cAAA,EAAe;AAAA,gBAC7D,OAAA,EAAS,CAAC,KAAA,KAAsB;AAxM9C,kBAAA,IAAA,EAAA,EAAA,EAAA;AAyMgB,kBAAA,KAAA,CAAM,cAAA,EAAe;AACrB,kBAAA,oBAAA,CAAqB,KAAK,CAAA;AAC1B,kBAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,OAAA,CAAQ,KAAA,KAAR,IAAA,GAAA,MAAA,GAAA,EAAA,CAAe,OAAA,KAAf,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAAyB,KAAA,CAAA;AAAA,gBAC3B,CAAA;AAAA,gBACA,QAAA,EAAU,CAAC,QAAA,GAAW,CAAA,GAAI,EAAA;AAAA,gBAC1B,GAAA,EAAK,CAAC,EAAA,KAAuB;AAC3B,kBAAA,SAAA,CAAU,OAAA,CAAQ,KAAK,CAAA,GAAI,EAAA;AAAA,gBAC7B;AAAA,eACD,CAAA;AAAA,YACH,CAAC;AAAA;AAAA;AACH;AAAA,KACF;AAAA,EAEJ;AACF;;;;"}
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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"}
@@ -30,11 +30,17 @@ const CarouselTabList = forwardRef(
30
30
  var _a;
31
31
  let newIndex = focusedTabIndex ?? selectedIndex;
32
32
  if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
33
+ event.preventDefault();
33
34
  const direction = event.key === "ArrowLeft" ? -1 : 1;
34
- newIndex = (newIndex + direction + scrollSnaps.length) % scrollSnaps.length;
35
+ newIndex = Math.max(
36
+ 0,
37
+ Math.min(newIndex + direction, scrollSnaps.length - 1)
38
+ );
35
39
  } else if (event.key === "Home") {
40
+ event.preventDefault();
36
41
  newIndex = 0;
37
42
  } else if (event.key === "End") {
43
+ event.preventDefault();
38
44
  newIndex = scrollSnaps.length - 1;
39
45
  }
40
46
  if (newIndex !== focusedTabIndex) {
@@ -1 +1 @@
1
- {"version":3,"file":"CarouselTabList.js","sources":["../src/CarouselTabList.tsx"],"sourcesContent":["import { makePrefixer, type RenderPropsType, renderProps } from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport {\n forwardRef,\n type HTMLAttributes,\n type KeyboardEventHandler,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { useCarouselContext } from \"./CarouselContext\";\nimport {\n CarouselTab,\n type CarouselTabProps,\n useCarouselTab,\n} from \"./CarouselTab\";\nimport carouselControlsCss from \"./CarouselTabList.css\";\nimport { getSlideDescription } from \"./getSlideDescription\";\nimport { getVisibleSlideIndexes } from \"./getVisibleSlideIndexes\";\n\nconst withBaseName = makePrefixer(\"saltCarouselTabList\");\n\n/**\n * Props for the CarouselTabList component.\n */\nexport interface CarouselTabListProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Render prop to enable customisation of tab button.\n */\n render?: RenderPropsType[\"render\"];\n}\n\nexport interface CarouselTabRendererProps extends CarouselTabProps {\n render?: CarouselTabListProps[\"render\"];\n}\n\nconst CarouselTabRenderer = forwardRef<\n HTMLButtonElement,\n CarouselTabRendererProps\n>((props, ref) => {\n return renderProps(CarouselTab, { ...props, ref });\n});\n\nexport const CarouselTabList = forwardRef<HTMLDivElement, CarouselTabListProps>(\n function CarouselTabList({ className, render, onKeyDown, ...rest }, ref) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-carousel-controls\",\n css: carouselControlsCss,\n window: targetWindow,\n });\n\n const { emblaApi, setAriaVariant, setAnnouncementState } =\n useCarouselContext();\n const { selectedIndex, scrollSnaps } = useCarouselTab(emblaApi);\n\n const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\n const [focusedTabIndex, setFocusedTabIndex] = useState<number | null>(null);\n\n const handleKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {\n let newIndex = focusedTabIndex ?? selectedIndex;\n\n if (event.key === \"ArrowLeft\" || event.key === \"ArrowRight\") {\n const direction = event.key === \"ArrowLeft\" ? -1 : 1;\n newIndex =\n (newIndex + direction + scrollSnaps.length) % scrollSnaps.length;\n } else if (event.key === \"Home\") {\n newIndex = 0;\n } else if (event.key === \"End\") {\n newIndex = scrollSnaps.length - 1;\n }\n\n if (newIndex !== focusedTabIndex) {\n buttonRefs.current[newIndex]?.focus();\n setFocusedTabIndex(newIndex);\n }\n onKeyDown?.(event);\n };\n\n useEffect(() => {\n setAriaVariant(\"tabpanel\");\n }, [setAriaVariant]);\n\n return (\n <div\n role=\"tablist\"\n className={clsx(withBaseName(), className)}\n onKeyDown={handleKeyDown}\n ref={ref}\n {...rest}\n >\n {scrollSnaps.map((_, scrollSnapIndex) => {\n const visibleSlides = getVisibleSlideIndexes(\n emblaApi,\n scrollSnapIndex,\n );\n const startSlideNumber =\n visibleSlides.length >= 1 ? visibleSlides[0] : 0;\n const endSlideNumber =\n visibleSlides.length > 1\n ? visibleSlides[visibleSlides.length - 1]\n : 0;\n const slideNodes = emblaApi?.slideNodes();\n const numberOfSlides = slideNodes?.length;\n\n let ariaLabel: string;\n if (endSlideNumber >= 1) {\n ariaLabel = `Slide ${startSlideNumber} to ${endSlideNumber} of ${numberOfSlides}`;\n } else {\n const description = getSlideDescription(emblaApi, startSlideNumber);\n ariaLabel = `${description}`;\n }\n\n const selected = selectedIndex === scrollSnapIndex;\n const ariaControls = slideNodes?.length\n ? slideNodes[startSlideNumber - 1].id\n : undefined;\n\n return (\n <CarouselTabRenderer\n key={`carouselTab-${scrollSnapIndex}}`}\n ref={(element: HTMLButtonElement) => {\n buttonRefs.current[scrollSnapIndex] = element;\n }}\n render={render}\n role={\"tab\"}\n selected={selected}\n onBlur={() => {\n setFocusedTabIndex(null);\n }}\n onFocus={() => {\n setFocusedTabIndex(scrollSnapIndex);\n setAnnouncementState(\"tab\");\n emblaApi?.scrollTo(scrollSnapIndex);\n }}\n aria-label={ariaLabel}\n aria-selected={selected}\n tabIndex={selected && focusedTabIndex === null ? 0 : -1}\n aria-controls={ariaControls}\n />\n );\n })}\n </div>\n );\n },\n);\n"],"names":["CarouselTabList","carouselControlsCss"],"mappings":"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"}
1
+ {"version":3,"file":"CarouselTabList.js","sources":["../src/CarouselTabList.tsx"],"sourcesContent":["import { makePrefixer, type RenderPropsType, renderProps } from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { clsx } from \"clsx\";\nimport {\n forwardRef,\n type HTMLAttributes,\n type KeyboardEventHandler,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { useCarouselContext } from \"./CarouselContext\";\nimport {\n CarouselTab,\n type CarouselTabProps,\n useCarouselTab,\n} from \"./CarouselTab\";\nimport carouselControlsCss from \"./CarouselTabList.css\";\nimport { getSlideDescription } from \"./getSlideDescription\";\nimport { getVisibleSlideIndexes } from \"./getVisibleSlideIndexes\";\n\nconst withBaseName = makePrefixer(\"saltCarouselTabList\");\n\n/**\n * Props for the CarouselTabList component.\n */\nexport interface CarouselTabListProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Render prop to enable customisation of tab button.\n */\n render?: RenderPropsType[\"render\"];\n}\n\nexport interface CarouselTabRendererProps extends CarouselTabProps {\n render?: CarouselTabListProps[\"render\"];\n}\n\nconst CarouselTabRenderer = forwardRef<\n HTMLButtonElement,\n CarouselTabRendererProps\n>((props, ref) => {\n return renderProps(CarouselTab, { ...props, ref });\n});\n\nexport const CarouselTabList = forwardRef<HTMLDivElement, CarouselTabListProps>(\n function CarouselTabList({ className, render, onKeyDown, ...rest }, ref) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"salt-carousel-controls\",\n css: carouselControlsCss,\n window: targetWindow,\n });\n\n const { emblaApi, setAriaVariant, setAnnouncementState } =\n useCarouselContext();\n const { selectedIndex, scrollSnaps } = useCarouselTab(emblaApi);\n\n const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\n const [focusedTabIndex, setFocusedTabIndex] = useState<number | null>(null);\n\n const handleKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {\n let newIndex = focusedTabIndex ?? selectedIndex;\n\n if (event.key === \"ArrowLeft\" || event.key === \"ArrowRight\") {\n event.preventDefault();\n const direction = event.key === \"ArrowLeft\" ? -1 : 1;\n newIndex = Math.max(\n 0,\n Math.min(newIndex + direction, scrollSnaps.length - 1),\n );\n } else if (event.key === \"Home\") {\n event.preventDefault();\n newIndex = 0;\n } else if (event.key === \"End\") {\n event.preventDefault();\n newIndex = scrollSnaps.length - 1;\n }\n\n if (newIndex !== focusedTabIndex) {\n buttonRefs.current[newIndex]?.focus();\n setFocusedTabIndex(newIndex);\n }\n onKeyDown?.(event);\n };\n\n useEffect(() => {\n setAriaVariant(\"tabpanel\");\n }, [setAriaVariant]);\n\n return (\n <div\n role=\"tablist\"\n className={clsx(withBaseName(), className)}\n onKeyDown={handleKeyDown}\n ref={ref}\n {...rest}\n >\n {scrollSnaps.map((_, scrollSnapIndex) => {\n const visibleSlides = getVisibleSlideIndexes(\n emblaApi,\n scrollSnapIndex,\n );\n const startSlideNumber =\n visibleSlides.length >= 1 ? visibleSlides[0] : 0;\n const endSlideNumber =\n visibleSlides.length > 1\n ? visibleSlides[visibleSlides.length - 1]\n : 0;\n const slideNodes = emblaApi?.slideNodes();\n const numberOfSlides = slideNodes?.length;\n\n let ariaLabel: string;\n if (endSlideNumber >= 1) {\n ariaLabel = `Slide ${startSlideNumber} to ${endSlideNumber} of ${numberOfSlides}`;\n } else {\n const description = getSlideDescription(emblaApi, startSlideNumber);\n ariaLabel = `${description}`;\n }\n\n const selected = selectedIndex === scrollSnapIndex;\n const ariaControls = slideNodes?.length\n ? slideNodes[startSlideNumber - 1].id\n : undefined;\n\n return (\n <CarouselTabRenderer\n key={`carouselTab-${scrollSnapIndex}}`}\n ref={(element: HTMLButtonElement) => {\n buttonRefs.current[scrollSnapIndex] = element;\n }}\n render={render}\n role={\"tab\"}\n selected={selected}\n onBlur={() => {\n setFocusedTabIndex(null);\n }}\n onFocus={() => {\n setFocusedTabIndex(scrollSnapIndex);\n setAnnouncementState(\"tab\");\n emblaApi?.scrollTo(scrollSnapIndex);\n }}\n aria-label={ariaLabel}\n aria-selected={selected}\n tabIndex={selected && focusedTabIndex === null ? 0 : -1}\n aria-controls={ariaControls}\n />\n );\n })}\n </div>\n );\n },\n);\n"],"names":["CarouselTabList","carouselControlsCss"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- const settlePixelThreshold = 10;
1
+ const settlePixelThreshold = 25;
2
2
  const createCustomSettle = (callback) => function fireCustomSettle(emblaApi) {
3
3
  const { dragHandler, location, target } = emblaApi.internalEngine();
4
4
  if (dragHandler.pointerDown()) return;
@@ -1 +1 @@
1
- {"version":3,"file":"createCustomSettle.js","sources":["../src/createCustomSettle.ts"],"sourcesContent":["import type { EmblaCarouselType } from \"embla-carousel\";\n\nconst settlePixelThreshold = 10;\n\nexport type SettleCallback = (emblaApi: EmblaCarouselType) => void;\nexport type CreateCustomSettle = (callback: SettleCallback) => SettleCallback;\n\nexport const createCustomSettle: CreateCustomSettle = (callback) =>\n function fireCustomSettle(emblaApi: EmblaCarouselType) {\n const { dragHandler, location, target } = emblaApi.internalEngine();\n if (dragHandler.pointerDown()) return;\n const displacement = target.get() - location.get();\n if (Math.abs(displacement) < settlePixelThreshold) {\n callback(emblaApi);\n }\n };\n"],"names":[],"mappings":"AAEA,MAAM,oBAAA,GAAuB,EAAA;AAKtB,MAAM,kBAAA,GAAyC,CAAC,QAAA,KACrD,SAAS,iBAAiB,QAAA,EAA6B;AACrD,EAAA,MAAM,EAAE,WAAA,EAAa,QAAA,EAAU,MAAA,EAAO,GAAI,SAAS,cAAA,EAAe;AAClE,EAAA,IAAI,WAAA,CAAY,aAAY,EAAG;AAC/B,EAAA,MAAM,YAAA,GAAe,MAAA,CAAO,GAAA,EAAI,GAAI,SAAS,GAAA,EAAI;AACjD,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,YAAY,CAAA,GAAI,oBAAA,EAAsB;AACjD,IAAA,QAAA,CAAS,QAAQ,CAAA;AAAA,EACnB;AACF;;;;"}
1
+ {"version":3,"file":"createCustomSettle.js","sources":["../src/createCustomSettle.ts"],"sourcesContent":["import type { EmblaCarouselType } from \"embla-carousel\";\n\nconst settlePixelThreshold = 25;\n\nexport type SettleCallback = (emblaApi: EmblaCarouselType) => void;\nexport type CreateCustomSettle = (callback: SettleCallback) => SettleCallback;\n\nexport const createCustomSettle: CreateCustomSettle = (callback) =>\n function fireCustomSettle(emblaApi: EmblaCarouselType) {\n const { dragHandler, location, target } = emblaApi.internalEngine();\n if (dragHandler.pointerDown()) return;\n const displacement = target.get() - location.get();\n if (Math.abs(displacement) < settlePixelThreshold) {\n callback(emblaApi);\n }\n };\n"],"names":[],"mappings":"AAEA,MAAM,oBAAA,GAAuB,EAAA;AAKtB,MAAM,kBAAA,GAAyC,CAAC,QAAA,KACrD,SAAS,iBAAiB,QAAA,EAA6B;AACrD,EAAA,MAAM,EAAE,WAAA,EAAa,QAAA,EAAU,MAAA,EAAO,GAAI,SAAS,cAAA,EAAe;AAClE,EAAA,IAAI,WAAA,CAAY,aAAY,EAAG;AAC/B,EAAA,MAAM,YAAA,GAAe,MAAA,CAAO,GAAA,EAAI,GAAI,SAAS,GAAA,EAAI;AACjD,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,YAAY,CAAA,GAAI,oBAAA,EAAsB;AACjD,IAAA,QAAA,CAAS,QAAQ,CAAA;AAAA,EACnB;AACF;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salt-ds/embla-carousel",
3
- "version": "0.1.6",
3
+ "version": "1.0.0",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,7 +20,7 @@
20
20
  "*.css"
21
21
  ],
22
22
  "dependencies": {
23
- "@salt-ds/core": "^1.49.0",
23
+ "@salt-ds/core": "^1.51.0",
24
24
  "clsx": "^2.0.0"
25
25
  },
26
26
  "devDependencies": {