@snack-uikit/carousel 0.6.14-preview-eff3bc02.0 → 0.6.14
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 +12 -0
- package/dist/cjs/components/ItemProvider/ItemProvider.js +20 -12
- package/dist/cjs/components/ItemProvider/styles.module.css +3 -5
- package/dist/esm/components/ItemProvider/ItemProvider.js +20 -16
- package/dist/esm/components/ItemProvider/styles.module.css +3 -5
- package/package.json +5 -5
- package/src/components/ItemProvider/ItemProvider.tsx +23 -18
- package/src/components/ItemProvider/styles.module.scss +9 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## 0.6.14 (2026-08-07)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **PDS-3768:** carousel cards overflow from siblings pages fixed ([a46e4f3](https://github.com/cloud-ru-tech/snack-uikit/commit/a46e4f389f798b2a3b758eb45d3f04e8decc06a8))
|
|
12
|
+
* **PDS-3768:** replaced deprecated lodash specific packages with a global latest lodash to resolve vulnerabilities ([260f60d](https://github.com/cloud-ru-tech/snack-uikit/commit/260f60ddb5e644d44787116e5034d8d8d723a8ac))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
6
18
|
## 0.6.13 (2026-06-24)
|
|
7
19
|
|
|
8
20
|
### Only dependencies have been changed
|
|
@@ -20,12 +20,16 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
20
20
|
});
|
|
21
21
|
exports.ItemProvider = ItemProvider;
|
|
22
22
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
23
|
-
const
|
|
23
|
+
const lodash_1 = require("lodash");
|
|
24
24
|
const merge_refs_1 = __importDefault(require("merge-refs"));
|
|
25
25
|
const react_1 = require("react");
|
|
26
26
|
const utils_1 = require("@snack-uikit/utils");
|
|
27
27
|
const testIds_1 = require("../../testIds");
|
|
28
28
|
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
29
|
+
function parseCssLength(value) {
|
|
30
|
+
const parsed = Number.parseFloat(value);
|
|
31
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
32
|
+
}
|
|
29
33
|
function ItemProvider(_ref) {
|
|
30
34
|
let items = _ref.items,
|
|
31
35
|
showItems = _ref.showItems,
|
|
@@ -37,6 +41,7 @@ function ItemProvider(_ref) {
|
|
|
37
41
|
page = _ref.page,
|
|
38
42
|
gap = _ref.gap;
|
|
39
43
|
const containerRef = (0, react_1.useRef)(null);
|
|
44
|
+
const itemsTrackerRef = (0, react_1.useRef)(null);
|
|
40
45
|
const _ref2 = (0, react_1.useState)({
|
|
41
46
|
itemWidth: 0,
|
|
42
47
|
gap: 0
|
|
@@ -46,30 +51,33 @@ function ItemProvider(_ref) {
|
|
|
46
51
|
setComputedValues = _ref3[1];
|
|
47
52
|
const recalculateItemsSize = (0, react_1.useCallback)(() => {
|
|
48
53
|
const containerElement = containerRef.current;
|
|
49
|
-
|
|
54
|
+
const trackerElement = itemsTrackerRef.current;
|
|
55
|
+
if (!containerElement || !trackerElement) {
|
|
50
56
|
return;
|
|
51
57
|
}
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const
|
|
58
|
+
const containerStyles = getComputedStyle(containerElement);
|
|
59
|
+
const trackerStyles = getComputedStyle(trackerElement);
|
|
60
|
+
const gapValue = gap ? parseCssLength(gap) : parseCssLength(trackerStyles.columnGap || trackerStyles.gap);
|
|
61
|
+
const paddingLeft = parseCssLength(containerStyles.paddingLeft);
|
|
62
|
+
const paddingRight = parseCssLength(containerStyles.paddingRight);
|
|
63
|
+
const gapsCount = Math.max(Math.trunc(showItems) - 1, 0);
|
|
64
|
+
const contentWidth = containerElement.getBoundingClientRect().width - paddingLeft - paddingRight;
|
|
65
|
+
const itemWidth = (contentWidth - gapsCount * gapValue) / showItems;
|
|
57
66
|
setComputedValues({
|
|
58
67
|
itemWidth,
|
|
59
|
-
gap
|
|
68
|
+
gap: gapValue
|
|
60
69
|
});
|
|
61
|
-
}, [showItems]);
|
|
62
|
-
(0,
|
|
70
|
+
}, [showItems, gap]);
|
|
71
|
+
(0, utils_1.useLayoutEffect)(() => {
|
|
63
72
|
const node = containerRef.current;
|
|
64
73
|
if (!node) {
|
|
65
74
|
return;
|
|
66
75
|
}
|
|
67
76
|
recalculateItemsSize();
|
|
68
|
-
const observer = new ResizeObserver((0,
|
|
77
|
+
const observer = new ResizeObserver((0, lodash_1.debounce)(recalculateItemsSize, 100));
|
|
69
78
|
observer.observe(node);
|
|
70
79
|
return () => observer.disconnect();
|
|
71
80
|
}, [recalculateItemsSize]);
|
|
72
|
-
const itemsTrackerRef = (0, react_1.useRef)(null);
|
|
73
81
|
const hideNonVisibleItems = () => {
|
|
74
82
|
var _a;
|
|
75
83
|
(_a = itemsTrackerRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll("[data-test-id=".concat(testIds_1.TEST_IDS.trackItem, "]")).forEach(slide => {
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
.itemProvider{
|
|
2
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
3
|
overflow-x:hidden;
|
|
8
4
|
width:100%;
|
|
9
|
-
margin:calc(0px - var(--space-carousel-content-layout-padding, 4px));
|
|
5
|
+
margin-block:calc(0px - var(--space-carousel-content-layout-padding, 4px));
|
|
6
|
+
padding-block:var(--space-carousel-content-layout-padding, 4px);
|
|
10
7
|
}
|
|
11
8
|
.itemProvider[data-swipe]{
|
|
12
9
|
cursor:grab;
|
|
@@ -22,6 +19,7 @@
|
|
|
22
19
|
.itemContainer{
|
|
23
20
|
flex-shrink:0;
|
|
24
21
|
box-sizing:border-box;
|
|
22
|
+
padding:var(--border-width-card-container, 1px);
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
.itemTracker{
|
|
@@ -10,35 +10,40 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
-
import debounce from 'lodash
|
|
13
|
+
import { debounce } from 'lodash';
|
|
14
14
|
import mergeRefs from 'merge-refs';
|
|
15
15
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
16
|
-
import { useSwipeable } from '@snack-uikit/utils';
|
|
16
|
+
import { useLayoutEffect, useSwipeable } from '@snack-uikit/utils';
|
|
17
17
|
import { TEST_IDS } from '../../testIds';
|
|
18
18
|
import styles from './styles.module.css';
|
|
19
|
+
function parseCssLength(value) {
|
|
20
|
+
const parsed = Number.parseFloat(value);
|
|
21
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
22
|
+
}
|
|
19
23
|
export function ItemProvider({ items, showItems, scrollBy, slideCallback, transition, swipe, swipeActivateLength, page, gap, }) {
|
|
20
24
|
const containerRef = useRef(null);
|
|
25
|
+
const itemsTrackerRef = useRef(null);
|
|
21
26
|
const [computedValues, setComputedValues] = useState({
|
|
22
27
|
itemWidth: 0,
|
|
23
28
|
gap: 0,
|
|
24
29
|
});
|
|
25
30
|
const recalculateItemsSize = useCallback(() => {
|
|
26
31
|
const containerElement = containerRef.current;
|
|
27
|
-
|
|
32
|
+
const trackerElement = itemsTrackerRef.current;
|
|
33
|
+
if (!containerElement || !trackerElement) {
|
|
28
34
|
return;
|
|
29
35
|
}
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
36
|
+
const containerStyles = getComputedStyle(containerElement);
|
|
37
|
+
const trackerStyles = getComputedStyle(trackerElement);
|
|
38
|
+
const gapValue = gap ? parseCssLength(gap) : parseCssLength(trackerStyles.columnGap || trackerStyles.gap);
|
|
39
|
+
const paddingLeft = parseCssLength(containerStyles.paddingLeft);
|
|
40
|
+
const paddingRight = parseCssLength(containerStyles.paddingRight);
|
|
41
|
+
const gapsCount = Math.max(Math.trunc(showItems) - 1, 0);
|
|
42
|
+
const contentWidth = containerElement.getBoundingClientRect().width - paddingLeft - paddingRight;
|
|
43
|
+
const itemWidth = (contentWidth - gapsCount * gapValue) / showItems;
|
|
44
|
+
setComputedValues({ itemWidth, gap: gapValue });
|
|
45
|
+
}, [showItems, gap]);
|
|
46
|
+
useLayoutEffect(() => {
|
|
42
47
|
const node = containerRef.current;
|
|
43
48
|
if (!node) {
|
|
44
49
|
return;
|
|
@@ -48,7 +53,6 @@ export function ItemProvider({ items, showItems, scrollBy, slideCallback, transi
|
|
|
48
53
|
observer.observe(node);
|
|
49
54
|
return () => observer.disconnect();
|
|
50
55
|
}, [recalculateItemsSize]);
|
|
51
|
-
const itemsTrackerRef = useRef(null);
|
|
52
56
|
const hideNonVisibleItems = () => {
|
|
53
57
|
var _a;
|
|
54
58
|
(_a = itemsTrackerRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`[data-test-id=${TEST_IDS.trackItem}]`).forEach(slide => {
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
.itemProvider{
|
|
2
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
3
|
overflow-x:hidden;
|
|
8
4
|
width:100%;
|
|
9
|
-
margin:calc(0px - var(--space-carousel-content-layout-padding, 4px));
|
|
5
|
+
margin-block:calc(0px - var(--space-carousel-content-layout-padding, 4px));
|
|
6
|
+
padding-block:var(--space-carousel-content-layout-padding, 4px);
|
|
10
7
|
}
|
|
11
8
|
.itemProvider[data-swipe]{
|
|
12
9
|
cursor:grab;
|
|
@@ -22,6 +19,7 @@
|
|
|
22
19
|
.itemContainer{
|
|
23
20
|
flex-shrink:0;
|
|
24
21
|
box-sizing:border-box;
|
|
22
|
+
padding:var(--border-width-card-container, 1px);
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
.itemTracker{
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.6.14
|
|
7
|
+
"version": "0.6.14",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
"license": "Apache-2.0",
|
|
37
37
|
"scripts": {},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@snack-uikit/icons": "0.27.8
|
|
40
|
-
"@snack-uikit/pagination": "0.10.27
|
|
39
|
+
"@snack-uikit/icons": "0.27.8",
|
|
40
|
+
"@snack-uikit/pagination": "0.10.27",
|
|
41
41
|
"@snack-uikit/utils": "4.0.2",
|
|
42
42
|
"classnames": "2.5.1",
|
|
43
|
-
"lodash
|
|
43
|
+
"lodash": "4.18.1",
|
|
44
44
|
"merge-refs": "1.3.0",
|
|
45
45
|
"uncontrollable": "8.0.4"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "9e6abdd71a846caf1f1252b9504560a3b796942f"
|
|
48
48
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import debounce from 'lodash
|
|
1
|
+
import { debounce } from 'lodash';
|
|
2
2
|
import mergeRefs from 'merge-refs';
|
|
3
3
|
import { MouseEventHandler, ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
|
|
5
|
-
import { SwipeCallback, useSwipeable } from '@snack-uikit/utils';
|
|
5
|
+
import { SwipeCallback, useLayoutEffect, useSwipeable } from '@snack-uikit/utils';
|
|
6
6
|
|
|
7
7
|
import { TEST_IDS } from '../../testIds';
|
|
8
8
|
import styles from './styles.module.scss';
|
|
@@ -19,6 +19,12 @@ export type ItemProviderProps = {
|
|
|
19
19
|
gap?: string;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
function parseCssLength(value: string): number {
|
|
23
|
+
const parsed = Number.parseFloat(value);
|
|
24
|
+
|
|
25
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
22
28
|
export function ItemProvider({
|
|
23
29
|
items,
|
|
24
30
|
showItems,
|
|
@@ -31,6 +37,7 @@ export function ItemProvider({
|
|
|
31
37
|
gap,
|
|
32
38
|
}: ItemProviderProps) {
|
|
33
39
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
40
|
+
const itemsTrackerRef = useRef<HTMLDivElement>(null);
|
|
34
41
|
|
|
35
42
|
const [computedValues, setComputedValues] = useState({
|
|
36
43
|
itemWidth: 0,
|
|
@@ -39,27 +46,27 @@ export function ItemProvider({
|
|
|
39
46
|
|
|
40
47
|
const recalculateItemsSize = useCallback(() => {
|
|
41
48
|
const containerElement = containerRef.current;
|
|
49
|
+
const trackerElement = itemsTrackerRef.current;
|
|
42
50
|
|
|
43
|
-
if (!containerElement) {
|
|
51
|
+
if (!containerElement || !trackerElement) {
|
|
44
52
|
return;
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
const
|
|
55
|
+
const containerStyles = getComputedStyle(containerElement);
|
|
56
|
+
const trackerStyles = getComputedStyle(trackerElement);
|
|
48
57
|
|
|
49
|
-
const
|
|
50
|
-
const paddingLeft =
|
|
51
|
-
const paddingRight =
|
|
58
|
+
const gapValue = gap ? parseCssLength(gap) : parseCssLength(trackerStyles.columnGap || trackerStyles.gap);
|
|
59
|
+
const paddingLeft = parseCssLength(containerStyles.paddingLeft);
|
|
60
|
+
const paddingRight = parseCssLength(containerStyles.paddingRight);
|
|
52
61
|
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
paddingLeft -
|
|
57
|
-
paddingRight) /
|
|
58
|
-
showItems;
|
|
59
|
-
setComputedValues({ itemWidth, gap });
|
|
60
|
-
}, [showItems]);
|
|
62
|
+
const gapsCount = Math.max(Math.trunc(showItems) - 1, 0);
|
|
63
|
+
const contentWidth = containerElement.getBoundingClientRect().width - paddingLeft - paddingRight;
|
|
64
|
+
const itemWidth = (contentWidth - gapsCount * gapValue) / showItems;
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
setComputedValues({ itemWidth, gap: gapValue });
|
|
67
|
+
}, [showItems, gap]);
|
|
68
|
+
|
|
69
|
+
useLayoutEffect(() => {
|
|
63
70
|
const node = containerRef.current;
|
|
64
71
|
|
|
65
72
|
if (!node) {
|
|
@@ -73,8 +80,6 @@ export function ItemProvider({
|
|
|
73
80
|
return () => observer.disconnect();
|
|
74
81
|
}, [recalculateItemsSize]);
|
|
75
82
|
|
|
76
|
-
const itemsTrackerRef = useRef<HTMLDivElement>(null);
|
|
77
|
-
|
|
78
83
|
const hideNonVisibleItems = () => {
|
|
79
84
|
itemsTrackerRef.current?.querySelectorAll(`[data-test-id=${TEST_IDS.trackItem}]`).forEach(slide => {
|
|
80
85
|
slide.setAttribute('aria-hidden', '0');
|
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
.itemProvider {
|
|
4
4
|
--gap: #{styles-tokens-carousel.$dimension-2m};
|
|
5
5
|
|
|
6
|
-
@include styles-tokens-carousel.composite-var(styles-tokens-carousel.$carousel-content-layout);
|
|
7
|
-
|
|
8
6
|
overflow-x: hidden;
|
|
9
7
|
width: 100%;
|
|
10
|
-
margin: calc(0px - styles-tokens-carousel.$space-carousel-content-layout-padding);
|
|
8
|
+
margin-block: calc(0px - #{styles-tokens-carousel.$space-carousel-content-layout-padding});
|
|
9
|
+
|
|
10
|
+
// Only block padding: inline padding created a gutter where the next item
|
|
11
|
+
// (and its outline) painted through when gap < padding. Card outline is
|
|
12
|
+
// contained by .itemContainer padding instead.
|
|
13
|
+
padding-block: styles-tokens-carousel.$space-carousel-content-layout-padding;
|
|
11
14
|
|
|
12
15
|
&[data-swipe] {
|
|
13
16
|
cursor: grab;
|
|
@@ -24,6 +27,9 @@
|
|
|
24
27
|
.itemContainer {
|
|
25
28
|
flex-shrink: 0;
|
|
26
29
|
box-sizing: border-box;
|
|
30
|
+
// Reserve space inside the slide for card outline/border so it is not clipped
|
|
31
|
+
// by overflow and does not paint into a shared gutter with the next slide.
|
|
32
|
+
padding: styles-tokens-carousel.$border-width-card-container;
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
.itemTracker {
|