@sima-land/moleculas 12.3.0 → 13.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/common/components/adult-block/{adult-block.d.ts → index.d.ts} +1 -2
- package/common/components/adult-block/{adult-block.js → index.js} +2 -1
- package/common/components/badge-list/index.d.ts +2 -6
- package/common/components/badge-list/index.js +26 -5
- package/common/components/product-info/index.d.ts +11 -49
- package/common/components/product-info/index.js +24 -35
- package/common/components/product-info/parts.d.ts +41 -0
- package/common/components/product-info/parts.js +176 -0
- package/common/components/product-info/product-info.module.scss +154 -31
- package/common/components/product-info/types.d.ts +5 -0
- package/common/components/product-info/types.js +2 -0
- package/common/components/product-info/utils.d.ts +3 -0
- package/{mobile/components/product-slider → common/components/product-info}/utils.js +2 -2
- package/common/components/quick-view-button/index.js +1 -1
- package/common/components/wish-button/index.js +1 -1
- package/common/icons/eighteen-plus.svg +3 -1
- package/desktop/components/breadcrumbs/index.js +5 -5
- package/desktop/components/gallery-modal/components/all-round-view.js +11 -11
- package/desktop/components/gallery-modal/components/gallery-modal.js +7 -7
- package/desktop/components/gallery-modal/components/image-overlay.js +1 -1
- package/desktop/components/gallery-modal/components/review-info.js +1 -1
- package/desktop/components/gallery-modal/utils.js +5 -5
- package/desktop/components/item-image/index.d.ts +1 -1
- package/desktop/components/item-image/index.js +8 -6
- package/desktop/components/modifiers/button/index.js +2 -2
- package/desktop/components/modifiers/group/index.js +12 -12
- package/desktop/components/modifiers/widget/index.js +2 -2
- package/desktop/components/person-modal/index.d.ts +0 -1
- package/desktop/components/person-modal/index.js +1 -2
- package/desktop/components/product-card/index.d.ts +11 -24
- package/desktop/components/product-card/index.js +43 -31
- package/desktop/components/product-card/product-card.module.scss +7 -48
- package/desktop/components/product-carousel/hover-card.d.ts +1 -4
- package/desktop/components/product-carousel/hover-card.js +22 -10
- package/desktop/components/product-carousel/index.d.ts +5 -9
- package/desktop/components/product-carousel/index.js +29 -28
- package/desktop/components/product-carousel/utils.js +10 -10
- package/desktop/components/promotion-card/banner-title.js +5 -5
- package/desktop/components/promotion-card/estimate.js +5 -3
- package/desktop/components/promotion-card/index.d.ts +3 -1
- package/desktop/components/promotion-card/index.js +6 -6
- package/desktop/components/promotion-card/placeholder.js +4 -4
- package/mobile/components/garland-calculator-button/garland-calculator-button.module.scss +32 -3
- package/mobile/components/garland-calculator-button/index.d.ts +2 -3
- package/mobile/components/garland-calculator-button/index.js +9 -14
- package/mobile/components/item-image/index.d.ts +4 -4
- package/mobile/components/item-image/index.js +10 -11
- package/mobile/components/modifiers-list/modifier-item/index.js +7 -7
- package/mobile/components/person-screen/index.d.ts +3 -3
- package/mobile/components/person-screen/index.js +16 -14
- package/mobile/components/product-actions-alert/index.d.ts +2 -5
- package/mobile/components/product-actions-alert/index.js +2 -5
- package/mobile/components/product-slider/index.d.ts +6 -9
- package/mobile/components/product-slider/index.js +17 -14
- package/mobile/components/product-slider/{item.module.scss → product-slider.module.scss} +1 -12
- package/mobile/components/pseudo-input/index.d.ts +5 -8
- package/mobile/components/pseudo-input/index.js +21 -10
- package/mobile/components/pseudo-input/pseudo-input.module.scss +6 -9
- package/mobile/components/search-bar/index.js +25 -25
- package/mobile/components/select-screen/index.d.ts +2 -2
- package/mobile/components/select-screen/index.js +7 -18
- package/package.json +30 -30
- package/README.md +0 -86
- package/common/__fixtures__/list-item.d.ts +0 -53
- package/common/__fixtures__/list-item.js +0 -107
- package/common/helpers/get-contained-items.d.ts +0 -7
- package/common/helpers/get-contained-items.js +0 -18
- package/common/icons/filled-rate.svg +0 -3
- package/common/icons/full-left-arrow.svg +0 -1
- package/common/icons/full-right-arrow.svg +0 -1
- package/common/icons/grid-small.svg +0 -11
- package/common/icons/grid.svg +0 -6
- package/common/icons/list.svg +0 -5
- package/common/icons/page.svg +0 -1
- package/common/icons/step-arrows.svg +0 -4
- package/common/icons/triple-dots.svg +0 -3
- package/common/icons/white-spinner.svg +0 -3
- package/desktop/components/breadcrumbs/items.d.ts +0 -3
- package/desktop/components/breadcrumbs/items.js +0 -157
- package/mobile/components/garland-calculator-button/tree-in-circle.svg +0 -6
- package/mobile/components/product-slider/item.d.ts +0 -18
- package/mobile/components/product-slider/item.js +0 -58
- package/mobile/components/product-slider/utils.d.ts +0 -4
|
@@ -1,29 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { ProductInfoProps } from '../../../common/components/product-info';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
onQuickViewClick?: () => void;
|
|
2
|
+
import { ProductInfo, ProductInfoProps } from '../../../common/components/product-info';
|
|
3
|
+
import { PlateProps } from '@sima-land/ui-nucleons/plate';
|
|
4
|
+
export declare type ProductCardChildren = React.ReactElement<ProductInfoProps, typeof ProductInfo>;
|
|
5
|
+
export interface ProductCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
children: ProductCardChildren;
|
|
8
7
|
}
|
|
8
|
+
export declare const HoverCard: React.ForwardRefExoticComponent<PlateProps & React.RefAttributes<HTMLDivElement | null>>;
|
|
9
|
+
export declare const ProductCard: ({ children, className, ...props }: ProductCardProps) => JSX.Element;
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
* @param
|
|
11
|
+
* Фильтрует содержимое карточки для вывода состояния без наведения.
|
|
12
|
+
* @param el Элемент.
|
|
12
13
|
* @return Элемент.
|
|
13
14
|
*/
|
|
14
|
-
declare const
|
|
15
|
-
|
|
16
|
-
stepText?: string | undefined;
|
|
17
|
-
markupText?: string | undefined;
|
|
18
|
-
loading?: boolean | undefined;
|
|
19
|
-
}) => JSX.Element;
|
|
20
|
-
export interface ProductCardComponent extends React.ForwardRefExoticComponent<React.PropsWithoutRef<ProductCardProps> & React.RefAttributes<HTMLDivElement>> {
|
|
21
|
-
CartControl: typeof CartControlSlot;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Карточка товара, появляющаяся над элементом карусели.
|
|
25
|
-
* @param props Свойства компонента.
|
|
26
|
-
* @return Элемент.
|
|
27
|
-
*/
|
|
28
|
-
export declare const ProductCard: ProductCardComponent;
|
|
29
|
-
export {};
|
|
15
|
+
export declare const reduceBaseInfo: (el: ProductCardChildren) => React.FunctionComponentElement<ProductInfoProps>;
|
|
16
|
+
export declare const reduceHoverInfo: (el: ProductCardChildren) => React.FunctionComponentElement<ProductInfoProps>;
|
|
@@ -33,41 +33,53 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
33
33
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
34
34
|
};
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.ProductCard = void 0;
|
|
36
|
+
exports.reduceHoverInfo = exports.reduceBaseInfo = exports.ProductCard = exports.HoverCard = void 0;
|
|
37
37
|
const react_1 = __importStar(require("react"));
|
|
38
38
|
const product_info_1 = require("../../../common/components/product-info");
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const shapes_1 = require("@sima-land/ui-nucleons/styling/shapes");
|
|
42
|
-
const shadows_1 = require("@sima-land/ui-nucleons/styling/shadows");
|
|
43
|
-
const define_slots_1 = require("@sima-land/ui-nucleons/helpers/define-slots");
|
|
44
|
-
const with_prevent_1 = __importDefault(require("@sima-land/ui-nucleons/helpers/with-prevent"));
|
|
45
|
-
const bind_1 = __importDefault(require("classnames/bind"));
|
|
39
|
+
const plate_1 = require("@sima-land/ui-nucleons/plate");
|
|
40
|
+
const classnames_1 = __importDefault(require("classnames"));
|
|
46
41
|
const product_card_module_scss_1 = __importDefault(require("./product-card.module.scss"));
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
!loading && markupText && react_1.default.createElement("div", { className: cx('bottom-text') }, markupText)));
|
|
42
|
+
exports.HoverCard = (0, react_1.forwardRef)((props, ref) => (react_1.default.createElement(plate_1.Plate, Object.assign({}, props, { ref: ref, rounds: 'm', shadow: 'z4', className: (0, classnames_1.default)(product_card_module_scss_1.default.card, props.className), "data-testid": 'product-card:hover-card' }))));
|
|
43
|
+
const ProductCard = (_a) => {
|
|
44
|
+
var { children, className } = _a, props = __rest(_a, ["children", "className"]);
|
|
45
|
+
const [hovered, toggle] = (0, react_1.useState)(false);
|
|
46
|
+
return (react_1.default.createElement("div", Object.assign({}, props, { "data-testid": 'product-card', className: (0, classnames_1.default)(product_card_module_scss_1.default.root, className) }),
|
|
47
|
+
react_1.default.createElement("div", { "data-testid": 'product-card:info', onMouseEnter: () => toggle(true) }, (0, exports.reduceBaseInfo)(children)),
|
|
48
|
+
hovered && (react_1.default.createElement(exports.HoverCard, { onMouseLeave: () => toggle(false) }, (0, exports.reduceHoverInfo)(children)))));
|
|
49
|
+
};
|
|
50
|
+
exports.ProductCard = ProductCard;
|
|
57
51
|
/**
|
|
58
|
-
*
|
|
59
|
-
* @param
|
|
52
|
+
* Фильтрует содержимое карточки для вывода состояния без наведения.
|
|
53
|
+
* @param el Элемент.
|
|
60
54
|
* @return Элемент.
|
|
61
55
|
*/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
56
|
+
const reduceBaseInfo = (el) => (0, react_1.cloneElement)(el, {
|
|
57
|
+
children: react_1.Children.map(el.props.children, child => {
|
|
58
|
+
let result;
|
|
59
|
+
if ((0, react_1.isValidElement)(child)) {
|
|
60
|
+
switch (child.type) {
|
|
61
|
+
// игнорируем кнопки-иконки на изображении
|
|
62
|
+
case product_info_1.Parts.Image:
|
|
63
|
+
result = (0, react_1.cloneElement)(child, { children: undefined });
|
|
64
|
+
break;
|
|
65
|
+
// игнорируем футер
|
|
66
|
+
case product_info_1.Parts.Footer:
|
|
67
|
+
result = null;
|
|
68
|
+
break;
|
|
69
|
+
// остальное оставляем без изменений
|
|
70
|
+
default:
|
|
71
|
+
result = child;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}),
|
|
77
|
+
});
|
|
78
|
+
exports.reduceBaseInfo = reduceBaseInfo;
|
|
79
|
+
const reduceHoverInfo = (el) => (0, react_1.cloneElement)(el, {
|
|
80
|
+
// при наведении у изображения строго непрозрачность = 1
|
|
81
|
+
children: react_1.Children.map(el.props.children, child => (0, react_1.isValidElement)(child) && child.type === product_info_1.Parts.Image
|
|
82
|
+
? (0, react_1.cloneElement)(child, { opacity: 1 })
|
|
83
|
+
: child),
|
|
72
84
|
});
|
|
73
|
-
exports.
|
|
85
|
+
exports.reduceHoverInfo = reduceHoverInfo;
|
|
@@ -1,53 +1,12 @@
|
|
|
1
|
-
@use 'node_modules/@sima-land/ui-nucleons/colors';
|
|
2
|
-
|
|
3
1
|
.root {
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
position: relative;
|
|
3
|
+
max-width: 400px;
|
|
6
4
|
}
|
|
7
5
|
|
|
8
|
-
.
|
|
6
|
+
.card {
|
|
9
7
|
position: absolute;
|
|
10
|
-
top:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
.control-wrapper {
|
|
16
|
-
display: flex;
|
|
17
|
-
flex-wrap: wrap;
|
|
18
|
-
margin-top: 16px;
|
|
19
|
-
&.loading {
|
|
20
|
-
.control {
|
|
21
|
-
background: colors.$gray4;
|
|
22
|
-
border-radius: 4px;
|
|
23
|
-
}
|
|
24
|
-
.unit-text::before {
|
|
25
|
-
content: '';
|
|
26
|
-
display: block;
|
|
27
|
-
background: colors.$gray4;
|
|
28
|
-
border-radius: 4px;
|
|
29
|
-
width: 48px;
|
|
30
|
-
height: 16px;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.control {
|
|
36
|
-
width: 122px;
|
|
37
|
-
height: 40px;
|
|
38
|
-
margin-right: 16px;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.unit-text {
|
|
42
|
-
padding-top: 12px; // чтобы при переносе на следующую строку правильно отступил от кнопки
|
|
43
|
-
font-size: 12px;
|
|
44
|
-
line-height: 16px;
|
|
45
|
-
color: colors.$gray38;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.bottom-text {
|
|
49
|
-
margin-top: 12px;
|
|
50
|
-
font-size: 12px;
|
|
51
|
-
line-height: 16px;
|
|
52
|
-
color: colors.$gray38;
|
|
8
|
+
top: -16px;
|
|
9
|
+
left: -16px;
|
|
10
|
+
padding: 16px 16px 24px;
|
|
11
|
+
width: calc(100% + 32px);
|
|
53
12
|
}
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ProductInfoProps } from '../../../common/components/product-info';
|
|
3
|
-
import { ProductCard } from '../product-card';
|
|
4
3
|
export interface HoverCardProps extends ProductInfoProps {
|
|
5
4
|
targetRef: React.RefObject<HTMLElement | null>;
|
|
6
5
|
onMouseLeave?: () => void;
|
|
7
|
-
onQuickViewClick?: () => void;
|
|
8
|
-
renderCartControl?: (Slot: typeof ProductCard.CartControl) => React.ReactNode;
|
|
9
6
|
}
|
|
10
7
|
/**
|
|
11
8
|
* Карточка товара, появляющаяся над элементом карусели.
|
|
12
9
|
* @param props Свойства компонента.
|
|
13
10
|
* @return Элемент.
|
|
14
11
|
*/
|
|
15
|
-
export declare const HoverCard: ({
|
|
12
|
+
export declare const HoverCard: ({ targetRef, onMouseLeave, ...productInfoProps }: HoverCardProps) => JSX.Element;
|
|
@@ -18,6 +18,17 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
18
18
|
__setModuleDefault(result, mod);
|
|
19
19
|
return result;
|
|
20
20
|
};
|
|
21
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
22
|
+
var t = {};
|
|
23
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
24
|
+
t[p] = s[p];
|
|
25
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
26
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
27
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
28
|
+
t[p[i]] = s[p[i]];
|
|
29
|
+
}
|
|
30
|
+
return t;
|
|
31
|
+
};
|
|
21
32
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
33
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
34
|
};
|
|
@@ -25,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
25
36
|
exports.HoverCard = void 0;
|
|
26
37
|
const react_1 = __importStar(require("react"));
|
|
27
38
|
const utils_1 = require("@sima-land/ui-nucleons/with-tooltip/utils");
|
|
39
|
+
const product_info_1 = require("../../../common/components/product-info");
|
|
28
40
|
const bind_1 = __importDefault(require("classnames/bind"));
|
|
29
41
|
const hover_card_module_scss_1 = __importDefault(require("./hover-card.module.scss"));
|
|
30
42
|
const shapes_1 = require("@sima-land/ui-nucleons/styling/shapes");
|
|
@@ -36,25 +48,25 @@ const cx = bind_1.default.bind(hover_card_module_scss_1.default);
|
|
|
36
48
|
* @param props Свойства компонента.
|
|
37
49
|
* @return Элемент.
|
|
38
50
|
*/
|
|
39
|
-
const HoverCard = (
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
react_1.
|
|
51
|
+
const HoverCard = (_a) => {
|
|
52
|
+
var { targetRef, onMouseLeave } = _a, productInfoProps = __rest(_a, ["targetRef", "onMouseLeave"]);
|
|
53
|
+
const rootRef = (0, react_1.useRef)(null);
|
|
54
|
+
const [ready, setReady] = (0, react_1.useState)(false);
|
|
55
|
+
(0, react_1.useLayoutEffect)(() => {
|
|
43
56
|
if (targetRef.current && rootRef.current) {
|
|
44
57
|
const element = rootRef.current;
|
|
45
58
|
const rect = targetRef.current.getBoundingClientRect();
|
|
46
|
-
const correction = utils_1.getOriginCorrection(rootRef.current);
|
|
59
|
+
const correction = (0, utils_1.getOriginCorrection)(rootRef.current);
|
|
47
60
|
element.style.left = `${correction.x + rect.left - 16}px`;
|
|
48
61
|
element.style.top = `${correction.y + rect.top - 16}px`;
|
|
49
62
|
element.style.width = `${rect.width + 32}px`;
|
|
50
63
|
setReady(true);
|
|
51
64
|
}
|
|
52
|
-
}
|
|
53
|
-
return (react_1.default.createElement(product_card_1.
|
|
54
|
-
// div props
|
|
55
|
-
ref: rootRef, "data-testid": 'product-carousel:hover-card', className: cx('root', !ready && 'hidden', shapes_1.SmallRounds.all, shadows_1.BoxShadow.z4), onMouseLeave: () => {
|
|
65
|
+
});
|
|
66
|
+
return (react_1.default.createElement(product_card_1.HoverCard, { ref: rootRef, style: { padding: '16px 16px 24px 16px' }, className: cx('root', !ready && 'hidden', shapes_1.SmallRounds.all, shadows_1.BoxShadow.z4), onMouseLeave: () => {
|
|
56
67
|
setReady(false);
|
|
57
68
|
onMouseLeave && onMouseLeave();
|
|
58
|
-
} },
|
|
69
|
+
} },
|
|
70
|
+
react_1.default.createElement(product_info_1.ProductInfo, Object.assign({}, productInfoProps))));
|
|
59
71
|
};
|
|
60
72
|
exports.HoverCard = HoverCard;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { ProductInfoProps } from '../../../common/components/product-info';
|
|
2
|
+
import { ProductInfo, ProductInfoProps } from '../../../common/components/product-info';
|
|
4
3
|
interface ItemSize {
|
|
5
4
|
xs?: 2 | 3 | 4;
|
|
6
5
|
s?: 2 | 3 | 4;
|
|
@@ -8,12 +7,13 @@ interface ItemSize {
|
|
|
8
7
|
l?: 2 | 3 | 4;
|
|
9
8
|
xl?: 2 | 3 | 4;
|
|
10
9
|
}
|
|
10
|
+
export declare type ItemElement = React.ReactElement<ProductInfoProps, typeof ProductInfo>;
|
|
11
11
|
export interface ProductCarouselProps {
|
|
12
12
|
/** CSS-класс для корневого элемента. */
|
|
13
13
|
className?: string;
|
|
14
14
|
/** Элементы карусели. */
|
|
15
|
-
children?:
|
|
16
|
-
/**
|
|
15
|
+
children?: ItemElement | ItemElement[];
|
|
16
|
+
/** Настройки размера элемента карусели. */
|
|
17
17
|
itemSize?: ItemSize;
|
|
18
18
|
/** Сработает при попадании карусели во viewport. */
|
|
19
19
|
onInViewport?: () => void;
|
|
@@ -22,14 +22,10 @@ export interface ProductCarouselProps {
|
|
|
22
22
|
/** Нужно ли показывать всплывающую карточку при наведении на элемент карусели. */
|
|
23
23
|
withHoverCard?: boolean;
|
|
24
24
|
}
|
|
25
|
-
declare type CarouselItemProps = ProductInfoProps & Pick<HoverCardProps, 'renderCartControl' | 'onQuickViewClick'>;
|
|
26
25
|
/**
|
|
27
26
|
* Карусель рекомендованных товаров.
|
|
28
27
|
* @param props Свойства.
|
|
29
28
|
* @return Элемент.
|
|
30
29
|
*/
|
|
31
|
-
export declare const ProductCarousel: {
|
|
32
|
-
({ className, itemSize, onInViewport, onNeedRequest, withHoverCard, children, }: ProductCarouselProps): JSX.Element;
|
|
33
|
-
Item: (props: CarouselItemProps) => null;
|
|
34
|
-
};
|
|
30
|
+
export declare const ProductCarousel: ({ className, itemSize, onInViewport, onNeedRequest, withHoverCard, children, }: ProductCarouselProps) => JSX.Element;
|
|
35
31
|
export {};
|
|
@@ -39,38 +39,34 @@ const getSizeClasses = (size = {}) => [
|
|
|
39
39
|
`size-l-${size.l || 2}`,
|
|
40
40
|
`size-xl-${size.xl || 2}`,
|
|
41
41
|
];
|
|
42
|
-
/**
|
|
43
|
-
* Компонент для проброса данных элемента карусели в компонент карусели.
|
|
44
|
-
* Экспериментальный вариант.
|
|
45
|
-
* @return Null.
|
|
46
|
-
*/
|
|
47
|
-
const Item = () => null;
|
|
48
42
|
/**
|
|
49
43
|
* Карусель рекомендованных товаров.
|
|
50
44
|
* @param props Свойства.
|
|
51
45
|
* @return Элемент.
|
|
52
46
|
*/
|
|
53
47
|
const ProductCarousel = ({ className, itemSize, onInViewport, onNeedRequest, withHoverCard, children, }) => {
|
|
54
|
-
const [activeItemIndex, setActiveItemIndex] = react_1.useState(null);
|
|
55
|
-
const cardShow = utils_1.useAllowFlag();
|
|
56
|
-
const needBigArrows = media_1.useMedia('(min-width: 1600px)');
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
48
|
+
const [activeItemIndex, setActiveItemIndex] = (0, react_1.useState)(null);
|
|
49
|
+
const cardShow = (0, utils_1.useAllowFlag)();
|
|
50
|
+
const needBigArrows = (0, media_1.useMedia)('(min-width: 1600px)');
|
|
51
|
+
const rootRef = (0, react_1.useRef)(null);
|
|
52
|
+
const targetItemRef = (0, react_1.useRef)(null);
|
|
53
|
+
let items;
|
|
54
|
+
if (Array.isArray(children)) {
|
|
55
|
+
items = children;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
items = children ? [children] : [];
|
|
59
|
+
}
|
|
64
60
|
// вычисляем ширину элемента карусели для позиционирования стрелок
|
|
65
|
-
const itemWidth = utils_1.useChildWidth(
|
|
61
|
+
const itemWidth = (0, utils_1.useChildWidth)(rootRef, `.${cx('item')}`, [items.length]);
|
|
66
62
|
// инициируем загрузку данных, когда компонент почти попал в зону видимости
|
|
67
|
-
utils_1.useViewport(
|
|
63
|
+
(0, utils_1.useViewport)(rootRef, onNeedRequest, {
|
|
68
64
|
rootMargin: '200px 0px 200px 0px',
|
|
69
65
|
});
|
|
70
66
|
// отправляем статистку, когда компонент попадает в зону видимости
|
|
71
|
-
utils_1.useViewport(
|
|
72
|
-
return
|
|
73
|
-
react_1.default.createElement(carousel_1.Carousel, Object.assign({ step: 3, draggable: false,
|
|
67
|
+
(0, utils_1.useViewport)(rootRef, onInViewport);
|
|
68
|
+
return (react_1.default.createElement("div", { ref: rootRef, className: cx('root', className) },
|
|
69
|
+
items.length > 0 && (react_1.default.createElement(carousel_1.Carousel, Object.assign({ step: 3, draggable: false,
|
|
74
70
|
// докидываем индекс чтобы позже брать актуальные данные из списка по нему
|
|
75
71
|
items: items.map((item, index) => [item, index]) }, (itemWidth !== null && {
|
|
76
72
|
withControls: true,
|
|
@@ -86,16 +82,21 @@ const ProductCarousel = ({ className, itemSize, onInViewport, onNeedRequest, wit
|
|
|
86
82
|
targetItemRef.current = e.currentTarget;
|
|
87
83
|
setActiveItemIndex(index);
|
|
88
84
|
}
|
|
89
|
-
} },
|
|
90
|
-
react_1.
|
|
85
|
+
} }, (0, react_1.cloneElement)(item, {
|
|
86
|
+
children: react_1.Children.toArray(item.props.children).reduce((list, child) => {
|
|
87
|
+
if ((0, react_1.isValidElement)(child)) {
|
|
88
|
+
child.type === product_info_1.Parts.Image &&
|
|
89
|
+
list.push(withHoverCard ? (0, react_1.cloneElement)(child, { children: undefined }) : child);
|
|
90
|
+
child.type !== product_info_1.Parts.Footer && list.push(child);
|
|
91
|
+
}
|
|
92
|
+
return list;
|
|
93
|
+
}, []),
|
|
94
|
+
}))),
|
|
91
95
|
// длительность прокрутки в Carousel - 320, делаем слегка с запасом
|
|
92
96
|
// @todo после восстановления проверить позицию курсора чтобы показать карточку (если будет критично)
|
|
93
|
-
onChangeTargetIndex: () => cardShow.disallowFor(360) })),
|
|
97
|
+
onChangeTargetIndex: () => cardShow.disallowFor(360) }))),
|
|
94
98
|
withHoverCard &&
|
|
95
99
|
activeItemIndex !== null &&
|
|
96
|
-
[items[activeItemIndex]].map(item => (react_1.default.createElement(hover_card_1.HoverCard, { key: activeItemIndex, targetRef: targetItemRef, onMouseLeave: () => setActiveItemIndex(null),
|
|
97
|
-
// данные элемента карусели
|
|
98
|
-
data: item.data, onLinkClick: item.onLinkClick, onQuickViewClick: item.onQuickViewClick, renderCartControl: item.renderCartControl }))))) : (react_1.default.createElement("div", { ref: stubRef }));
|
|
100
|
+
[items[activeItemIndex]].map(item => (react_1.default.createElement(hover_card_1.HoverCard, Object.assign({ key: activeItemIndex, targetRef: targetItemRef, onMouseLeave: () => setActiveItemIndex(null) }, item.props))))));
|
|
99
101
|
};
|
|
100
102
|
exports.ProductCarousel = ProductCarousel;
|
|
101
|
-
exports.ProductCarousel.Item = Item;
|
|
@@ -13,7 +13,7 @@ const on_1 = __importDefault(require("@sima-land/ui-nucleons/helpers/on"));
|
|
|
13
13
|
* @param options Опции IntersectionObserver.
|
|
14
14
|
*/
|
|
15
15
|
const useViewport = (ref, callback, options) => {
|
|
16
|
-
react_1.useEffect(() => {
|
|
16
|
+
(0, react_1.useEffect)(() => {
|
|
17
17
|
const element = ref.current;
|
|
18
18
|
if (element) {
|
|
19
19
|
const observer = new IntersectionObserver(entries => {
|
|
@@ -33,15 +33,15 @@ exports.useViewport = useViewport;
|
|
|
33
33
|
* @return Объект работы с флагом.
|
|
34
34
|
*/
|
|
35
35
|
const useAllowFlag = () => {
|
|
36
|
-
const ref = react_1.useRef(true);
|
|
37
|
-
const timerIdRef = react_1.useRef();
|
|
38
|
-
const allowed = react_1.useCallback(() => ref.current, []);
|
|
39
|
-
const disallowFor = react_1.useCallback(timeout => {
|
|
36
|
+
const ref = (0, react_1.useRef)(true);
|
|
37
|
+
const timerIdRef = (0, react_1.useRef)();
|
|
38
|
+
const allowed = (0, react_1.useCallback)(() => ref.current, []);
|
|
39
|
+
const disallowFor = (0, react_1.useCallback)(timeout => {
|
|
40
40
|
window.clearTimeout(timerIdRef.current);
|
|
41
41
|
ref.current = false;
|
|
42
42
|
timerIdRef.current = window.setTimeout(() => (ref.current = true), timeout);
|
|
43
43
|
}, []);
|
|
44
|
-
const controlRef = react_1.useRef({ allowed, disallowFor });
|
|
44
|
+
const controlRef = (0, react_1.useRef)({ allowed, disallowFor });
|
|
45
45
|
return controlRef.current;
|
|
46
46
|
};
|
|
47
47
|
exports.useAllowFlag = useAllowFlag;
|
|
@@ -53,16 +53,16 @@ exports.useAllowFlag = useAllowFlag;
|
|
|
53
53
|
* @return Ширина.
|
|
54
54
|
*/
|
|
55
55
|
const useChildWidth = (ref, selector, deps) => {
|
|
56
|
-
const [itemWidth, setItemWidth] = react_1.useState(null);
|
|
57
|
-
const update = react_1.useCallback(() => {
|
|
56
|
+
const [itemWidth, setItemWidth] = (0, react_1.useState)(null);
|
|
57
|
+
const update = (0, react_1.useCallback)(() => {
|
|
58
58
|
if (ref.current) {
|
|
59
59
|
const child = ref.current.querySelector(selector);
|
|
60
60
|
child && setItemWidth(child.clientWidth);
|
|
61
61
|
}
|
|
62
62
|
}, [ref]);
|
|
63
|
-
react_1.useEffect(() => {
|
|
63
|
+
(0, react_1.useEffect)(() => {
|
|
64
64
|
ref.current && update();
|
|
65
|
-
return on_1.default(window, 'resize', update);
|
|
65
|
+
return (0, on_1.default)(window, 'resize', update);
|
|
66
66
|
}, deps);
|
|
67
67
|
return itemWidth;
|
|
68
68
|
};
|
|
@@ -21,7 +21,7 @@ const BannerTitle = ({ promotionType, volumeDiscount, dueDate }) => {
|
|
|
21
21
|
break;
|
|
22
22
|
case 'volume-discount':
|
|
23
23
|
if (volumeDiscount) {
|
|
24
|
-
content = (react_1.default.createElement("div", { className: banner_title_module_scss_1.default.discount },
|
|
24
|
+
content = (react_1.default.createElement("div", { className: banner_title_module_scss_1.default.discount, "data-testid": 'promotion-card:banner-discount' },
|
|
25
25
|
"\u0441\u043A\u0438\u0434\u043A\u0438 \u0434\u043E",
|
|
26
26
|
react_1.default.createElement("div", { className: banner_title_module_scss_1.default.percent },
|
|
27
27
|
volumeDiscount,
|
|
@@ -32,14 +32,14 @@ const BannerTitle = ({ promotionType, volumeDiscount, dueDate }) => {
|
|
|
32
32
|
return react_1.default.createElement(react_1.default.Fragment, null, content);
|
|
33
33
|
};
|
|
34
34
|
exports.BannerTitle = BannerTitle;
|
|
35
|
-
const CustomTimer = ({ dueDate }) => (react_1.default.createElement(timer_1.Timer, { date: date_fns_1.formatISO(dueDate), timeout: 1000 * 60, format: ({ days, hours, minutes }) => (react_1.default.createElement("div", { className: banner_title_module_scss_1.default.timer },
|
|
35
|
+
const CustomTimer = ({ dueDate }) => (react_1.default.createElement(timer_1.Timer, { date: (0, date_fns_1.formatISO)(dueDate), timeout: 1000 * 60, format: ({ days, hours, minutes }) => (react_1.default.createElement("div", { className: banner_title_module_scss_1.default.timer, "data-testid": 'promotion-card:banner-timer' },
|
|
36
36
|
react_1.default.createElement(TimerPart, { label: '\u0434\u043D\u0435\u0439', value: Math.min(99, days) }),
|
|
37
|
-
react_1.default.createElement(
|
|
37
|
+
react_1.default.createElement(TimerDivider, null),
|
|
38
38
|
react_1.default.createElement(TimerPart, { label: '\u0447\u0430\u0441\u043E\u0432', value: hours % 24 }),
|
|
39
|
-
react_1.default.createElement(
|
|
39
|
+
react_1.default.createElement(TimerDivider, null),
|
|
40
40
|
react_1.default.createElement(TimerPart, { label: '\u043C\u0438\u043D\u0443\u0442', value: minutes % 60 }))) }));
|
|
41
41
|
exports.CustomTimer = CustomTimer;
|
|
42
42
|
const TimerPart = ({ label, value }) => (react_1.default.createElement("div", { className: banner_title_module_scss_1.default.part },
|
|
43
43
|
react_1.default.createElement("div", null, `${value}`.padStart(2, '0')),
|
|
44
44
|
react_1.default.createElement("div", { className: banner_title_module_scss_1.default.label }, label)));
|
|
45
|
-
const
|
|
45
|
+
const TimerDivider = () => react_1.default.createElement("div", { className: banner_title_module_scss_1.default.divider }, ":");
|
|
@@ -24,12 +24,14 @@ const toTimePart = (n) => `${n}`.padStart(2, '0');
|
|
|
24
24
|
const Estimate = ({ dueDate }) => {
|
|
25
25
|
const now = new Date();
|
|
26
26
|
let content = null;
|
|
27
|
-
if (date_fns_1.getYear(dueDate) !== date_fns_1.getYear(now)) {
|
|
28
|
-
const text = [date_fns_1.getDate, date_fns_1.getMonth, date_fns_1.getYear]
|
|
27
|
+
if ((0, date_fns_1.getYear)(dueDate) !== (0, date_fns_1.getYear)(now)) {
|
|
28
|
+
const text = [date_fns_1.getDate, (d) => (0, date_fns_1.getMonth)(d) + 1, date_fns_1.getYear]
|
|
29
|
+
.map(fn => toTimePart(fn(dueDate)))
|
|
30
|
+
.join('.');
|
|
29
31
|
content = `До ${text}`;
|
|
30
32
|
}
|
|
31
33
|
else {
|
|
32
|
-
content = `До ${date_fns_1.getDate(dueDate)} ${MONTH_SHORT_NAME[date_fns_1.getMonth(dueDate)]}`;
|
|
34
|
+
content = `До ${(0, date_fns_1.getDate)(dueDate)} ${MONTH_SHORT_NAME[(0, date_fns_1.getMonth)(dueDate)]}`;
|
|
33
35
|
}
|
|
34
36
|
return react_1.default.createElement(react_1.default.Fragment, null, content);
|
|
35
37
|
};
|
|
@@ -17,6 +17,8 @@ export interface PromotionCardProps extends React.HTMLAttributes<HTMLDivElement>
|
|
|
17
17
|
promotionType?: PromotionType;
|
|
18
18
|
/** Скидка за объем, указывается при соответствующем типе акции. */
|
|
19
19
|
volumeDiscount?: number;
|
|
20
|
+
/** Идентификатор для систем автоматизированного тестирования. */
|
|
21
|
+
'data-testid'?: string;
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
22
24
|
* Карточка акции.
|
|
@@ -24,6 +26,6 @@ export interface PromotionCardProps extends React.HTMLAttributes<HTMLDivElement>
|
|
|
24
26
|
* @return Элемент.
|
|
25
27
|
*/
|
|
26
28
|
export declare const PromotionCard: {
|
|
27
|
-
({ promotionType, href, imageSrc, title, subtitle, dueDate, volumeDiscount, postfix, className, ...restProps }: PromotionCardProps): JSX.Element;
|
|
29
|
+
({ promotionType, href, imageSrc, title, subtitle, dueDate, volumeDiscount, postfix, className, "data-testid": testId, ...restProps }: PromotionCardProps): JSX.Element;
|
|
28
30
|
Placeholder: (props: React.HTMLAttributes<HTMLDivElement>) => JSX.Element;
|
|
29
31
|
};
|
|
@@ -55,13 +55,13 @@ const PromotionName = {
|
|
|
55
55
|
const PromotionCard = (_a) => {
|
|
56
56
|
var { promotionType, href, imageSrc, title, subtitle, dueDate, volumeDiscount, postfix = promotionType ? PromotionName[promotionType] : undefined,
|
|
57
57
|
// div props
|
|
58
|
-
className } = _a, restProps = __rest(_a, ["promotionType", "href", "imageSrc", "title", "subtitle", "dueDate", "volumeDiscount", "postfix", "className"]);
|
|
58
|
+
className, 'data-testid': testId = 'promotion-card' } = _a, restProps = __rest(_a, ["promotionType", "href", "imageSrc", "title", "subtitle", "dueDate", "volumeDiscount", "postfix", "className", 'data-testid']);
|
|
59
59
|
// время нужно выводить только на клиенте - вводим состояние
|
|
60
|
-
const [mounted, setMounted] = react_1.useState(false);
|
|
61
|
-
react_1.useEffect(() => {
|
|
60
|
+
const [mounted, setMounted] = (0, react_1.useState)(false);
|
|
61
|
+
(0, react_1.useEffect)(() => {
|
|
62
62
|
setMounted(true);
|
|
63
63
|
}, []);
|
|
64
|
-
return (react_1.default.createElement("div", Object.assign({ className: classnames_1.default(className, index_module_scss_1.default.root) }, restProps),
|
|
64
|
+
return (react_1.default.createElement("div", Object.assign({ className: (0, classnames_1.default)(className, index_module_scss_1.default.root) }, restProps, { "data-testid": testId }),
|
|
65
65
|
react_1.default.createElement("a", { href: href, className: index_module_scss_1.default.link },
|
|
66
66
|
react_1.default.createElement("div", { className: index_module_scss_1.default.banner },
|
|
67
67
|
react_1.default.createElement("img", { src: imageSrc, alt: title, className: index_module_scss_1.default.image }),
|
|
@@ -72,10 +72,10 @@ const PromotionCard = (_a) => {
|
|
|
72
72
|
react_1.default.createElement("h4", { className: index_module_scss_1.default.title }, title),
|
|
73
73
|
subtitle && react_1.default.createElement("p", { className: index_module_scss_1.default.subtitle }, subtitle)),
|
|
74
74
|
react_1.default.createElement("div", { className: index_module_scss_1.default.footer },
|
|
75
|
-
mounted && (react_1.default.createElement("time", { className: index_module_scss_1.default.timer },
|
|
75
|
+
mounted && (react_1.default.createElement("time", { className: index_module_scss_1.default.timer, "data-testid": 'promotion-card:due-date' },
|
|
76
76
|
react_1.default.createElement(watch_1.default, { className: index_module_scss_1.default['timer-svg'] }),
|
|
77
77
|
react_1.default.createElement(estimate_1.Estimate, { dueDate: dueDate }))),
|
|
78
|
-
postfix && react_1.default.createElement("div", { className: index_module_scss_1.default.postfix }, postfix))))));
|
|
78
|
+
postfix && (react_1.default.createElement("div", { "data-testid": 'promotion-card:postfix', className: index_module_scss_1.default.postfix }, postfix)))))));
|
|
79
79
|
};
|
|
80
80
|
exports.PromotionCard = PromotionCard;
|
|
81
81
|
exports.PromotionCard.Placeholder = placeholder_1.PromotionCardPlaceholder;
|
|
@@ -12,8 +12,8 @@ const classnames_1 = __importDefault(require("classnames"));
|
|
|
12
12
|
* @param props Свойства div.
|
|
13
13
|
* @return Элемент.
|
|
14
14
|
*/
|
|
15
|
-
const PromotionCardPlaceholder = (props) => (react_1.default.createElement("div", Object.assign({}, props),
|
|
16
|
-
react_1.default.createElement("div", { className: classnames_1.default(placeholder_module_scss_1.default.part, placeholder_module_scss_1.default.banner) }),
|
|
17
|
-
react_1.default.createElement("div", { className: classnames_1.default(placeholder_module_scss_1.default.part, placeholder_module_scss_1.default.text) }),
|
|
18
|
-
react_1.default.createElement("div", { className: classnames_1.default(placeholder_module_scss_1.default.part, placeholder_module_scss_1.default.text) })));
|
|
15
|
+
const PromotionCardPlaceholder = (props) => (react_1.default.createElement("div", Object.assign({ "data-testid": 'promotion-card:placeholder' }, props),
|
|
16
|
+
react_1.default.createElement("div", { className: (0, classnames_1.default)(placeholder_module_scss_1.default.part, placeholder_module_scss_1.default.banner) }),
|
|
17
|
+
react_1.default.createElement("div", { className: (0, classnames_1.default)(placeholder_module_scss_1.default.part, placeholder_module_scss_1.default.text) }),
|
|
18
|
+
react_1.default.createElement("div", { className: (0, classnames_1.default)(placeholder_module_scss_1.default.part, placeholder_module_scss_1.default.text) })));
|
|
19
19
|
exports.PromotionCardPlaceholder = PromotionCardPlaceholder;
|
|
@@ -1,4 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
@use 'node_modules/@sima-land/ui-nucleons/colors';
|
|
2
|
+
|
|
3
|
+
.root {
|
|
4
|
+
padding: 24px;
|
|
5
|
+
display: flex;
|
|
6
|
+
justify-content: space-between;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.main {
|
|
10
|
+
margin-right: 20px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.text {
|
|
14
|
+
font-size: 14px;
|
|
15
|
+
line-height: 20px;
|
|
16
|
+
margin-bottom: 12px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.icon {
|
|
20
|
+
flex-shrink: 0;
|
|
21
|
+
width: 48px;
|
|
22
|
+
height: 48px;
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
background: rgba(colors.$additional-amber, 0.5);
|
|
27
|
+
border-radius: 50%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.svg {
|
|
31
|
+
display: block;
|
|
32
|
+
fill: #fff;
|
|
4
33
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export interface GarlandCalculatorButtonProps {
|
|
3
|
-
/** Обработчик
|
|
4
|
-
onClick?: React.MouseEventHandler
|
|
3
|
+
/** Обработчик клика. */
|
|
4
|
+
onClick?: React.MouseEventHandler<HTMLAnchorElement>;
|
|
5
5
|
/** CSS-класс. */
|
|
6
6
|
className?: string;
|
|
7
7
|
}
|
|
@@ -11,4 +11,3 @@ export interface GarlandCalculatorButtonProps {
|
|
|
11
11
|
* @return Элемент.
|
|
12
12
|
*/
|
|
13
13
|
export declare const GarlandCalculatorButton: ({ className, onClick }: GarlandCalculatorButtonProps) => JSX.Element;
|
|
14
|
-
export default GarlandCalculatorButton;
|