@sima-land/moleculas 15.1.0 → 15.3.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/product-info/index.js +3 -1
- package/common/components/product-info/parts.d.ts +16 -2
- package/common/components/product-info/parts.js +15 -4
- package/common/components/product-info/product-info.module.scss +38 -3
- package/desktop/components/gallery-modal/components/review-info.js +5 -0
- package/mobile/components/product-slider/index.js +7 -6
- package/mobile/components/product-slider/product-slider.module.scss +7 -0
- package/package.json +1 -1
|
@@ -15,19 +15,21 @@ exports.UNAVAILABLE_REASON = {
|
|
|
15
15
|
onlyForLegalEntities: 'Товар доступен только для юридических лиц',
|
|
16
16
|
};
|
|
17
17
|
const ProductInfo = ({ restriction, children }) => {
|
|
18
|
-
const { image, badges, prices, title, trademark, footer } = (0, define_slots_1.defineSlots)(children, {
|
|
18
|
+
const { image, badges, prices, title, trademark, footer, secondaryInfo } = (0, define_slots_1.defineSlots)(children, {
|
|
19
19
|
image: parts_1.Parts.Image,
|
|
20
20
|
badges: parts_1.Parts.Badges,
|
|
21
21
|
prices: parts_1.Parts.Prices,
|
|
22
22
|
title: parts_1.Parts.Title,
|
|
23
23
|
trademark: parts_1.Parts.TrademarkLink,
|
|
24
24
|
footer: parts_1.Parts.Footer,
|
|
25
|
+
secondaryInfo: parts_1.Parts.SecondaryInfo,
|
|
25
26
|
});
|
|
26
27
|
return (react_1.default.createElement(utils_1.ProductInfoContext.Provider, { value: { restriction } },
|
|
27
28
|
image,
|
|
28
29
|
restriction !== 'adult' && badges,
|
|
29
30
|
prices,
|
|
30
31
|
title,
|
|
32
|
+
restriction !== 'adult' && secondaryInfo,
|
|
31
33
|
!restriction && trademark,
|
|
32
34
|
footer));
|
|
33
35
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
2
|
import { LinkProps } from '@sima-land/ui-nucleons/link';
|
|
3
|
+
import { HintProps } from '@sima-land/ui-nucleons/hint';
|
|
3
4
|
import { StrokedSVGProps } from '@sima-land/ui-nucleons/stroked-svg';
|
|
4
5
|
import { BadgeListProps } from '../badge-list';
|
|
5
6
|
export interface ImageProps {
|
|
@@ -10,14 +11,22 @@ export interface ImageProps {
|
|
|
10
11
|
children?: React.ReactNode;
|
|
11
12
|
opacity?: number;
|
|
12
13
|
}
|
|
14
|
+
declare function SecondaryInfo({ children }: {
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}): JSX.Element;
|
|
13
17
|
export declare const Parts: {
|
|
14
18
|
readonly Image: ({ src, alt, href, onClick, children, opacity }: ImageProps) => JSX.Element;
|
|
15
|
-
readonly ImageButton: ({ icon, fill, onClick, hint, "data-testid": testId, }: {
|
|
19
|
+
readonly ImageButton: ({ icon, fill, onClick, hint, hintDirection, "data-testid": testId, position, }: {
|
|
16
20
|
icon: StrokedSVGProps['component'];
|
|
17
21
|
fill?: string | undefined;
|
|
18
22
|
onClick?: React.MouseEventHandler<HTMLDivElement> | undefined;
|
|
19
23
|
hint?: string | undefined;
|
|
24
|
+
hintDirection?: ("left" | "right" | "bottom" | "top") | undefined;
|
|
20
25
|
'data-testid'?: string | undefined;
|
|
26
|
+
position?: {
|
|
27
|
+
x: 'left' | 'right';
|
|
28
|
+
y: 'top' | 'bottom';
|
|
29
|
+
} | undefined;
|
|
21
30
|
}) => JSX.Element;
|
|
22
31
|
readonly Badges: (props: BadgeListProps) => JSX.Element;
|
|
23
32
|
readonly Prices: ({ price, oldPrice, currencyGrapheme, unavailableReason, }: {
|
|
@@ -28,7 +37,11 @@ export declare const Parts: {
|
|
|
28
37
|
}) => JSX.Element;
|
|
29
38
|
readonly Title: ({ children, href, onClick }: Pick<LinkProps, 'children' | 'href' | 'onClick'>) => JSX.Element;
|
|
30
39
|
readonly TrademarkLink: ({ href, children, onClick, }: Pick<LinkProps, 'href' | 'children' | 'onClick'>) => JSX.Element;
|
|
40
|
+
readonly SecondaryInfo: typeof SecondaryInfo;
|
|
31
41
|
readonly Footer: React.FC<{}>;
|
|
42
|
+
readonly FooterContext: React.Context<{
|
|
43
|
+
className?: string | undefined;
|
|
44
|
+
}>;
|
|
32
45
|
readonly CartControl: ({ children, stepText, markupText, loading, }: {
|
|
33
46
|
children?: React.ReactNode;
|
|
34
47
|
stepText?: string | undefined;
|
|
@@ -39,3 +52,4 @@ export declare const Parts: {
|
|
|
39
52
|
readonly WaitListAddButton: ({ className, ...props }: LinkProps) => JSX.Element;
|
|
40
53
|
readonly WaitListAddedLink: ({ className, ...props }: LinkProps) => JSX.Element;
|
|
41
54
|
};
|
|
55
|
+
export {};
|
|
@@ -51,17 +51,17 @@ const cx = bind_1.default.bind(product_info_module_scss_1.default);
|
|
|
51
51
|
* @param props Свойства.
|
|
52
52
|
* @return Элемент.
|
|
53
53
|
*/
|
|
54
|
-
const ImageButton = ({ icon, fill, onClick, hint, 'data-testid': testId, }) => {
|
|
54
|
+
const ImageButton = ({ icon, fill, onClick, hint, hintDirection = 'left', 'data-testid': testId, position, }) => {
|
|
55
55
|
const commonProps = {
|
|
56
56
|
role: 'banner',
|
|
57
57
|
component: icon,
|
|
58
58
|
fill,
|
|
59
59
|
strokeWidth: 1.5,
|
|
60
|
-
className: cx('image-button'),
|
|
60
|
+
className: cx('image-button', position && 'positioned', position && `${position.x}-${position.y}`),
|
|
61
61
|
onClick,
|
|
62
62
|
'data-testid': testId,
|
|
63
63
|
};
|
|
64
|
-
return hint ? (react_1.default.createElement(with_hint_1.WithHint, { hint: hint, direction:
|
|
64
|
+
return hint ? (react_1.default.createElement(with_hint_1.WithHint, { hint: hint, direction: hintDirection }, (ref, toggle) => (react_1.default.createElement(stroked_svg_1.StrokedSVG, Object.assign({}, commonProps, { ref: ref, onMouseEnter: () => toggle(true), onMouseLeave: () => toggle(false) }))))) : (react_1.default.createElement(stroked_svg_1.StrokedSVG, Object.assign({}, commonProps)));
|
|
65
65
|
};
|
|
66
66
|
/**
|
|
67
67
|
* Слот - изображение товара.
|
|
@@ -111,12 +111,18 @@ const Title = ({ children, href, onClick }) => {
|
|
|
111
111
|
* @return Элемент.
|
|
112
112
|
*/
|
|
113
113
|
const TrademarkLink = ({ href, children, onClick, }) => (react_1.default.createElement(link_1.Link, { className: cx('trademark-link'), href: href, children: children, onClick: onClick, color: 'basic-blue', "data-testid": 'product-info:trademark-link' }));
|
|
114
|
+
const FooterContext = (0, react_1.createContext)({
|
|
115
|
+
className: undefined,
|
|
116
|
+
});
|
|
114
117
|
/**
|
|
115
118
|
* Слот - футер.
|
|
116
119
|
* @param props Свойства.
|
|
117
120
|
* @return Элемент.
|
|
118
121
|
*/
|
|
119
|
-
const Footer = ({ children }) =>
|
|
122
|
+
const Footer = ({ children }) => {
|
|
123
|
+
const { className = cx('footer') } = (0, react_1.useContext)(FooterContext);
|
|
124
|
+
return (react_1.default.createElement("div", { className: className, "data-testid": 'product-info:footer' }, children));
|
|
125
|
+
};
|
|
120
126
|
/**
|
|
121
127
|
* Блок управления корзиной.
|
|
122
128
|
* @param props Свойства.
|
|
@@ -159,6 +165,9 @@ const WaitListAddedLink = (_a) => {
|
|
|
159
165
|
'\u00A0',
|
|
160
166
|
"\u043E\u0436\u0438\u0434\u0430\u043D\u0438\u044F")));
|
|
161
167
|
};
|
|
168
|
+
function SecondaryInfo({ children }) {
|
|
169
|
+
return react_1.default.createElement("div", { className: cx('secondary-info') }, children);
|
|
170
|
+
}
|
|
162
171
|
exports.Parts = {
|
|
163
172
|
// основные компоненты-слоты и компоненты-запчасти
|
|
164
173
|
Image,
|
|
@@ -167,8 +176,10 @@ exports.Parts = {
|
|
|
167
176
|
Prices,
|
|
168
177
|
Title,
|
|
169
178
|
TrademarkLink,
|
|
179
|
+
SecondaryInfo,
|
|
170
180
|
// компоненты-слоты и компоненты-запчасти, предназначенные для вывода футера (dcе что ниже ссылки на торговую марку)
|
|
171
181
|
Footer,
|
|
182
|
+
FooterContext,
|
|
172
183
|
CartControl,
|
|
173
184
|
AdultConfirmButton,
|
|
174
185
|
WaitListAddButton,
|
|
@@ -63,6 +63,8 @@
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
.image-button {
|
|
66
|
+
width: 24px;
|
|
67
|
+
height: 24px;
|
|
66
68
|
display: block;
|
|
67
69
|
border: 0;
|
|
68
70
|
outline: 0;
|
|
@@ -71,13 +73,39 @@
|
|
|
71
73
|
--gutter: 8px;
|
|
72
74
|
right: var(--gutter);
|
|
73
75
|
cursor: pointer;
|
|
74
|
-
&:
|
|
76
|
+
&:not(.positioned) {
|
|
77
|
+
&:nth-child(1) {
|
|
78
|
+
top: var(--gutter);
|
|
79
|
+
}
|
|
80
|
+
&:nth-child(2) {
|
|
81
|
+
bottom: var(--gutter);
|
|
82
|
+
}
|
|
83
|
+
&:nth-child(3) {
|
|
84
|
+
top: var(--gutter);
|
|
85
|
+
left: var(--gutter);
|
|
86
|
+
}
|
|
87
|
+
&:nth-child(3) {
|
|
88
|
+
top: var(--gutter);
|
|
89
|
+
left: var(--gutter);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
&.left-top {
|
|
93
|
+
left: var(--gutter);
|
|
75
94
|
top: var(--gutter);
|
|
76
95
|
}
|
|
77
|
-
|
|
96
|
+
&.left-bottom {
|
|
97
|
+
left: var(--gutter);
|
|
78
98
|
bottom: var(--gutter);
|
|
79
99
|
}
|
|
80
|
-
|
|
100
|
+
&.right-top {
|
|
101
|
+
right: var(--gutter);
|
|
102
|
+
top: var(--gutter);
|
|
103
|
+
}
|
|
104
|
+
&.right-bottom {
|
|
105
|
+
right: var(--gutter);
|
|
106
|
+
bottom: var(--gutter);
|
|
107
|
+
}
|
|
108
|
+
&:nth-child(n + 5) {
|
|
81
109
|
display: none;
|
|
82
110
|
}
|
|
83
111
|
&:hover {
|
|
@@ -143,6 +171,13 @@
|
|
|
143
171
|
color: colors.$basic-gray87;
|
|
144
172
|
}
|
|
145
173
|
|
|
174
|
+
.secondary-info {
|
|
175
|
+
margin-top: 8px;
|
|
176
|
+
font-size: 14px;
|
|
177
|
+
line-height: 20px;
|
|
178
|
+
color: colors.$basic-gray38;
|
|
179
|
+
}
|
|
180
|
+
|
|
146
181
|
.trademark-link {
|
|
147
182
|
display: block;
|
|
148
183
|
margin-top: 8px;
|
|
@@ -42,7 +42,12 @@ const ReviewInfo = ({ rating, author, onGoToReview, content, className, notAffec
|
|
|
42
42
|
setClamped(contentEl.scrollHeight > contentEl.clientHeight);
|
|
43
43
|
}
|
|
44
44
|
}, [opened]);
|
|
45
|
+
(0, react_1.useEffect)(() => {
|
|
46
|
+
setClamped(false);
|
|
47
|
+
setOpened(false);
|
|
48
|
+
}, [content, author]);
|
|
45
49
|
(0, react_1.useEffect)(checkClamp, [content, opened]);
|
|
50
|
+
// @todo что делать если при изменении размеров изменилось состояние обрезания?
|
|
46
51
|
(0, react_1.useEffect)(() => (0, on_1.default)(window, 'resize', checkClamp), [content, checkClamp]);
|
|
47
52
|
return (react_1.default.createElement("div", { className: cx('root', className, opened && shadows_1.BoxShadow.z2straight) },
|
|
48
53
|
loading && (react_1.default.createElement(Layout, null,
|
|
@@ -45,11 +45,12 @@ const ProductSlider = ({ children, onInViewport, onNeedRequest }) => {
|
|
|
45
45
|
entry.isIntersecting && (onInViewport === null || onInViewport === void 0 ? void 0 : onInViewport());
|
|
46
46
|
});
|
|
47
47
|
return (react_1.default.createElement("div", { ref: rootRef, "data-testid": 'product-slider:root' },
|
|
48
|
-
react_1.default.createElement(touch_slider_1.TouchSlider, null,
|
|
49
|
-
(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
react_1.default.createElement(touch_slider_1.TouchSlider, null,
|
|
49
|
+
react_1.default.createElement(product_info_1.Parts.FooterContext.Provider, { value: { className: product_slider_module_scss_1.default.footer } }, react_1.Children.toArray(children).reduce((list, item) => {
|
|
50
|
+
(0, react_1.isValidElement)(item) &&
|
|
51
|
+
item.type === product_info_1.ProductInfo &&
|
|
52
|
+
list.push(react_1.default.createElement("div", { key: item.key, className: product_slider_module_scss_1.default.item, "data-testid": 'product-slider:item' }, item));
|
|
53
|
+
return list;
|
|
54
|
+
}, [])))));
|
|
54
55
|
};
|
|
55
56
|
exports.ProductSlider = ProductSlider;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
@use 'node_modules/@sima-land/ui-nucleons/breakpoints';
|
|
2
2
|
|
|
3
3
|
.item {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
4
6
|
position: relative;
|
|
5
7
|
flex-shrink: 0;
|
|
6
8
|
width: 160px;
|
|
@@ -11,3 +13,8 @@
|
|
|
11
13
|
margin-right: 16px;
|
|
12
14
|
}
|
|
13
15
|
}
|
|
16
|
+
|
|
17
|
+
.footer {
|
|
18
|
+
margin-top: auto;
|
|
19
|
+
padding-top: 16px;
|
|
20
|
+
}
|