@sima-land/moleculas 15.0.1 → 15.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.
- package/common/components/badge/index.d.ts +1 -4
- package/common/components/badge/index.js +24 -9
- package/common/components/badge-list/badge-list.module.scss +1 -0
- package/common/components/badge-list/index.d.ts +9 -2
- package/common/components/badge-list/index.js +14 -6
- package/common/components/product-info/parts.d.ts +3 -0
- package/common/components/product-info/parts.js +8 -1
- package/desktop/components/gallery-modal/components/gallery-modal.d.ts +2 -4
- package/desktop/components/gallery-modal/components/gallery-modal.js +17 -18
- package/desktop/components/gallery-modal/components/gallery-modal.module.scss +38 -28
- package/desktop/components/gallery-modal/components/review-info.d.ts +5 -2
- package/desktop/components/gallery-modal/components/review-info.js +70 -6
- package/desktop/components/gallery-modal/components/review-info.module.scss +69 -4
- package/desktop/components/gallery-modal/utils.d.ts +3 -3
- package/desktop/components/gallery-modal/utils.js +14 -17
- 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
|
@@ -14,9 +14,6 @@ export interface BadgeProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorEl
|
|
|
14
14
|
/**
|
|
15
15
|
* Шильдик.
|
|
16
16
|
* @param props Свойства.
|
|
17
|
-
* @param props.color Цвет.
|
|
18
|
-
* @param props.fields Содержимое.
|
|
19
|
-
* @param props.href Ссылка.
|
|
20
17
|
* @return Элемент.
|
|
21
18
|
*/
|
|
22
|
-
export declare const Badge:
|
|
19
|
+
export declare const Badge: React.ForwardRefExoticComponent<BadgeProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
2
21
|
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
22
|
var t = {};
|
|
4
23
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -15,23 +34,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
15
34
|
};
|
|
16
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
36
|
exports.Badge = void 0;
|
|
18
|
-
const react_1 =
|
|
19
|
-
const bind_1 = __importDefault(require("classnames/bind"));
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
20
38
|
const timer_1 = require("@sima-land/ui-nucleons/timer");
|
|
39
|
+
const bind_1 = __importDefault(require("classnames/bind"));
|
|
21
40
|
const badge_module_scss_1 = __importDefault(require("./badge.module.scss"));
|
|
22
41
|
const cx = bind_1.default.bind(badge_module_scss_1.default);
|
|
23
42
|
/**
|
|
24
43
|
* Шильдик.
|
|
25
44
|
* @param props Свойства.
|
|
26
|
-
* @param props.color Цвет.
|
|
27
|
-
* @param props.fields Содержимое.
|
|
28
|
-
* @param props.href Ссылка.
|
|
29
45
|
* @return Элемент.
|
|
30
46
|
*/
|
|
31
|
-
|
|
47
|
+
exports.Badge = (0, react_1.forwardRef)(function Badge(_a, ref) {
|
|
32
48
|
var { className, color, fields, href, style, 'data-testid': testId = 'badge' } = _a, restProps = __rest(_a, ["className", "color", "fields", "href", "style", 'data-testid']);
|
|
33
49
|
const iconOnly = fields.length === 1 && fields[0].type === 'svg-url';
|
|
34
|
-
return (react_1.default.createElement("a", Object.assign({}, restProps, { "data-testid": testId, href: href, style: Object.assign(Object.assign({}, style), { '--badge-color': color }), className: cx('root', iconOnly && 'icon-only', href && 'interactive', className), children: iconOnly ? (react_1.default.createElement("img", { className: cx('icon'), src: fields[0].value })) : (
|
|
50
|
+
return (react_1.default.createElement("a", Object.assign({ ref: ref }, restProps, { "data-testid": testId, href: href, style: Object.assign(Object.assign({}, style), { '--badge-color': color }), className: cx('root', iconOnly && 'icon-only', href && 'interactive', className), children: iconOnly ? (react_1.default.createElement("img", { className: cx('icon'), src: fields[0].value })) : (
|
|
35
51
|
// вложенный span нужен для того чтобы объединить `display: inline-flex` и `text-overflow: ellipsis`
|
|
36
52
|
react_1.default.createElement("span", { className: cx('content') }, fields.reduce((acc, item, i) => {
|
|
37
53
|
let result = null;
|
|
@@ -48,8 +64,7 @@ const Badge = (_a) => {
|
|
|
48
64
|
i < fields.length - 1 && acc.push(' ');
|
|
49
65
|
return acc;
|
|
50
66
|
}, []))) })));
|
|
51
|
-
};
|
|
52
|
-
exports.Badge = Badge;
|
|
67
|
+
});
|
|
53
68
|
/**
|
|
54
69
|
* Форматирует оставшееся время.
|
|
55
70
|
* @param distance Оставшееся время.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
2
|
export interface BadgeListProps {
|
|
3
3
|
/** Список данных для шильдиков. */
|
|
4
4
|
children?: React.ReactNode;
|
|
@@ -14,4 +14,11 @@ export interface BadgeListProps {
|
|
|
14
14
|
* @param props Свойства компонента.
|
|
15
15
|
* @return Элемент.
|
|
16
16
|
*/
|
|
17
|
-
export declare const BadgeList:
|
|
17
|
+
export declare const BadgeList: {
|
|
18
|
+
({ children, className, lineLimit, style }: BadgeListProps): JSX.Element;
|
|
19
|
+
Slot: typeof BadgeListSlot;
|
|
20
|
+
};
|
|
21
|
+
declare function BadgeListSlot({ children }: {
|
|
22
|
+
children?: ReactNode;
|
|
23
|
+
}): JSX.Element;
|
|
24
|
+
export {};
|
|
@@ -33,10 +33,18 @@ const cx = bind_1.default.bind(badge_list_module_scss_1.default);
|
|
|
33
33
|
* @param props Свойства компонента.
|
|
34
34
|
* @return Элемент.
|
|
35
35
|
*/
|
|
36
|
-
const BadgeList = ({ children, className, lineLimit, style }) => (react_1.default.createElement("div", { className: cx('root', className, lineLimit && 'line-limit'), style: lineLimit ? Object.assign(Object.assign({}, style), { '--line-limit': lineLimit }) : style }, react_1.Children.toArray(children).
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
const BadgeList = ({ children, className, lineLimit, style }) => (react_1.default.createElement("div", { className: cx('root', className, lineLimit && 'line-limit'), style: lineLimit ? Object.assign(Object.assign({}, style), { '--line-limit': lineLimit }) : style }, react_1.Children.toArray(children).map(child => {
|
|
37
|
+
switch (true) {
|
|
38
|
+
case (0, react_1.isValidElement)(child) && child.type === badge_1.Badge:
|
|
39
|
+
return react_1.default.createElement(BadgeListSlot, null, child);
|
|
40
|
+
case (0, react_1.isValidElement)(child) && child.type === BadgeListSlot:
|
|
41
|
+
return child;
|
|
42
|
+
default:
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
})));
|
|
42
46
|
exports.BadgeList = BadgeList;
|
|
47
|
+
function BadgeListSlot({ children }) {
|
|
48
|
+
return react_1.default.createElement("div", { className: cx('item') }, children);
|
|
49
|
+
}
|
|
50
|
+
exports.BadgeList.Slot = BadgeListSlot;
|
|
@@ -29,6 +29,9 @@ export declare const Parts: {
|
|
|
29
29
|
readonly Title: ({ children, href, onClick }: Pick<LinkProps, 'children' | 'href' | 'onClick'>) => JSX.Element;
|
|
30
30
|
readonly TrademarkLink: ({ href, children, onClick, }: Pick<LinkProps, 'href' | 'children' | 'onClick'>) => JSX.Element;
|
|
31
31
|
readonly Footer: React.FC<{}>;
|
|
32
|
+
readonly FooterContext: React.Context<{
|
|
33
|
+
className?: string | undefined;
|
|
34
|
+
}>;
|
|
32
35
|
readonly CartControl: ({ children, stepText, markupText, loading, }: {
|
|
33
36
|
children?: React.ReactNode;
|
|
34
37
|
stepText?: string | undefined;
|
|
@@ -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 Свойства.
|
|
@@ -169,6 +175,7 @@ exports.Parts = {
|
|
|
169
175
|
TrademarkLink,
|
|
170
176
|
// компоненты-слоты и компоненты-запчасти, предназначенные для вывода футера (dcе что ниже ссылки на торговую марку)
|
|
171
177
|
Footer,
|
|
178
|
+
FooterContext,
|
|
172
179
|
CartControl,
|
|
173
180
|
AdultConfirmButton,
|
|
174
181
|
WaitListAddButton,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { MediaData } from '../types';
|
|
3
3
|
import { ModalProps } from '@sima-land/ui-nucleons/modal';
|
|
4
|
+
import { ReviewInfoProps } from './review-info';
|
|
4
5
|
export interface GalleryModalProps extends Pick<ModalProps, 'withScrollDisable' | 'scrollDisableOptions'> {
|
|
5
6
|
/** Список медиа. */
|
|
6
7
|
media: MediaData[];
|
|
@@ -9,10 +10,7 @@ export interface GalleryModalProps extends Pick<ModalProps, 'withScrollDisable'
|
|
|
9
10
|
/** Будет вызвана при переключении элемента из списка медиа. */
|
|
10
11
|
onMediaChange?: (data: MediaData, index: number) => void;
|
|
11
12
|
/** Данные отзыва, выводятся внизу окна. */
|
|
12
|
-
review?:
|
|
13
|
-
rating: number;
|
|
14
|
-
author: string;
|
|
15
|
-
};
|
|
13
|
+
review?: Pick<ReviewInfoProps, 'rating' | 'author' | 'content' | 'loading' | 'notAffectRating'>;
|
|
16
14
|
/** Будет вызвана при попытке перейти к отзыву. */
|
|
17
15
|
onGoToReview?: () => void;
|
|
18
16
|
/** Будет вызвана при закрытии. */
|
|
@@ -50,38 +50,37 @@ const gallery_modal_module_scss_1 = __importDefault(require("./gallery-modal.mod
|
|
|
50
50
|
const cx = bind_1.default.bind(gallery_modal_module_scss_1.default);
|
|
51
51
|
const GalleryModal = ({ media, defaultMediaIndex = 0, review, onClose, onGoToReview, onMediaChange, withScrollDisable, scrollDisableOptions, onVideoEvent, }) => {
|
|
52
52
|
const [currentIndex, setCurrent] = (0, react_1.useState)(defaultMediaIndex);
|
|
53
|
-
const
|
|
54
|
-
const areaRef = (0, react_1.useRef)(null);
|
|
55
|
-
const footerGutter = 40;
|
|
56
|
-
const footerHeight = 20;
|
|
57
|
-
const correction = review ? footerGutter + footerHeight : 0;
|
|
58
|
-
const size = (0, utils_1.useRectFit)(areaRef, { correction });
|
|
59
|
-
const innerStyles = Object.assign({}, (size !== null && {
|
|
60
|
-
'--area-height': `${size + correction}px`,
|
|
61
|
-
'--all-round-view-size': `${size}px`,
|
|
62
|
-
}));
|
|
53
|
+
const rootRef = (0, react_1.useRef)(null);
|
|
63
54
|
const handlerRef = (0, identity_1.useIdentityRef)(onMediaChange);
|
|
55
|
+
const thumbnailsRef = (0, react_1.useRef)(null);
|
|
64
56
|
(0, react_1.useEffect)(() => {
|
|
65
57
|
var _a;
|
|
66
58
|
(_a = handlerRef.current) === null || _a === void 0 ? void 0 : _a.call(handlerRef, media[currentIndex], currentIndex);
|
|
67
59
|
thumbnailsRef.current &&
|
|
68
60
|
(0, scroll_to_child_1.scrollToChild)(thumbnailsRef.current, thumbnailsRef.current.children[currentIndex]);
|
|
69
61
|
}, [currentIndex]);
|
|
62
|
+
const size = (0, utils_1.useSquareFit)(rootRef, { hasReview: Boolean(review) });
|
|
63
|
+
const style = size
|
|
64
|
+
? {
|
|
65
|
+
'--square-size': `${size}px`,
|
|
66
|
+
'--all-round-view-size': `${size}px`,
|
|
67
|
+
}
|
|
68
|
+
: {};
|
|
70
69
|
return (react_1.default.createElement(modal_1.Modal, Object.assign({ size: 'fullscreen', onClose: onClose }, { withScrollDisable, scrollDisableOptions }),
|
|
71
70
|
react_1.default.createElement(modal_1.Modal.Header, { onClose: onClose }),
|
|
72
71
|
react_1.default.createElement(modal_1.Modal.Body, null,
|
|
73
|
-
react_1.default.createElement(layout_1.DesktopLayout, { className: cx('root'), style:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
react_1.default.createElement(layout_1.DesktopLayout, { className: cx('root'), ref: rootRef, style: style },
|
|
73
|
+
react_1.default.createElement("div", { className: cx('main') },
|
|
74
|
+
react_1.default.createElement("div", { className: cx('thumbnails'), ref: thumbnailsRef }, media.map((item, index) => (react_1.default.createElement(thumbnail_1.Thumbnail, Object.assign({ key: index, className: cx('thumbnail'), type: item.type, checked: index === currentIndex, onClick: () => setCurrent(index) }, (item.type === 'image' && {
|
|
75
|
+
src: item.data.thumbnailSrc,
|
|
76
|
+
alt: item.data.alt || '',
|
|
77
|
+
})))))),
|
|
79
78
|
react_1.default.createElement("div", { className: cx('square') },
|
|
80
79
|
react_1.default.createElement(Media, Object.assign({}, media[currentIndex], { onVideoEvent: onVideoEvent })),
|
|
81
80
|
media.length > 1 && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
82
81
|
react_1.default.createElement(arrow_button_1.ArrowButton, { direction: 'left', className: cx('button', 'left'), onClick: () => setCurrent((media.length + currentIndex - 1) % media.length), "data-testid": 'gallery-modal:prev-media-button' }),
|
|
83
|
-
react_1.default.createElement(arrow_button_1.ArrowButton, { direction: 'right', className: cx('button', 'right'), onClick: () => setCurrent((currentIndex + 1) % media.length), "data-testid": 'gallery-modal:next-media-button' })))),
|
|
84
|
-
|
|
82
|
+
react_1.default.createElement(arrow_button_1.ArrowButton, { direction: 'right', className: cx('button', 'right'), onClick: () => setCurrent((currentIndex + 1) % media.length), "data-testid": 'gallery-modal:next-media-button' })))))),
|
|
83
|
+
size !== null && (react_1.default.createElement("div", { className: cx('footer'), style: style }, review && react_1.default.createElement(review_info_1.ReviewInfo, Object.assign({}, review, { onGoToReview: onGoToReview })))))));
|
|
85
84
|
};
|
|
86
85
|
exports.GalleryModal = GalleryModal;
|
|
87
86
|
const Media = (_a) => {
|
|
@@ -1,47 +1,52 @@
|
|
|
1
1
|
@use 'node_modules/@sima-land/ui-nucleons/colors';
|
|
2
2
|
|
|
3
3
|
.root {
|
|
4
|
-
height: calc(100vh - var(--header-height) - 40px);
|
|
5
4
|
position: relative;
|
|
5
|
+
height: calc(100vh - var(--header-height));
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
// box-shadow: inset 0 0 0 6px #f00;
|
|
6
9
|
}
|
|
7
10
|
|
|
8
|
-
.
|
|
11
|
+
.main {
|
|
12
|
+
position: relative;
|
|
13
|
+
// box-shadow: inset 0 0 0 6px #0f0;
|
|
14
|
+
width: 100%;
|
|
9
15
|
height: var(--area-height);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.thumbnails {
|
|
19
|
+
width: 104px;
|
|
20
|
+
height: 100%;
|
|
10
21
|
overflow-x: hidden;
|
|
11
22
|
overflow-y: auto;
|
|
12
23
|
position: absolute;
|
|
13
|
-
width: 104px;
|
|
14
24
|
.thumbnail + .thumbnail {
|
|
15
25
|
margin-top: 8px;
|
|
16
26
|
}
|
|
17
27
|
}
|
|
18
28
|
|
|
19
|
-
.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
.square {
|
|
30
|
+
width: var(--square-size);
|
|
31
|
+
height: var(--square-size);
|
|
32
|
+
position: relative;
|
|
33
|
+
left: 50%;
|
|
34
|
+
transform: translateX(-50%);
|
|
35
|
+
&::after {
|
|
36
|
+
content: '';
|
|
37
|
+
display: block;
|
|
38
|
+
padding-top: 100%;
|
|
39
|
+
width: 100%;
|
|
26
40
|
}
|
|
27
|
-
.
|
|
28
|
-
position:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
width: 100%;
|
|
41
|
+
.button {
|
|
42
|
+
position: absolute;
|
|
43
|
+
top: 50%;
|
|
44
|
+
transform: translateY(-50%);
|
|
45
|
+
&.left {
|
|
46
|
+
right: calc(100% + 24px);
|
|
34
47
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
top: 50%;
|
|
38
|
-
transform: translateY(-50%);
|
|
39
|
-
&.left {
|
|
40
|
-
right: calc(100% + 24px);
|
|
41
|
-
}
|
|
42
|
-
&.right {
|
|
43
|
-
left: calc(100% + 24px);
|
|
44
|
-
}
|
|
48
|
+
&.right {
|
|
49
|
+
left: calc(100% + 24px);
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
52
|
}
|
|
@@ -71,5 +76,10 @@
|
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
.footer {
|
|
74
|
-
|
|
79
|
+
position: absolute;
|
|
80
|
+
bottom: 0;
|
|
81
|
+
left: 0;
|
|
82
|
+
right: 0;
|
|
83
|
+
margin-top: auto;
|
|
84
|
+
// background-color: #ff000024;
|
|
75
85
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
2
|
export interface ReviewInfoProps {
|
|
3
3
|
rating: number;
|
|
4
4
|
author: string;
|
|
5
|
+
content?: ReactNode;
|
|
5
6
|
onGoToReview?: () => void;
|
|
7
|
+
notAffectRating?: boolean;
|
|
6
8
|
className?: string;
|
|
9
|
+
loading?: boolean;
|
|
7
10
|
}
|
|
8
|
-
export declare const ReviewInfo: ({ rating, author, onGoToReview, className }: ReviewInfoProps) => JSX.Element;
|
|
11
|
+
export declare const ReviewInfo: ({ rating, author, onGoToReview, content, className, notAffectRating, loading, }: ReviewInfoProps) => JSX.Element;
|
|
@@ -1,16 +1,80 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
2
21
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
22
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
23
|
};
|
|
5
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
25
|
exports.ReviewInfo = void 0;
|
|
7
|
-
const react_1 =
|
|
26
|
+
const react_1 = __importStar(require("react"));
|
|
8
27
|
const link_1 = require("@sima-land/ui-nucleons/link");
|
|
9
28
|
const rating_1 = require("@sima-land/ui-nucleons/rating");
|
|
10
|
-
const
|
|
29
|
+
const bind_1 = __importDefault(require("classnames/bind"));
|
|
11
30
|
const review_info_module_scss_1 = __importDefault(require("./review-info.module.scss"));
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
31
|
+
const on_1 = __importDefault(require("@sima-land/ui-nucleons/helpers/on"));
|
|
32
|
+
const shadows_1 = require("@sima-land/ui-nucleons/styling/shadows");
|
|
33
|
+
const custom_scrollbar_1 = require("@sima-land/ui-nucleons/_internal/custom-scrollbar");
|
|
34
|
+
const cx = bind_1.default.bind(review_info_module_scss_1.default);
|
|
35
|
+
const ReviewInfo = ({ rating, author, onGoToReview, content, className, notAffectRating, loading, }) => {
|
|
36
|
+
const [clamped, setClamped] = (0, react_1.useState)(false);
|
|
37
|
+
const [opened, setOpened] = (0, react_1.useState)(false);
|
|
38
|
+
const contentRef = (0, react_1.useRef)(null);
|
|
39
|
+
const checkClamp = (0, react_1.useCallback)(() => {
|
|
40
|
+
const contentEl = contentRef.current;
|
|
41
|
+
if (!opened && contentEl) {
|
|
42
|
+
setClamped(contentEl.scrollHeight > contentEl.clientHeight);
|
|
43
|
+
}
|
|
44
|
+
}, [opened]);
|
|
45
|
+
(0, react_1.useEffect)(() => {
|
|
46
|
+
setClamped(false);
|
|
47
|
+
setOpened(false);
|
|
48
|
+
}, [content, author]);
|
|
49
|
+
(0, react_1.useEffect)(checkClamp, [content, opened]);
|
|
50
|
+
// @todo что делать если при изменении размеров изменилось состояние обрезания?
|
|
51
|
+
(0, react_1.useEffect)(() => (0, on_1.default)(window, 'resize', checkClamp), [content, checkClamp]);
|
|
52
|
+
return (react_1.default.createElement("div", { className: cx('root', className, opened && shadows_1.BoxShadow.z2straight) },
|
|
53
|
+
loading && (react_1.default.createElement(Layout, null,
|
|
54
|
+
react_1.default.createElement(LoadingView, null))),
|
|
55
|
+
!loading && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
56
|
+
react_1.default.createElement(Layout, { className: cx('header') },
|
|
57
|
+
react_1.default.createElement("div", { className: cx('rating') }, notAffectRating ? 'Не влияет на рейтинг' : react_1.default.createElement(rating_1.Rating, { value: rating })),
|
|
58
|
+
react_1.default.createElement("div", { className: cx('author') }, author),
|
|
59
|
+
onGoToReview && (react_1.default.createElement(link_1.Link, { className: cx('link'), pseudo: true, onClick: onGoToReview, "data-testid": 'gallery-modal:go-to-review-link' }, "\u041F\u0435\u0440\u0435\u0439\u0442\u0438 \u043A \u043E\u0442\u0437\u044B\u0432\u0443"))),
|
|
60
|
+
content && !opened && (react_1.default.createElement(Layout, null,
|
|
61
|
+
react_1.default.createElement("div", { ref: contentRef, className: cx('content'), "data-testid": 'review-info:collapsed-content' }, content))),
|
|
62
|
+
content && opened && (react_1.default.createElement(custom_scrollbar_1.CustomScrollbar, { className: cx('content', 'opened') },
|
|
63
|
+
react_1.default.createElement(Layout, null,
|
|
64
|
+
react_1.default.createElement("div", { "data-testid": 'review-info:expanded-content' }, content),
|
|
65
|
+
react_1.default.createElement(link_1.Link, { pseudo: true, onClick: () => setOpened(false), "data-testid": 'review-info:collapse' }, "\u0421\u043A\u0440\u044B\u0442\u044C \u043E\u0442\u0437\u044B\u0432")))),
|
|
66
|
+
content && !opened && clamped && (react_1.default.createElement(Layout, null,
|
|
67
|
+
react_1.default.createElement(link_1.Link, { pseudo: true, onClick: () => setOpened(true), "data-testid": 'review-info:expand' }, "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u043E\u043B\u043D\u043E\u0441\u0442\u044C\u044E")))))));
|
|
68
|
+
};
|
|
16
69
|
exports.ReviewInfo = ReviewInfo;
|
|
70
|
+
function Layout({ className, children }) {
|
|
71
|
+
return react_1.default.createElement("div", { className: cx(className, 'layout') }, children);
|
|
72
|
+
}
|
|
73
|
+
function LoadingView() {
|
|
74
|
+
return (react_1.default.createElement("div", { className: cx('loading') },
|
|
75
|
+
react_1.default.createElement("div", { className: cx('head') },
|
|
76
|
+
react_1.default.createElement("div", { className: cx('text', 'name') }),
|
|
77
|
+
react_1.default.createElement(link_1.Link, { pseudo: true, disabled: true, className: cx('button') }, "\u041F\u0435\u0440\u0435\u0439\u0442\u0438 \u043A \u043E\u0442\u0437\u044B\u0432\u0443")),
|
|
78
|
+
react_1.default.createElement("div", { className: cx('text') }),
|
|
79
|
+
react_1.default.createElement("div", { className: cx('text') })));
|
|
80
|
+
}
|
|
@@ -1,15 +1,35 @@
|
|
|
1
1
|
@use 'node_modules/@sima-land/ui-nucleons/colors';
|
|
2
2
|
|
|
3
3
|
.root {
|
|
4
|
-
height:
|
|
5
|
-
|
|
4
|
+
min-height: 140px;
|
|
5
|
+
max-height: 314px;
|
|
6
6
|
font-size: 14px;
|
|
7
7
|
line-height: 20px;
|
|
8
|
+
padding-bottom: 24px;
|
|
9
|
+
background: #fff;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.layout {
|
|
13
|
+
width: var(--square-size);
|
|
14
|
+
margin: 0 auto;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.header {
|
|
18
|
+
height: 56px;
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
padding: 26px 0 14px;
|
|
8
22
|
white-space: nowrap;
|
|
9
23
|
}
|
|
10
24
|
|
|
25
|
+
.rating {
|
|
26
|
+
flex-shrink: 0;
|
|
27
|
+
font-weight: 600;
|
|
28
|
+
color: colors.$basic-gray38;
|
|
29
|
+
}
|
|
30
|
+
|
|
11
31
|
.author {
|
|
12
|
-
flex: 1;
|
|
32
|
+
flex-grow: 1;
|
|
13
33
|
margin-left: 16px;
|
|
14
34
|
overflow: hidden;
|
|
15
35
|
text-overflow: ellipsis;
|
|
@@ -18,5 +38,50 @@
|
|
|
18
38
|
|
|
19
39
|
.link {
|
|
20
40
|
margin-left: 16px;
|
|
21
|
-
font-weight:
|
|
41
|
+
font-weight: 600;
|
|
42
|
+
&:hover {
|
|
43
|
+
font-weight: 600;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.content {
|
|
48
|
+
&.opened {
|
|
49
|
+
max-height: calc(314px - 56px - 24px);
|
|
50
|
+
}
|
|
51
|
+
&:not(.opened) {
|
|
52
|
+
display: -webkit-box;
|
|
53
|
+
-webkit-box-orient: vertical;
|
|
54
|
+
-webkit-line-clamp: 2;
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.loading {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-wrap: wrap;
|
|
62
|
+
padding-top: 26px;
|
|
63
|
+
.head {
|
|
64
|
+
width: 100%;
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
margin-bottom: 8px;
|
|
68
|
+
}
|
|
69
|
+
.text {
|
|
70
|
+
width: 100%;
|
|
71
|
+
border-radius: 4px;
|
|
72
|
+
background: colors.$basic-gray4;
|
|
73
|
+
height: 16px;
|
|
74
|
+
line-height: 20px;
|
|
75
|
+
vertical-align: middle;
|
|
76
|
+
margin: 2px 0;
|
|
77
|
+
}
|
|
78
|
+
.name {
|
|
79
|
+
max-width: 240px;
|
|
80
|
+
margin-right: 16px;
|
|
81
|
+
}
|
|
82
|
+
.button {
|
|
83
|
+
margin-left: auto;
|
|
84
|
+
white-space: nowrap;
|
|
85
|
+
font-weight: 600;
|
|
86
|
+
}
|
|
22
87
|
}
|
|
@@ -6,11 +6,11 @@ import React from 'react';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare const useImagesLoad: (srcList: string[]) => boolean;
|
|
8
8
|
/**
|
|
9
|
-
* Хук, вернет размер квадрата вписанного в
|
|
9
|
+
* Хук, вернет размер квадрата вписанного в область окна галереи фото по макетам.
|
|
10
10
|
* @param areaRef Реф с элементом.
|
|
11
11
|
* @param options Опции.
|
|
12
12
|
* @return Размер или null.
|
|
13
13
|
*/
|
|
14
|
-
export declare const
|
|
15
|
-
|
|
14
|
+
export declare const useSquareFit: (areaRef: React.RefObject<HTMLDivElement | null>, { hasReview }: {
|
|
15
|
+
hasReview: boolean;
|
|
16
16
|
}) => number | null;
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.useSquareFit = exports.useImagesLoad = void 0;
|
|
7
7
|
const react_1 = require("react");
|
|
8
8
|
const on_1 = __importDefault(require("@sima-land/ui-nucleons/helpers/on"));
|
|
9
9
|
/**
|
|
@@ -25,29 +25,26 @@ const useImagesLoad = (srcList) => {
|
|
|
25
25
|
};
|
|
26
26
|
exports.useImagesLoad = useImagesLoad;
|
|
27
27
|
/**
|
|
28
|
-
* Хук, вернет размер квадрата вписанного в
|
|
28
|
+
* Хук, вернет размер квадрата вписанного в область окна галереи фото по макетам.
|
|
29
29
|
* @param areaRef Реф с элементом.
|
|
30
30
|
* @param options Опции.
|
|
31
31
|
* @return Размер или null.
|
|
32
32
|
*/
|
|
33
|
-
const
|
|
33
|
+
const useSquareFit = (areaRef, { hasReview }) => {
|
|
34
34
|
const [size, setSize] = (0, react_1.useState)(null);
|
|
35
35
|
(0, react_1.useEffect)(() => {
|
|
36
|
-
|
|
36
|
+
function calc() {
|
|
37
37
|
if (areaRef.current) {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
else {
|
|
44
|
-
setSize(availHeight - correction);
|
|
45
|
-
}
|
|
38
|
+
const correction = 2 * (104 + 16 + 56 + 24); // ширина превью + отступ + ширина кнопки + отступ
|
|
39
|
+
// доступная область в которую можно вписать **квадрат**
|
|
40
|
+
const availWidth = areaRef.current.clientWidth - correction;
|
|
41
|
+
const availHeight = areaRef.current.clientHeight - (hasReview ? 140 : 40); // вычитаем высоту футера
|
|
42
|
+
setSize(Math.min(availWidth, availHeight));
|
|
46
43
|
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return (0, on_1.default)(window, 'resize', () => window.requestAnimationFrame(
|
|
50
|
-
}, [areaRef]);
|
|
44
|
+
}
|
|
45
|
+
calc();
|
|
46
|
+
return (0, on_1.default)(window, 'resize', () => window.requestAnimationFrame(calc));
|
|
47
|
+
}, [areaRef, hasReview]);
|
|
51
48
|
return size;
|
|
52
49
|
};
|
|
53
|
-
exports.
|
|
50
|
+
exports.useSquareFit = useSquareFit;
|
|
@@ -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
|
+
}
|