@sima-land/moleculas 16.0.0-alpha.2 → 16.0.0-alpha.3

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.
Files changed (33) hide show
  1. package/common/components/media-modal/__mocks__/index.d.ts +4 -0
  2. package/common/components/media-modal/__mocks__/index.js +141 -0
  3. package/common/components/media-modal/index.d.ts +9 -0
  4. package/common/components/media-modal/index.js +23 -0
  5. package/common/components/media-modal/layout.d.ts +36 -0
  6. package/common/components/media-modal/layout.js +70 -0
  7. package/common/components/media-modal/layout.module.scss +81 -0
  8. package/common/components/media-modal/parts/index.d.ts +36 -0
  9. package/common/components/media-modal/parts/index.js +60 -0
  10. package/common/components/media-modal/parts/media.d.ts +13 -0
  11. package/common/components/media-modal/parts/media.js +118 -0
  12. package/common/components/media-modal/parts/media.module.scss +62 -0
  13. package/common/components/media-modal/parts/parts.module.scss +21 -0
  14. package/common/components/media-modal/parts/product-brief.d.ts +17 -0
  15. package/common/components/media-modal/parts/product-brief.js +42 -0
  16. package/common/components/media-modal/parts/product-brief.module.scss +100 -0
  17. package/common/components/media-modal/parts/review.d.ts +13 -0
  18. package/common/components/media-modal/parts/review.js +24 -0
  19. package/common/components/media-modal/parts/review.module.scss +70 -0
  20. package/common/components/media-modal/parts/scroll-section.d.ts +9 -0
  21. package/common/components/media-modal/parts/scroll-section.js +19 -0
  22. package/common/components/media-modal/parts/scroll-section.module.scss +18 -0
  23. package/common/components/media-modal/parts/thumbnails.d.ts +27 -0
  24. package/common/components/media-modal/parts/thumbnails.js +92 -0
  25. package/common/components/media-modal/parts/thumbnails.module.scss +104 -0
  26. package/common/components/media-modal/types.d.ts +14 -0
  27. package/common/components/media-modal/types.js +2 -0
  28. package/common/components/media-modal/utils.d.ts +16 -0
  29. package/common/components/media-modal/utils.js +50 -0
  30. package/common/components/product-info/parts.js +6 -2
  31. package/common/components/product-info/product-info.module.scss +9 -0
  32. package/common/icons/image-broken.svg +7 -0
  33. package/package.json +6 -5
@@ -0,0 +1,21 @@
1
+ @use 'node_modules/@sima-land/ui-nucleons/breakpoints';
2
+ @use 'node_modules/@sima-land/ui-nucleons/utils';
3
+
4
+ .header-layout {
5
+ min-height: 40px; // ВАЖНО: чтобы при отсутствии табов не уменьшалась высота
6
+ @include breakpoints.up('xs') {
7
+ flex-wrap: wrap;
8
+ display: flex;
9
+ align-items: flex-end;
10
+ justify-content: space-between;
11
+ }
12
+ }
13
+
14
+ .header-button {
15
+ margin-top: 16px !important;
16
+ @include breakpoints.down('xs') {
17
+ &:first-child {
18
+ margin-left: auto;
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,17 @@
1
+ import { HTMLAttributes, MouseEventHandler, ReactNode } from 'react';
2
+ export interface ProductBriefProps extends HTMLAttributes<HTMLDivElement> {
3
+ size?: 's' | 'l';
4
+ title?: string;
5
+ price?: string | number;
6
+ currency?: string;
7
+ footer?: ReactNode;
8
+ href?: string;
9
+ onLinkClick?: MouseEventHandler<HTMLAnchorElement>;
10
+ imageSrc?: string;
11
+ }
12
+ /**
13
+ * Блок товара-рекомендации.
14
+ * @param props Свойства.
15
+ * @return Элемент.
16
+ */
17
+ export declare function ProductBrief({ size: sizeProp, imageSrc, title, price, currency, footer, className, href, onLinkClick, ...restProps }: ProductBriefProps): JSX.Element;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.ProductBrief = void 0;
18
+ const react_1 = __importDefault(require("react"));
19
+ const image_overlay_1 = require("../../../../desktop/components/gallery-modal/components/image-overlay");
20
+ const price_1 = require("@sima-land/ui-nucleons/price");
21
+ const classnames_1 = __importDefault(require("classnames"));
22
+ const product_brief_module_scss_1 = __importDefault(require("./product-brief.module.scss"));
23
+ const breakpoint_1 = require("@sima-land/ui-nucleons/hooks/breakpoint");
24
+ /**
25
+ * Блок товара-рекомендации.
26
+ * @param props Свойства.
27
+ * @return Элемент.
28
+ */
29
+ function ProductBrief(_a) {
30
+ var { size: sizeProp, imageSrc, title, price, currency, footer, className, href, onLinkClick } = _a, restProps = __rest(_a, ["size", "imageSrc", "title", "price", "currency", "footer", "className", "href", "onLinkClick"]);
31
+ const desktop = (0, breakpoint_1.useBreakpoint)('xs+');
32
+ const size = (sizeProp !== null && sizeProp !== void 0 ? sizeProp : desktop) ? 'l' : 's';
33
+ return (react_1.default.createElement("div", Object.assign({ className: (0, classnames_1.default)(product_brief_module_scss_1.default.root, product_brief_module_scss_1.default[`size-${size}`], className) }, restProps),
34
+ react_1.default.createElement("a", { className: product_brief_module_scss_1.default.image, href: href, onClick: onLinkClick },
35
+ react_1.default.createElement(image_overlay_1.ImageOverlay, { className: product_brief_module_scss_1.default.overlay },
36
+ react_1.default.createElement("img", { src: imageSrc }))),
37
+ react_1.default.createElement("div", { className: product_brief_module_scss_1.default.info },
38
+ react_1.default.createElement("a", { className: product_brief_module_scss_1.default.title, href: href, onClick: onLinkClick }, title),
39
+ price && react_1.default.createElement(price_1.Price, { className: product_brief_module_scss_1.default.price, value: price, currencyGrapheme: currency })),
40
+ footer && react_1.default.createElement("div", { className: product_brief_module_scss_1.default.footer }, footer)));
41
+ }
42
+ exports.ProductBrief = ProductBrief;
@@ -0,0 +1,100 @@
1
+ @use 'node_modules/@sima-land/ui-nucleons/colors';
2
+
3
+ .root {
4
+ display: grid;
5
+ }
6
+
7
+ .image {
8
+ display: block;
9
+ .overlay {
10
+ width: 100%;
11
+ height: 100%;
12
+ > img {
13
+ display: block;
14
+ width: 100%;
15
+ height: 100%;
16
+ object-fit: contain;
17
+ }
18
+ }
19
+ }
20
+
21
+ .info {
22
+ font-size: 20px;
23
+ line-height: 28px;
24
+ }
25
+
26
+ .title {
27
+ display: block;
28
+ text-decoration: none;
29
+ color: colors.$basic-gray87;
30
+ }
31
+
32
+ .price {
33
+ font-weight: 600;
34
+ }
35
+
36
+ .footer {
37
+ display: flex;
38
+ > * {
39
+ width: 128px;
40
+ --stepper-width: 128px;
41
+ }
42
+ > *:last-child {
43
+ margin-left: 8px;
44
+ }
45
+ }
46
+
47
+ .size-s {
48
+ column-gap: 8px;
49
+ row-gap: 16px;
50
+ grid-template-columns: 88px 1fr;
51
+ .image {
52
+ width: 88px;
53
+ height: 88px;
54
+ grid-column: 1 / 2;
55
+ grid-row: 1 / 2;
56
+ }
57
+ .info {
58
+ grid-column: 2 / 3;
59
+ grid-row: 1 / 2;
60
+ }
61
+ .title {
62
+ display: -webkit-box;
63
+ -webkit-box-orient: vertical;
64
+ -webkit-line-clamp: 2;
65
+ overflow: hidden;
66
+ }
67
+ .title + .price {
68
+ margin-top: 4px;
69
+ }
70
+ .footer {
71
+ grid-column: 1 / 3;
72
+ grid-row: 2 / 3;
73
+ }
74
+ }
75
+
76
+ .size-l {
77
+ column-gap: 16px;
78
+ row-gap: 16px;
79
+ grid-template-columns: 112px 1fr;
80
+ .image {
81
+ width: 112px;
82
+ height: 112px;
83
+ grid-column: 1 / 2;
84
+ grid-row: 1 / 3;
85
+ }
86
+ .info {
87
+ min-width: 0; // ВАЖНО: чтобы title не вызывал переполнение
88
+ grid-column: 2 / 3;
89
+ grid-row: 1 / 2;
90
+ .title {
91
+ overflow: hidden;
92
+ white-space: nowrap;
93
+ text-overflow: ellipsis;
94
+ }
95
+ }
96
+ .footer {
97
+ grid-column: 2 / 3;
98
+ grid-row: 2 / 3;
99
+ }
100
+ }
@@ -0,0 +1,13 @@
1
+ import { ReactNode } from 'react';
2
+ export interface ReviewProps {
3
+ rating?: number;
4
+ ratingPlaceholder?: string;
5
+ author?: string;
6
+ children?: ReactNode;
7
+ }
8
+ /**
9
+ * Отзыв.
10
+ * @param props Свойства.
11
+ * @return Элемент.
12
+ */
13
+ export declare function Review({ rating, ratingPlaceholder, author, children }: ReviewProps): JSX.Element;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Review = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const rating_1 = require("@sima-land/ui-nucleons/rating");
9
+ const text_button_1 = require("@sima-land/ui-nucleons/text-button");
10
+ const review_module_scss_1 = __importDefault(require("./review.module.scss"));
11
+ /**
12
+ * Отзыв.
13
+ * @param props Свойства.
14
+ * @return Элемент.
15
+ */
16
+ function Review({ rating, ratingPlaceholder, author, children }) {
17
+ return (react_1.default.createElement("div", { className: review_module_scss_1.default.root },
18
+ react_1.default.createElement("div", { className: review_module_scss_1.default.rating }, typeof rating === 'number' ? react_1.default.createElement(rating_1.Rating, { size: 's', value: rating }) : ratingPlaceholder),
19
+ react_1.default.createElement("div", { className: review_module_scss_1.default.author }, author),
20
+ react_1.default.createElement("div", { className: review_module_scss_1.default.link },
21
+ react_1.default.createElement(text_button_1.TextButton, { size: 's' }, "\u041F\u0435\u0440\u0435\u0439\u0442\u0438 \u043A \u043E\u0442\u0437\u044B\u0432\u0443")),
22
+ react_1.default.createElement("div", { className: review_module_scss_1.default.content }, children)));
23
+ }
24
+ exports.Review = Review;
@@ -0,0 +1,70 @@
1
+ @use 'node_modules/@sima-land/ui-nucleons/breakpoints';
2
+ @use 'node_modules/@sima-land/ui-nucleons/colors';
3
+
4
+ .root {
5
+ display: grid;
6
+ font-size: 14px;
7
+ line-height: 20px;
8
+ }
9
+
10
+ .rating {
11
+ height: 20px;
12
+ display: flex;
13
+ align-items: center;
14
+ }
15
+
16
+ .author {
17
+ overflow: hidden;
18
+ text-overflow: ellipsis;
19
+ white-space: nowrap;
20
+ color: colors.$basic-gray24;
21
+ }
22
+
23
+ @include breakpoints.down('xs') {
24
+ .root {
25
+ gap: 12px;
26
+ grid-template-columns: auto 1fr;
27
+ }
28
+ .rating {
29
+ grid-column: 1 / 2;
30
+ grid-row: 1 / 2;
31
+ }
32
+ .author {
33
+ grid-column: 2 / 3;
34
+ grid-row: 1 / 2;
35
+ }
36
+ .link {
37
+ grid-column: 1 / 3;
38
+ }
39
+ .content {
40
+ grid-column: 1 / 3;
41
+ }
42
+ }
43
+
44
+ @include breakpoints.up('xs') {
45
+ .root {
46
+ column-gap: 16px;
47
+ row-gap: 12px;
48
+ grid-template-columns: auto 1fr auto;
49
+ }
50
+ .rating {
51
+ grid-column: 1 / 2;
52
+ grid-row: 1 / 2;
53
+ }
54
+ .author {
55
+ grid-column: 2 / 3;
56
+ grid-row: 1 / 2;
57
+ overflow: hidden;
58
+ text-overflow: ellipsis;
59
+ white-space: nowrap;
60
+ color: colors.$basic-gray24;
61
+ }
62
+ .link {
63
+ grid-column: 3 / 4;
64
+ grid-row: 1 / 2;
65
+ }
66
+ .content {
67
+ grid-column: 1 / 4;
68
+ grid-row: 2 / 3;
69
+ }
70
+ }
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from 'react';
2
+ /**
3
+ * Секция с прокруткой по горизонтали по дизайн-гайдам.
4
+ * @param props Свойства.
5
+ * @return Элемент.
6
+ */
7
+ export declare function ScrollSection({ children }: {
8
+ children?: ReactNode;
9
+ }): JSX.Element;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ScrollSection = void 0;
7
+ const layout_1 = require("@sima-land/ui-nucleons/layout");
8
+ const react_1 = __importDefault(require("react"));
9
+ const scroll_section_module_scss_1 = __importDefault(require("./scroll-section.module.scss"));
10
+ /**
11
+ * Секция с прокруткой по горизонтали по дизайн-гайдам.
12
+ * @param props Свойства.
13
+ * @return Элемент.
14
+ */
15
+ function ScrollSection({ children }) {
16
+ return (react_1.default.createElement(layout_1.Layout, { disabledOn: ['mxs', 'ms'] },
17
+ react_1.default.createElement("div", { className: scroll_section_module_scss_1.default.inner }, children)));
18
+ }
19
+ exports.ScrollSection = ScrollSection;
@@ -0,0 +1,18 @@
1
+ @use 'node_modules/@sima-land/ui-nucleons/breakpoints';
2
+ @use 'node_modules/@sima-land/ui-nucleons/utils';
3
+
4
+ .inner {
5
+ display: flex;
6
+ overflow-x: scroll;
7
+ @include utils.hidden-scrollbars;
8
+ @include breakpoints.down('mm') {
9
+ padding-left: 16px;
10
+ padding-right: 16px;
11
+ }
12
+ > * {
13
+ flex-shrink: 0;
14
+ }
15
+ > *:not(:last-child) {
16
+ margin-right: 8px;
17
+ }
18
+ }
@@ -0,0 +1,27 @@
1
+ import React, { ReactNode } from 'react';
2
+ export interface ThumbnailProps {
3
+ size?: 's' | 'l';
4
+ type: 'icon-360' | 'icon-video' | 'preview-image' | 'preview-video';
5
+ src?: string;
6
+ alt?: string;
7
+ checked?: boolean;
8
+ onClick?: React.MouseEventHandler;
9
+ className?: string;
10
+ loading?: boolean;
11
+ }
12
+ export interface ThumbnailsProps {
13
+ size?: 's' | 'l';
14
+ children?: ReactNode;
15
+ }
16
+ /**
17
+ * Компонент превью медиа.
18
+ * @param props Свойства.
19
+ * @return Элемент.
20
+ */
21
+ export declare function Thumbnail({ size: sizeFromProps, type, src, alt, checked, onClick, className, loading, }: ThumbnailProps): JSX.Element;
22
+ /**
23
+ * Компонент превью медиа.
24
+ * @param props Свойства.
25
+ * @return Элемент.
26
+ */
27
+ export declare function Thumbnails({ children, ...rest }: ThumbnailsProps): JSX.Element | null;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __rest = (this && this.__rest) || function (s, e) {
26
+ var t = {};
27
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
28
+ t[p] = s[p];
29
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
30
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
31
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
32
+ t[p[i]] = s[p[i]];
33
+ }
34
+ return t;
35
+ };
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.Thumbnails = exports.Thumbnail = void 0;
41
+ const react_1 = __importStar(require("react"));
42
+ const image_overlay_1 = require("../../../../desktop/components/gallery-modal/components/image-overlay");
43
+ const breakpoint_1 = require("@sima-land/ui-nucleons/hooks/breakpoint");
44
+ const play_1 = __importDefault(require("@sima-land/ui-quarks/icons/24x24/Filled/play"));
45
+ const _360_svg_1 = __importDefault(require("../../../../desktop/components/gallery-modal/icons/360.svg"));
46
+ const bind_1 = __importDefault(require("classnames/bind"));
47
+ const thumbnails_module_scss_1 = __importDefault(require("./thumbnails.module.scss"));
48
+ const utils_1 = require("../utils");
49
+ const scroll_section_1 = require("./scroll-section");
50
+ const cx = bind_1.default.bind(thumbnails_module_scss_1.default);
51
+ /**
52
+ * Компонент превью медиа.
53
+ * @param props Свойства.
54
+ * @return Элемент.
55
+ */
56
+ function Thumbnail({ size: sizeFromProps, type, src, alt, checked, onClick, className, loading, }) {
57
+ var _a;
58
+ const { size: sizeFromContext } = (0, react_1.useContext)(ThumbnailsContext);
59
+ const size = (_a = sizeFromProps !== null && sizeFromProps !== void 0 ? sizeFromProps : sizeFromContext) !== null && _a !== void 0 ? _a : 'l';
60
+ const rootClassName = cx('thumbnail', `size-${size}`, (type === null || type === void 0 ? void 0 : type.startsWith('icon-')) && 'iconic', checked && 'checked', loading && 'loading', className);
61
+ if (loading) {
62
+ return react_1.default.createElement("button", { className: rootClassName, onClick: onClick, "data-testid": 'thumbnail' });
63
+ }
64
+ return (react_1.default.createElement("button", { className: rootClassName, onClick: onClick, "data-testid": 'thumbnail' },
65
+ (type === null || type === void 0 ? void 0 : type.startsWith('preview-')) && (react_1.default.createElement(image_overlay_1.ImageOverlay, { className: cx('image-overlay') },
66
+ react_1.default.createElement("img", { src: src, alt: alt }))),
67
+ type === 'preview-video' && react_1.default.createElement("span", { className: cx('play') }),
68
+ type === 'icon-360' && react_1.default.createElement(_360_svg_1.default, { fill: 'currentColor' }),
69
+ type === 'icon-video' && react_1.default.createElement(play_1.default, { width: 32, height: 32, fill: 'currentColor' })));
70
+ }
71
+ exports.Thumbnail = Thumbnail;
72
+ /**
73
+ * Компонент превью медиа.
74
+ * @param props Свойства.
75
+ * @return Элемент.
76
+ */
77
+ function Thumbnails(_a) {
78
+ var { children } = _a, rest = __rest(_a, ["children"]);
79
+ const mounted = (0, utils_1.useMounted)();
80
+ const desktop = (0, breakpoint_1.useBreakpoint)('xs+');
81
+ if (!mounted) {
82
+ return null;
83
+ }
84
+ if (!desktop) {
85
+ return (react_1.default.createElement(ThumbnailsContext.Provider, { value: Object.assign({ size: 's' }, rest) },
86
+ react_1.default.createElement(scroll_section_1.ScrollSection, null, children)));
87
+ }
88
+ return (react_1.default.createElement(ThumbnailsContext.Provider, { value: Object.assign({ size: 'l' }, rest) },
89
+ react_1.default.createElement("div", { className: cx('list-desktop') }, children)));
90
+ }
91
+ exports.Thumbnails = Thumbnails;
92
+ const ThumbnailsContext = (0, react_1.createContext)({});
@@ -0,0 +1,104 @@
1
+ @use 'node_modules/@sima-land/ui-nucleons/breakpoints';
2
+ @use 'node_modules/@sima-land/ui-nucleons/colors';
3
+ @use 'node_modules/@sima-land/ui-nucleons/utils';
4
+
5
+ .thumbnail {
6
+ --thumbnail-color: #{colors.$basic-gray12};
7
+ --thumbnail-border-size: 1px;
8
+ position: relative;
9
+ display: flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ background: transparent;
13
+ border-radius: 4px;
14
+ border: 0;
15
+ padding: 0;
16
+ color: var(--thumbnail-color);
17
+ outline: 0;
18
+ &.size-s {
19
+ width: 48px;
20
+ height: 48px;
21
+ }
22
+ &.size-l {
23
+ width: 80px;
24
+ height: 80px;
25
+ }
26
+ &.iconic {
27
+ --thumbnail-border-size: 2px;
28
+ }
29
+ &.checked {
30
+ --thumbnail-color: #{colors.$basic-blue};
31
+ --thumbnail-border-size: 2px;
32
+ }
33
+ &:not(.checked):not(.loading):hover {
34
+ cursor: pointer;
35
+ --thumbnail-color: #{colors.$basic-gray38};
36
+ }
37
+ &.loading {
38
+ background: colors.$basic-gray4;
39
+ --thumbnail-color: #{colors.$basic-gray4};
40
+ --thumbnail-border-size: 0;
41
+ }
42
+ .play {
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ width: 24px;
47
+ height: 24px;
48
+ border-radius: 50%;
49
+ background: rgba(0, 0, 0, 0.4);
50
+ z-index: 2;
51
+ pointer-events: none;
52
+ &::after {
53
+ content: '';
54
+ display: block;
55
+ width: 0;
56
+ height: 0;
57
+ border-style: solid;
58
+ border-width: 4px 0 4px 7px;
59
+ border-color: transparent transparent transparent #fff;
60
+ transform: translateX(1px);
61
+ }
62
+ }
63
+ .image-overlay {
64
+ position: absolute;
65
+ top: 0;
66
+ left: 0;
67
+ height: 100%;
68
+ width: 100%;
69
+ border-radius: inherit;
70
+ overflow: hidden;
71
+ > img {
72
+ display: block;
73
+ width: 100%;
74
+ height: 100%;
75
+ object-fit: contain;
76
+ }
77
+ }
78
+ &::after {
79
+ content: '';
80
+ position: absolute;
81
+ top: 0;
82
+ left: 0;
83
+ display: block;
84
+ height: 100%;
85
+ width: 100%;
86
+ border-radius: inherit;
87
+ box-shadow: inset 0 0 0 var(--thumbnail-border-size) var(--thumbnail-color);
88
+ }
89
+ }
90
+
91
+ .list-desktop {
92
+ display: flex;
93
+ height: 0;
94
+ flex-grow: 1;
95
+ flex-direction: column;
96
+ overflow-y: auto;
97
+ @include utils.semi-stylized-scrollbars;
98
+ > * {
99
+ flex-shrink: 0;
100
+ }
101
+ > *:not(:last-child) {
102
+ margin-bottom: 8px;
103
+ }
104
+ }
@@ -0,0 +1,14 @@
1
+ export type MediaType = 'image' | 'video' | '360';
2
+ export interface MediaShape<T extends string, D extends Record<string, any>> {
3
+ type: T;
4
+ data: D;
5
+ }
6
+ export type MediaData = MediaShape<'image', {
7
+ src: string;
8
+ alt?: string;
9
+ }> | MediaShape<'video', {
10
+ src: string;
11
+ alt?: string;
12
+ }> | MediaShape<'360', {
13
+ photos: string[];
14
+ }>;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,16 @@
1
+ import { RefObject } from 'react';
2
+ /**
3
+ * Возвращает ограничивающий прямоугольник элемента в виде состояния.
4
+ * @param ref Ref элемента.
5
+ * @return Прямоугольник.
6
+ */
7
+ export declare function useClientRect(ref: RefObject<HTMLElement>): {
8
+ ready: boolean;
9
+ width: number;
10
+ height: number;
11
+ };
12
+ /**
13
+ * Возвращает true если компонент смонтирован.
14
+ * @return True если компонент смонтирован.
15
+ */
16
+ export declare function useMounted(): boolean;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useMounted = exports.useClientRect = void 0;
4
+ const react_1 = require("react");
5
+ /**
6
+ * Возвращает ограничивающий прямоугольник элемента в виде состояния.
7
+ * @param ref Ref элемента.
8
+ * @return Прямоугольник.
9
+ */
10
+ function useClientRect(ref) {
11
+ const [rect, setRect] = (0, react_1.useState)(() => ({
12
+ ready: false,
13
+ width: 0,
14
+ height: 0,
15
+ }));
16
+ (0, react_1.useEffect)(() => {
17
+ if (ref.current) {
18
+ const element = ref.current;
19
+ setRect({
20
+ ready: true,
21
+ width: element.getBoundingClientRect().width,
22
+ height: element.getBoundingClientRect().height,
23
+ });
24
+ const observer = new ResizeObserver(() => {
25
+ setRect({
26
+ ready: true,
27
+ width: element.getBoundingClientRect().width,
28
+ height: element.getBoundingClientRect().height,
29
+ });
30
+ });
31
+ observer.observe(element);
32
+ return () => observer.disconnect();
33
+ }
34
+ }, [ref]);
35
+ return rect;
36
+ }
37
+ exports.useClientRect = useClientRect;
38
+ /**
39
+ * Возвращает true если компонент смонтирован.
40
+ * @return True если компонент смонтирован.
41
+ */
42
+ function useMounted() {
43
+ const [mounted, setMounted] = (0, react_1.useState)(false);
44
+ (0, react_1.useEffect)(() => {
45
+ setMounted(true);
46
+ return () => setMounted(false);
47
+ }, []);
48
+ return mounted;
49
+ }
50
+ exports.useMounted = useMounted;