@sima-land/moleculas 15.0.1 → 15.1.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.
@@ -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: ({ className, color, fields, href, style, "data-testid": testId, ...restProps }: BadgeProps) => JSX.Element;
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 = __importDefault(require("react"));
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
- const Badge = (_a) => {
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 Оставшееся время.
@@ -11,6 +11,7 @@
11
11
  }
12
12
 
13
13
  .item {
14
+ max-width: 100%;
14
15
  margin-bottom: 4px;
15
16
  &:not(:last-child) {
16
17
  margin-right: 4px;
@@ -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: ({ children, className, lineLimit, style }: BadgeListProps) => JSX.Element;
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).reduce((list, item) => {
37
- (0, react_1.isValidElement)(item) &&
38
- item.type === badge_1.Badge &&
39
- list.push((0, react_1.cloneElement)(item, { className: cx('item', item.props.className) }));
40
- return list;
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;
@@ -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 thumbnailsRef = (0, react_1.useRef)(null);
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: innerStyles },
74
- size !== null && media.length > 1 && (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
- }))))))),
78
- react_1.default.createElement("div", { ref: areaRef, className: cx('area') }, size !== null && (react_1.default.createElement("div", { className: cx('inner'), style: { width: `${size}px` } },
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
- review && (react_1.default.createElement(review_info_1.ReviewInfo, { rating: review.rating, author: review.author, onGoToReview: onGoToReview, className: cx('footer') })))))))));
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
- .thumbnails {
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
- .area {
20
- height: 100%;
21
- margin: 0 auto;
22
- width: calc(100% - (2 * (120px + 56px + 24px)));
23
- .inner {
24
- height: 100%;
25
- margin: 0 auto;
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
- .square {
28
- position: relative;
29
- &::after {
30
- content: '';
31
- display: block;
32
- padding-top: 100%;
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
- .button {
36
- position: absolute;
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
- margin-top: 40px;
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
- /// <reference types="react" />
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,75 @@
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 = __importDefault(require("react"));
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 classnames_1 = __importDefault(require("classnames"));
29
+ const bind_1 = __importDefault(require("classnames/bind"));
11
30
  const review_info_module_scss_1 = __importDefault(require("./review-info.module.scss"));
12
- const ReviewInfo = ({ rating, author, onGoToReview, className }) => (react_1.default.createElement("div", { className: (0, classnames_1.default)(review_info_module_scss_1.default.root, className) },
13
- react_1.default.createElement(rating_1.Rating, { value: rating }),
14
- react_1.default.createElement("div", { className: review_info_module_scss_1.default.author }, author),
15
- onGoToReview && (react_1.default.createElement(link_1.Link, { className: review_info_module_scss_1.default.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"))));
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)(checkClamp, [content, opened]);
46
+ (0, react_1.useEffect)(() => (0, on_1.default)(window, 'resize', checkClamp), [content, checkClamp]);
47
+ return (react_1.default.createElement("div", { className: cx('root', className, opened && shadows_1.BoxShadow.z2straight) },
48
+ loading && (react_1.default.createElement(Layout, null,
49
+ react_1.default.createElement(LoadingView, null))),
50
+ !loading && (react_1.default.createElement(react_1.default.Fragment, null,
51
+ react_1.default.createElement(Layout, { className: cx('header') },
52
+ react_1.default.createElement("div", { className: cx('rating') }, notAffectRating ? 'Не влияет на рейтинг' : react_1.default.createElement(rating_1.Rating, { value: rating })),
53
+ react_1.default.createElement("div", { className: cx('author') }, author),
54
+ 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"))),
55
+ content && !opened && (react_1.default.createElement(Layout, null,
56
+ react_1.default.createElement("div", { ref: contentRef, className: cx('content'), "data-testid": 'review-info:collapsed-content' }, content))),
57
+ content && opened && (react_1.default.createElement(custom_scrollbar_1.CustomScrollbar, { className: cx('content', 'opened') },
58
+ react_1.default.createElement(Layout, null,
59
+ react_1.default.createElement("div", { "data-testid": 'review-info:expanded-content' }, content),
60
+ 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")))),
61
+ content && !opened && clamped && (react_1.default.createElement(Layout, null,
62
+ 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")))))));
63
+ };
16
64
  exports.ReviewInfo = ReviewInfo;
65
+ function Layout({ className, children }) {
66
+ return react_1.default.createElement("div", { className: cx(className, 'layout') }, children);
67
+ }
68
+ function LoadingView() {
69
+ return (react_1.default.createElement("div", { className: cx('loading') },
70
+ react_1.default.createElement("div", { className: cx('head') },
71
+ react_1.default.createElement("div", { className: cx('text', 'name') }),
72
+ 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")),
73
+ react_1.default.createElement("div", { className: cx('text') }),
74
+ react_1.default.createElement("div", { className: cx('text') })));
75
+ }
@@ -1,15 +1,35 @@
1
1
  @use 'node_modules/@sima-land/ui-nucleons/colors';
2
2
 
3
3
  .root {
4
- height: 20px;
5
- display: flex;
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: 500;
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 useRectFit: (areaRef: React.RefObject<HTMLDivElement | null>, { correction }: {
15
- correction: number;
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.useRectFit = exports.useImagesLoad = void 0;
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 useRectFit = (areaRef, { correction }) => {
33
+ const useSquareFit = (areaRef, { hasReview }) => {
34
34
  const [size, setSize] = (0, react_1.useState)(null);
35
35
  (0, react_1.useEffect)(() => {
36
- const calcSquareSize = () => {
36
+ function calc() {
37
37
  if (areaRef.current) {
38
- const availWidth = areaRef.current.clientWidth;
39
- const availHeight = areaRef.current.clientHeight;
40
- if (availWidth + correction <= availHeight) {
41
- setSize(availWidth);
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
- calcSquareSize();
49
- return (0, on_1.default)(window, 'resize', () => window.requestAnimationFrame(calcSquareSize));
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.useRectFit = useRectFit;
50
+ exports.useSquareFit = useSquareFit;
@@ -48,7 +48,7 @@ const ProductSlider = ({ children, onInViewport, onNeedRequest }) => {
48
48
  react_1.default.createElement(touch_slider_1.TouchSlider, null, react_1.Children.toArray(children).reduce((list, item) => {
49
49
  (0, react_1.isValidElement)(item) &&
50
50
  item.type === product_info_1.ProductInfo &&
51
- list.push(react_1.default.createElement("div", { key: item.key, className: product_slider_module_scss_1.default.item }, item));
51
+ list.push(react_1.default.createElement("div", { key: item.key, className: product_slider_module_scss_1.default.item, "data-testid": 'product-slider:item' }, item));
52
52
  return list;
53
53
  }, []))));
54
54
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sima-land/moleculas",
3
- "version": "15.0.1",
3
+ "version": "15.1.0",
4
4
  "repository": "ssh://git@github.com:sima-land/moleculas.git",
5
5
  "author": "www.sima-land.ru team",
6
6
  "license": "Apache-2.0",