@snack-uikit/carousel 0.2.1

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +201 -0
  3. package/README.md +29 -0
  4. package/dist/components/Carousel/Carousel.d.ts +30 -0
  5. package/dist/components/Carousel/Carousel.js +53 -0
  6. package/dist/components/Carousel/index.d.ts +1 -0
  7. package/dist/components/Carousel/index.js +1 -0
  8. package/dist/components/Carousel/styles.module.css +21 -0
  9. package/dist/components/Control/Control.d.ts +7 -0
  10. package/dist/components/Control/Control.js +22 -0
  11. package/dist/components/Control/index.d.ts +1 -0
  12. package/dist/components/Control/index.js +1 -0
  13. package/dist/components/Control/styles.module.css +45 -0
  14. package/dist/components/ItemProvider/ItemProvider.d.ts +12 -0
  15. package/dist/components/ItemProvider/ItemProvider.js +113 -0
  16. package/dist/components/ItemProvider/helpers.d.ts +2 -0
  17. package/dist/components/ItemProvider/helpers.js +9 -0
  18. package/dist/components/ItemProvider/hooks.d.ts +1 -0
  19. package/dist/components/ItemProvider/hooks.js +20 -0
  20. package/dist/components/ItemProvider/index.d.ts +1 -0
  21. package/dist/components/ItemProvider/index.js +1 -0
  22. package/dist/components/ItemProvider/styles.module.css +39 -0
  23. package/dist/components/index.d.ts +1 -0
  24. package/dist/components/index.js +1 -0
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.js +1 -0
  27. package/dist/testIds.d.ts +7 -0
  28. package/dist/testIds.js +7 -0
  29. package/package.json +42 -0
  30. package/src/components/Carousel/Carousel.tsx +135 -0
  31. package/src/components/Carousel/index.ts +1 -0
  32. package/src/components/Carousel/styles.module.scss +27 -0
  33. package/src/components/Control/Control.tsx +28 -0
  34. package/src/components/Control/index.ts +1 -0
  35. package/src/components/Control/styles.module.scss +54 -0
  36. package/src/components/ItemProvider/ItemProvider.tsx +198 -0
  37. package/src/components/ItemProvider/helpers.ts +11 -0
  38. package/src/components/ItemProvider/hooks.ts +26 -0
  39. package/src/components/ItemProvider/index.ts +1 -0
  40. package/src/components/ItemProvider/styles.module.scss +42 -0
  41. package/src/components/index.ts +1 -0
  42. package/src/index.ts +1 -0
  43. package/src/testIds.ts +7 -0
@@ -0,0 +1 @@
1
+ export * from './ItemProvider';
@@ -0,0 +1 @@
1
+ export * from './ItemProvider';
@@ -0,0 +1,39 @@
1
+ .itemProvider{
2
+ --gap:var(--dimension-2m, 16px);
3
+ padding-top:var(--space-carousel-content-layout-padding, 4px);
4
+ padding-bottom:var(--space-carousel-content-layout-padding, 4px);
5
+ padding-left:var(--space-carousel-content-layout-padding, 4px);
6
+ padding-right:var(--space-carousel-content-layout-padding, 4px);
7
+ overflow:hidden;
8
+ width:100%;
9
+ margin:calc(0px - var(--space-carousel-content-layout-padding, 4px));
10
+ }
11
+ .itemProvider[data-swipe]{
12
+ cursor:grab;
13
+ }
14
+ .itemProvider[data-swipe][data-pointers]{
15
+ cursor:grabbing;
16
+ -webkit-user-select:none;
17
+ -moz-user-select:none;
18
+ user-select:none;
19
+ -webkit-user-drag:none;
20
+ }
21
+
22
+ .itemTracker{
23
+ display:flex;
24
+ gap:var(--gap, var(--dimension-2m, 16px));
25
+ box-sizing:border-box;
26
+ height:100%;
27
+ }
28
+
29
+ .itemContainer{
30
+ flex-shrink:0;
31
+ box-sizing:border-box;
32
+ }
33
+
34
+ .hiddenItem{
35
+ pointer-events:none;
36
+ }
37
+ .hiddenItem *{
38
+ pointer-events:none;
39
+ }
@@ -0,0 +1 @@
1
+ export * from './Carousel';
@@ -0,0 +1 @@
1
+ export * from './Carousel';
@@ -0,0 +1 @@
1
+ export * from './components';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './components';
@@ -0,0 +1,7 @@
1
+ export declare const TEST_IDS: {
2
+ arrowNext: string;
3
+ arrowPrev: string;
4
+ pagination: string;
5
+ trackLine: string;
6
+ trackItem: string;
7
+ };
@@ -0,0 +1,7 @@
1
+ export const TEST_IDS = {
2
+ arrowNext: 'carousel__arrow-next',
3
+ arrowPrev: 'carousel__arrow-prev',
4
+ pagination: 'carousel__pagination',
5
+ trackLine: 'carousel__track-line',
6
+ trackItem: 'carousel__track-item',
7
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@snack-uikit/carousel",
3
+ "title": "Carousel",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "version": "0.2.1",
8
+ "sideEffects": [
9
+ "*.css",
10
+ "*.woff",
11
+ "*.woff2"
12
+ ],
13
+ "description": "",
14
+ "main": "./dist/index.js",
15
+ "module": "./dist/index.js",
16
+ "homepage": "https://github.com/cloud-ru-tech/snack-uikit/tree/master/packages/carousel",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/cloud-ru-tech/snack-uikit.git",
20
+ "directory": "packages/carousel"
21
+ },
22
+ "author": "Nikita Ershov <niershov@cloud.ru>",
23
+ "contributors": [
24
+ "Nikita Ershov <niershov@cloud.ru>"
25
+ ],
26
+ "files": [
27
+ "dist",
28
+ "src",
29
+ "./CHANGELOG.md",
30
+ "./LICENSE"
31
+ ],
32
+ "license": "Apache-2.0",
33
+ "scripts": {},
34
+ "dependencies": {
35
+ "@snack-uikit/icons": "0.19.1",
36
+ "@snack-uikit/pagination": "0.5.3",
37
+ "@snack-uikit/utils": "3.2.0",
38
+ "classnames": "2.3.2",
39
+ "uncontrollable": "8.0.4"
40
+ },
41
+ "gitHead": "9a8ce098f22356776cffc78875caa94bc72397a5"
42
+ }
@@ -0,0 +1,135 @@
1
+ import cn from 'classnames';
2
+ import { ReactElement, useCallback, useEffect, useMemo, useRef } from 'react';
3
+ import { useUncontrolledProp } from 'uncontrollable';
4
+
5
+ import { PaginationSlider } from '@snack-uikit/pagination';
6
+ import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
7
+
8
+ import { TEST_IDS } from '../../testIds';
9
+ import { Control } from '../Control';
10
+ import { ItemProvider } from '../ItemProvider';
11
+ import styles from './styles.module.scss';
12
+
13
+ export type CarouselProps = WithSupportProps<{
14
+ /** CSS - класснейм */
15
+ className?: string;
16
+ /** Массив айтемов */
17
+ children: ReactElement[];
18
+ /** Кол-во отображаемых единовременно айтемов @default 1 */
19
+ showItems?: number;
20
+ /** Сдвиг айтемов при смене 1 страницы @default Math.trunc(show) */
21
+ scrollBy?: number;
22
+ /** Время переключения 1 страницы (в s) @default 0.4 */
23
+ transition?: number;
24
+ /** Переключение страниц свайпом @default true */
25
+ swipe?: boolean;
26
+ /** Использовать стрелки для переключения страниц @default true */
27
+ arrows?: boolean;
28
+ /** Использовать пагинацию для переключения страниц @default true */
29
+ pagination?: boolean;
30
+ /** Расстояние между айтемами @default var(--dimension-2m)*/
31
+ gap?: string;
32
+ /** Управление состоянием извне */
33
+ state?: {
34
+ page: number;
35
+ onChange(page: number): void;
36
+ };
37
+ /** Цикличная прокрутка @default false*/
38
+ infiniteScroll?: boolean;
39
+ }>;
40
+
41
+ export function Carousel({
42
+ children,
43
+ showItems = 1,
44
+ scrollBy: scrollByProp,
45
+ transition = 0.4,
46
+ swipe = true,
47
+ arrows = true,
48
+ pagination = true,
49
+ className,
50
+ gap,
51
+ state,
52
+ infiniteScroll = false,
53
+ ...rest
54
+ }: CarouselProps) {
55
+ const initialItems = children;
56
+ const itemsRef = useRef(children);
57
+
58
+ useEffect(() => {
59
+ itemsRef.current = initialItems;
60
+ // eslint-disable-next-line react-hooks/exhaustive-deps
61
+ }, [initialItems.length]);
62
+
63
+ const scrollBy = useMemo(() => scrollByProp ?? Math.trunc(showItems), [showItems, scrollByProp]);
64
+
65
+ const [page, setPage] = useUncontrolledProp<number>(state?.page, state?.page ?? 0, newPage => {
66
+ const result = typeof newPage === 'function' ? newPage(page) : newPage;
67
+ state?.onChange(result);
68
+ });
69
+
70
+ const total = useMemo(
71
+ () => Math.trunc(initialItems.length / scrollBy) + (initialItems.length % scrollBy ? 1 : 0),
72
+ [initialItems.length, scrollBy],
73
+ );
74
+
75
+ const onLeftArrowClick = useCallback(() => {
76
+ setPage((page: number) => (infiniteScroll ? (total + page - 1) % total : Math.max(0, page - 1)));
77
+ }, [infiniteScroll, setPage, total]);
78
+
79
+ const onRightArrowClick = useCallback(() => {
80
+ setPage((page: number) => (infiniteScroll ? (page + 1) % total : Math.min(total - 1, page + 1)));
81
+ }, [infiniteScroll, setPage, total]);
82
+
83
+ const slideCallback = useCallback(
84
+ (direction: number) => {
85
+ if (direction < 0) {
86
+ onRightArrowClick();
87
+ return;
88
+ }
89
+
90
+ onLeftArrowClick();
91
+ },
92
+ [onLeftArrowClick, onRightArrowClick],
93
+ );
94
+
95
+ return (
96
+ <div className={cn(styles.carousel, className)} {...extractSupportProps(rest)}>
97
+ <div className={styles.carouselBase}>
98
+ <ItemProvider
99
+ showItems={showItems}
100
+ scrollBy={scrollBy}
101
+ swipe={swipe}
102
+ transition={transition}
103
+ items={itemsRef.current}
104
+ slideCallback={slideCallback}
105
+ page={page}
106
+ gap={gap}
107
+ />
108
+
109
+ {arrows && (
110
+ <>
111
+ {(infiniteScroll || page > 0) && (
112
+ <Control onClick={onLeftArrowClick} variant='prev' data-test-id={TEST_IDS.arrowPrev} />
113
+ )}
114
+ {(infiniteScroll || page + 1 < total) && (
115
+ <Control onClick={onRightArrowClick} variant='next' data-test-id={TEST_IDS.arrowNext} />
116
+ )}
117
+ </>
118
+ )}
119
+ </div>
120
+
121
+ {pagination && (
122
+ <div className={styles.pagination}>
123
+ <PaginationSlider
124
+ data-test-id={TEST_IDS.pagination}
125
+ page={page + 1}
126
+ onChange={(page: number) => {
127
+ setPage(page - 1);
128
+ }}
129
+ total={total}
130
+ />
131
+ </div>
132
+ )}
133
+ </div>
134
+ );
135
+ }
@@ -0,0 +1 @@
1
+ export * from './Carousel'
@@ -0,0 +1,27 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-carousel';
2
+
3
+ .carouselBase {
4
+ position: relative;
5
+
6
+ display: flex;
7
+
8
+ box-sizing: border-box;
9
+ width: 100%;
10
+
11
+ outline: none;
12
+ }
13
+
14
+ .carousel {
15
+ display: flex;
16
+ flex-direction: column;
17
+ align-items: center;
18
+ box-sizing: border-box;
19
+ }
20
+
21
+ .pagination {
22
+ @include composite-var($carousel-sliders-wrap);
23
+
24
+ display: flex;
25
+ justify-content: center;
26
+ box-sizing: border-box;
27
+ }
@@ -0,0 +1,28 @@
1
+ import { forwardRef } from 'react';
2
+
3
+ import { ChevronLeftSVG } from '@snack-uikit/icons';
4
+ import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
5
+
6
+ import styles from './styles.module.scss';
7
+
8
+ type ControlProps = WithSupportProps<{
9
+ onClick?(): void;
10
+ variant: 'prev' | 'next';
11
+ }>;
12
+
13
+ export const Control = forwardRef<HTMLButtonElement, ControlProps>(
14
+ ({ onClick, variant, ...rest }: ControlProps, ref) => (
15
+ <button
16
+ ref={ref}
17
+ className={styles.control}
18
+ onClick={() => {
19
+ onClick?.();
20
+ }}
21
+ type='button'
22
+ data-variant={variant}
23
+ {...extractSupportProps(rest)}
24
+ >
25
+ <ChevronLeftSVG size={24} className={styles.icon} />
26
+ </button>
27
+ ),
28
+ );
@@ -0,0 +1 @@
1
+ export * from './Control'
@@ -0,0 +1,54 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-carousel';
2
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
3
+
4
+ .control {
5
+ @include composite-var($carousel-control-container);
6
+
7
+ cursor: pointer;
8
+
9
+ position: absolute;
10
+ top: 50%;
11
+ left: calc($size-carousel-control-wrap-width - $size-carousel-control-container);
12
+ transform: translateY(-50%);
13
+
14
+ display: flex;
15
+ flex-shrink: 0;
16
+ align-items: center;
17
+ justify-content: center;
18
+
19
+ color: $sys-neutral-text-light;
20
+
21
+ background-color: $sys-neutral-background1-level;
22
+ border-color: transparent;
23
+ border-style: solid;
24
+ outline-color: transparent;
25
+ box-shadow: $box-shadow-elevation-level3;
26
+
27
+ &:hover, &:focus-visible {
28
+ color: $sys-neutral-text-main;
29
+ }
30
+
31
+ &:hover {
32
+ box-shadow: $box-shadow-elevation-level4;
33
+ }
34
+
35
+ &:focus-visible {
36
+ @include outline-var($container-focused-s);
37
+
38
+ border-color: $sys-available-complementary;
39
+ outline-color: $sys-available-on-complementary;
40
+ box-shadow: none;
41
+ }
42
+
43
+ &[data-variant='next'] {
44
+ right: calc($size-carousel-control-wrap-width - $size-carousel-control-container);
45
+ left: auto;
46
+ &:focus-visible {
47
+ outline-offset: $dimension-025m;
48
+ }
49
+
50
+ .icon {
51
+ transform: rotate(180deg);
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,198 @@
1
+ import { MouseEvent, ReactElement, TouchEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+
3
+ import { TEST_IDS } from '../../testIds';
4
+ import { getPageX } from './helpers';
5
+ import { useWindowWidthChange } from './hooks';
6
+ import styles from './styles.module.scss';
7
+
8
+ export type ItemProviderProps = {
9
+ items: ReactElement[];
10
+ showItems: number;
11
+ scrollBy: number;
12
+ slideCallback(direction: number): void;
13
+ transition: number;
14
+ swipe: boolean;
15
+ page: number;
16
+ gap?: string;
17
+ };
18
+
19
+ export function ItemProvider({
20
+ items,
21
+ showItems,
22
+ scrollBy,
23
+ slideCallback,
24
+ transition,
25
+ swipe,
26
+ page,
27
+ gap,
28
+ }: ItemProviderProps) {
29
+ const [computedValues, setComputedValues] = useState({
30
+ itemWidth: 200,
31
+ gap: 0,
32
+ });
33
+
34
+ const ref = useCallback(
35
+ (node: HTMLDivElement) => {
36
+ if (node !== null) {
37
+ const styles = getComputedStyle(node);
38
+
39
+ const gap = Number.parseFloat(styles.getPropertyValue('--gap'));
40
+ const paddingLeft = Number.parseFloat(styles.getPropertyValue('padding-left'));
41
+ const paddingRight = Number.parseFloat(styles.getPropertyValue('padding-right'));
42
+
43
+ const itemWidth =
44
+ (node.getBoundingClientRect().width - (Math.trunc(showItems) - 1) * gap - paddingLeft - paddingRight) /
45
+ showItems;
46
+ setComputedValues({ itemWidth, gap });
47
+ }
48
+ },
49
+ // eslint-disable-next-line react-hooks/exhaustive-deps
50
+ [computedValues.itemWidth, showItems],
51
+ );
52
+
53
+ const itemsTrackerRef = useRef<HTMLDivElement>(null);
54
+
55
+ function hideNonVisibleItems() {
56
+ itemsTrackerRef.current?.querySelectorAll(`[data-test-id=${TEST_IDS.trackItem}]`).forEach(function (slide) {
57
+ slide.setAttribute('aria-hidden', '0');
58
+
59
+ slide.querySelectorAll('a, button, select, input, textarea, [tabindex="0"]').forEach(function (focusableElement) {
60
+ focusableElement.setAttribute('tabindex', '-5');
61
+ focusableElement.classList.add(styles.hiddenItem);
62
+ });
63
+ });
64
+ }
65
+
66
+ function showVisibleItems(slide: number, page: number, show: number) {
67
+ itemsTrackerRef.current?.querySelectorAll(`[data-test-id=${TEST_IDS.trackItem}]`).forEach((item, i) => {
68
+ if (i >= page * slide && i < page * slide + Math.trunc(show)) {
69
+ item.removeAttribute('aria-hidden');
70
+ item
71
+ .querySelectorAll('a, button, select, input, textarea, [tabindex="-5"]')
72
+ .forEach(function (focusableElement) {
73
+ focusableElement.setAttribute('tabindex', '0');
74
+ focusableElement.classList.remove(styles.hiddenItem);
75
+ });
76
+ }
77
+ });
78
+ }
79
+
80
+ const transform = useMemo(
81
+ () => Number(-(page * scrollBy * computedValues.itemWidth + computedValues.gap * page * scrollBy)),
82
+ [computedValues.gap, computedValues.itemWidth, page, scrollBy],
83
+ );
84
+
85
+ useEffect(() => {
86
+ hideNonVisibleItems();
87
+ showVisibleItems(scrollBy, page, showItems);
88
+ }, [page, scrollBy, showItems]);
89
+
90
+ useWindowWidthChange((change: number) => {
91
+ setComputedValues(computedValues => ({ ...computedValues, itemWidth: computedValues.itemWidth - change }));
92
+ });
93
+
94
+ const [drag, setDrag] = useState({
95
+ initial: transform,
96
+ start: 0,
97
+ isDown: false,
98
+ drag: 0,
99
+ finished: true,
100
+ pointers: true,
101
+ });
102
+
103
+ const handleDragStart = (e: MouseEvent | TouchEvent) => {
104
+ e.persist();
105
+ setDrag({
106
+ ...drag,
107
+ isDown: true,
108
+ start: getPageX(e),
109
+ initial: transform,
110
+ finished: false,
111
+ });
112
+ };
113
+
114
+ const handleDragFinish = (e: MouseEvent | TouchEvent) => {
115
+ e.persist();
116
+ if (drag.finished) {
117
+ return;
118
+ }
119
+ if (Math.abs(drag.drag) < computedValues.itemWidth / 2) {
120
+ return setDrag({
121
+ initial: transform,
122
+ start: 0,
123
+ isDown: false,
124
+ drag: 0,
125
+ finished: true,
126
+ pointers: true,
127
+ });
128
+ }
129
+
130
+ slideCallback(drag.drag > 0 ? -1 : 1);
131
+ setDrag({ ...drag, drag: 0, isDown: false, finished: true, pointers: true });
132
+ return;
133
+ };
134
+
135
+ const handleDragMove = (e: MouseEvent | TouchEvent) => {
136
+ e.persist();
137
+
138
+ if (!drag.isDown) {
139
+ return;
140
+ }
141
+
142
+ const pos = getPageX(e);
143
+
144
+ setDrag({
145
+ ...drag,
146
+ drag: drag.start - pos,
147
+ pointers: Math.abs(drag.start - pos) < Number.MIN_SAFE_INTEGER,
148
+ });
149
+ };
150
+
151
+ const swipeProps = swipe
152
+ ? {
153
+ onTouchCancel: handleDragFinish,
154
+ onTouchEnd: handleDragFinish,
155
+ onTouchMove: handleDragMove,
156
+ onTouchStart: handleDragStart,
157
+ onMouseDown: handleDragStart,
158
+ onMouseLeave: handleDragFinish,
159
+ onMouseUp: handleDragFinish,
160
+ onMouseMove: handleDragMove,
161
+ }
162
+ : {};
163
+
164
+ return (
165
+ <div
166
+ ref={ref}
167
+ className={styles.itemProvider}
168
+ data-pointers={!drag.pointers || undefined}
169
+ data-swipe={swipe || undefined}
170
+ style={{
171
+ ...(Boolean(gap) ? { '--gap': gap } : {}),
172
+ }}
173
+ >
174
+ <div
175
+ {...swipeProps}
176
+ className={styles.itemTracker}
177
+ data-test-id={TEST_IDS.trackLine}
178
+ style={{
179
+ transform: `translateX(${transform - drag.drag}px)`,
180
+ transition: `transform ${transition}s ease 0s`,
181
+ }}
182
+ ref={itemsTrackerRef}
183
+ >
184
+ {items.map((item, i) => (
185
+ <div
186
+ key={i}
187
+ style={{ width: computedValues.itemWidth }}
188
+ className={styles.itemContainer}
189
+ role='group'
190
+ data-test-id={TEST_IDS.trackItem}
191
+ >
192
+ {item}
193
+ </div>
194
+ ))}
195
+ </div>
196
+ </div>
197
+ );
198
+ }
@@ -0,0 +1,11 @@
1
+ import { MouseEvent, TouchEvent } from 'react';
2
+
3
+ export function getPageX(e: TouchEvent | MouseEvent): number {
4
+ if (e.nativeEvent instanceof globalThis.MouseEvent) {
5
+ return e.nativeEvent.pageX;
6
+ } else if (e.nativeEvent instanceof globalThis.TouchEvent) {
7
+ return e.nativeEvent.changedTouches[0].pageX;
8
+ }
9
+
10
+ return 0;
11
+ }
@@ -0,0 +1,26 @@
1
+ import { useLayoutEffect, useState } from 'react';
2
+
3
+ const getWindowWidth = () => {
4
+ if (typeof window === 'undefined') {
5
+ return 0;
6
+ }
7
+
8
+ return window.innerWidth;
9
+ };
10
+
11
+ export const useWindowWidthChange = (cb: (changed: number) => void) => {
12
+ const [windowWidth, setWindowWidth] = useState(getWindowWidth());
13
+
14
+ useLayoutEffect(() => {
15
+ const update = () => {
16
+ const changed = windowWidth - window.innerWidth;
17
+ setWindowWidth(window.innerWidth);
18
+ cb(changed);
19
+ };
20
+
21
+ window.addEventListener('resize', update);
22
+
23
+ return () => window.removeEventListener('resize', update);
24
+ }, [cb, windowWidth]);
25
+ return;
26
+ };
@@ -0,0 +1 @@
1
+ export * from './ItemProvider'
@@ -0,0 +1,42 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-carousel';
2
+
3
+ .itemProvider {
4
+ --gap: #{$dimension-2m};
5
+
6
+ @include composite-var($carousel-content-layout);
7
+
8
+ overflow: hidden;
9
+ width: 100%;
10
+ margin: calc(0px - $space-carousel-content-layout-padding);
11
+
12
+ &[data-swipe] {
13
+ cursor: grab;
14
+ }
15
+
16
+ &[data-swipe][data-pointers] {
17
+ cursor: grabbing;
18
+ user-select: none;
19
+
20
+ -webkit-user-drag: none;
21
+ }
22
+ }
23
+
24
+ .itemTracker {
25
+ display: flex;
26
+ gap: var(--gap, $dimension-2m);
27
+ box-sizing: border-box;
28
+ height: 100%;
29
+ }
30
+
31
+ .itemContainer {
32
+ flex-shrink: 0;
33
+ box-sizing: border-box;
34
+ }
35
+
36
+ .hiddenItem {
37
+ pointer-events: none;
38
+
39
+ * {
40
+ pointer-events: none;
41
+ }
42
+ }
@@ -0,0 +1 @@
1
+ export * from './Carousel'
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './components';
package/src/testIds.ts ADDED
@@ -0,0 +1,7 @@
1
+ export const TEST_IDS = {
2
+ arrowNext: 'carousel__arrow-next',
3
+ arrowPrev: 'carousel__arrow-prev',
4
+ pagination: 'carousel__pagination',
5
+ trackLine: 'carousel__track-line',
6
+ trackItem: 'carousel__track-item',
7
+ };