@sima-land/moleculas 16.0.0-alpha.5 → 16.0.0-alpha.7
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/media-modal/__mocks__/index.js +1 -0
- package/common/components/media-modal/parts/all-round-view.js +2 -2
- package/common/components/media-modal/parts/index.js +2 -2
- package/common/components/media-modal/parts/media-content.d.ts +3 -1
- package/common/components/media-modal/parts/media-content.js +20 -5
- package/common/components/media-modal/parts/media-view.js +13 -2
- package/common/components/media-modal/parts/media-view.module.scss +5 -0
- package/common/components/media-modal/parts/product-brief.js +6 -2
- package/common/components/media-modal/parts/product-brief.module.scss +5 -0
- package/common/components/media-modal/parts/thumbnail.js +9 -5
- package/common/components/media-modal/parts/thumbnail.module.scss +5 -0
- package/common/components/modifier/index.d.ts +2 -48
- package/common/components/modifier/index.js +3 -100
- package/common/components/modifier/modifier.d.ts +14 -0
- package/common/components/modifier/modifier.js +117 -0
- package/common/components/modifier/{modifiers.module.scss → modifier.module.scss} +21 -0
- package/common/components/modifier/types.d.ts +35 -0
- package/common/components/modifier/types.js +2 -0
- package/common/components/product-info/parts.js +2 -2
- package/common/components/product-info/types.d.ts +2 -2
- package/common/components/rating-counter/index.d.ts +11 -1
- package/common/components/rating-counter/index.js +4 -4
- package/common/components/rating-counter/rating-counter-util.scss +1 -1
- package/common/hooks/index.d.ts +13 -0
- package/common/hooks/index.js +24 -0
- package/desktop/components/gallery-modal/components/all-round-view.js +13 -6
- package/desktop/components/gallery-modal/components/all-round-view.module.scss +5 -0
- package/desktop/components/gallery-modal/components/thumbnail.js +2 -2
- package/desktop/components/gallery-modal/utils.d.ts +3 -1
- package/desktop/components/gallery-modal/utils.js +15 -3
- package/desktop/components/product-carousel/index.d.ts +11 -6
- package/desktop/components/product-carousel/index.js +19 -18
- package/desktop/components/promotion-card/index.js +2 -2
- package/mobile/components/modifiers-list/index.js +2 -2
- package/mobile/components/modifiers-list/modifier-item/index.js +2 -2
- package/mobile/components/person-info/index.js +8 -8
- package/mobile/components/product-row/index.js +5 -5
- package/mobile/components/pseudo-input/index.js +2 -2
- package/mobile/components/search-bar/index.js +6 -6
- package/mobile/components/select-screen/index.d.ts +4 -4
- package/mobile/components/select-screen/index.js +7 -7
- package/package.json +2 -2
- package/common/components/rating-counter/star.svg +0 -5
- package/common/icons/eighteen-plus.svg +0 -5
- package/common/icons/touch-x.svg +0 -6
|
@@ -42,7 +42,7 @@ const react_1 = __importStar(require("react"));
|
|
|
42
42
|
const breakpoint_1 = require("@sima-land/ui-nucleons/hooks/breakpoint");
|
|
43
43
|
const all_round_view_1 = require("../../../../desktop/components/gallery-modal/components/all-round-view");
|
|
44
44
|
const utils_1 = require("../utils");
|
|
45
|
-
const
|
|
45
|
+
const TouchScrollX_1 = __importDefault(require("@sima-land/ui-quarks/icons/64x64/Colorful/TouchScrollX"));
|
|
46
46
|
const all_round_view_module_scss_1 = __importDefault(require("./all-round-view.module.scss"));
|
|
47
47
|
/**
|
|
48
48
|
* Просмотр 360.
|
|
@@ -73,6 +73,6 @@ function AfterMount(_a) {
|
|
|
73
73
|
}, []);
|
|
74
74
|
return (react_1.default.createElement("div", { className: all_round_view_module_scss_1.default.root },
|
|
75
75
|
!desktop && tutorial && (react_1.default.createElement("div", { className: all_round_view_module_scss_1.default.tutorial, onPointerUp: acceptTutorial },
|
|
76
|
-
react_1.default.createElement(
|
|
76
|
+
react_1.default.createElement(TouchScrollX_1.default, null))),
|
|
77
77
|
react_1.default.createElement(all_round_view_1.AllRoundView, Object.assign({}, props, { controls: desktop, state: state, onStateChange: setState }))));
|
|
78
78
|
}
|
|
@@ -10,11 +10,11 @@ const define_slots_1 = require("@sima-land/ui-nucleons/helpers/define-slots");
|
|
|
10
10
|
const breakpoint_1 = require("@sima-land/ui-nucleons/hooks/breakpoint");
|
|
11
11
|
const utils_1 = require("../utils");
|
|
12
12
|
const scroll_section_1 = require("./scroll-section");
|
|
13
|
-
const
|
|
13
|
+
const Left_1 = __importDefault(require("@sima-land/ui-quarks/icons/16x16/Stroked/Arrows/Left"));
|
|
14
14
|
const parts_module_scss_1 = __importDefault(require("./parts.module.scss"));
|
|
15
15
|
exports.Preset = {
|
|
16
16
|
headerTabs: (userProps) => (Object.assign({ view: 'clean-underline' }, userProps)),
|
|
17
|
-
headerButton: (userProps) => (Object.assign({ size: 's', startIcon:
|
|
17
|
+
headerButton: (userProps) => (Object.assign({ size: 's', startIcon: Left_1.default, iconGutter: 4, className: parts_module_scss_1.default['header-button'], children: 'Вернуться к основному товару' }, userProps)),
|
|
18
18
|
};
|
|
19
19
|
/**
|
|
20
20
|
* Формирует layout для шапки модального окна медиа.
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { MediaData } from '../types';
|
|
3
3
|
export interface MediaContentProps {
|
|
4
|
-
|
|
4
|
+
children?: ReactNode;
|
|
5
5
|
targetIndex?: number;
|
|
6
6
|
onChangeTargetIndex?: (newIndex: number) => void;
|
|
7
7
|
loading?: boolean;
|
|
8
|
+
/** @deprecated */
|
|
9
|
+
items?: MediaData[];
|
|
8
10
|
}
|
|
9
11
|
export interface FitSquareProps {
|
|
10
12
|
children?: ReactNode;
|
|
@@ -50,26 +50,28 @@ exports.MediaContent = MediaContent;
|
|
|
50
50
|
* @param props Свойства.
|
|
51
51
|
* @return Элемент.
|
|
52
52
|
*/
|
|
53
|
-
function MediaCarousel({ items, targetIndex, onChangeTargetIndex, loading }) {
|
|
53
|
+
function MediaCarousel({ items: itemsProp, children, targetIndex, onChangeTargetIndex, loading, }) {
|
|
54
54
|
const ref = (0, react_1.useRef)(null);
|
|
55
55
|
const area = (0, utils_1.useClientRect)(ref);
|
|
56
|
+
const items = toViewPropsList(itemsProp, children);
|
|
56
57
|
return (react_1.default.createElement("div", { ref: ref, className: media_content_module_scss_1.default.carousel }, area.ready && (react_1.default.createElement(carousel_1.Carousel, { infinite: false, targetIndex: targetIndex, onChangeTargetIndex: onChangeTargetIndex, draggable: true, containerProps: {
|
|
57
58
|
className: media_content_module_scss_1.default.carousel,
|
|
58
59
|
// в случае если будет работать некорректно - дать возможность задавать пропсы для Carousel
|
|
59
60
|
[page_scroll_lock_1.BSL_IGNORE_ATTR]: true,
|
|
60
|
-
}, withControls: false, items: items, renderItem:
|
|
61
|
+
}, withControls: false, items: items, renderItem: (props) => (react_1.default.createElement("div", { className: media_content_module_scss_1.default['carousel-item'] },
|
|
61
62
|
react_1.default.createElement(FitSquare, Object.assign({}, area),
|
|
62
|
-
react_1.default.createElement(media_view_1.MediaView, {
|
|
63
|
+
react_1.default.createElement(media_view_1.MediaView, Object.assign({ loading: loading }, props))))) }))));
|
|
63
64
|
}
|
|
64
65
|
/**
|
|
65
66
|
* Отображает медиа-контент в виде слайдера.
|
|
66
67
|
* @param props Свойства.
|
|
67
68
|
* @return Элемент.
|
|
68
69
|
*/
|
|
69
|
-
function MediaSlider({ items, targetIndex, onChangeTargetIndex, loading }) {
|
|
70
|
+
function MediaSlider({ items: itemsProp, children, targetIndex, onChangeTargetIndex, loading, }) {
|
|
70
71
|
const [position, setPosition] = (0, react_1.useState)(0);
|
|
71
72
|
const ref = (0, react_1.useRef)(null);
|
|
72
73
|
const area = (0, utils_1.useClientRect)(ref);
|
|
74
|
+
const items = toViewPropsList(itemsProp, children);
|
|
73
75
|
const total = items.length;
|
|
74
76
|
const controls = total > 1;
|
|
75
77
|
const correction = controls ? -(2 * (56 + 24)) : 0;
|
|
@@ -85,13 +87,26 @@ function MediaSlider({ items, targetIndex, onChangeTargetIndex, loading }) {
|
|
|
85
87
|
onChangeTargetIndex === null || onChangeTargetIndex === void 0 ? void 0 : onChangeTargetIndex(next);
|
|
86
88
|
} })),
|
|
87
89
|
react_1.default.createElement(FitSquare, { width: area.width + correction, height: area.height },
|
|
88
|
-
react_1.default.createElement(media_view_1.MediaView, {
|
|
90
|
+
react_1.default.createElement(media_view_1.MediaView, Object.assign({ loading: loading }, items[position]))),
|
|
89
91
|
controls && (react_1.default.createElement(arrow_button_1.ArrowButton, { direction: 'right', onClick: () => {
|
|
90
92
|
const next = (position + 1) % total;
|
|
91
93
|
setPosition(next);
|
|
92
94
|
onChangeTargetIndex === null || onChangeTargetIndex === void 0 ? void 0 : onChangeTargetIndex(next);
|
|
93
95
|
} }))))));
|
|
94
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Получив пропсы items и children вернет массив пропсов для MediaView.
|
|
99
|
+
* @param items Проп items.
|
|
100
|
+
* @param children Проп children.
|
|
101
|
+
* @return Массив пропсов для MediaVuew.
|
|
102
|
+
*/
|
|
103
|
+
function toViewPropsList(items, children) {
|
|
104
|
+
var _a;
|
|
105
|
+
return ((_a = items === null || items === void 0 ? void 0 : items.map(media => ({ media }))) !== null && _a !== void 0 ? _a : react_1.Children.toArray(children).reduce((acc, item) => {
|
|
106
|
+
(0, react_1.isValidElement)(item) && item.type === media_view_1.MediaView && acc.push(item.props);
|
|
107
|
+
return acc;
|
|
108
|
+
}, []));
|
|
109
|
+
}
|
|
95
110
|
/**
|
|
96
111
|
* Вписывает квадрат в заданную прямоугольную область.
|
|
97
112
|
* @param props Свойства.
|
|
@@ -30,9 +30,11 @@ exports.MediaView = void 0;
|
|
|
30
30
|
const react_1 = __importStar(require("react"));
|
|
31
31
|
const image_overlay_1 = require("../../../../desktop/components/gallery-modal/components/image-overlay");
|
|
32
32
|
const breakpoint_1 = require("@sima-land/ui-nucleons/hooks/breakpoint");
|
|
33
|
-
const media_view_module_scss_1 = __importDefault(require("./media-view.module.scss"));
|
|
34
33
|
const classnames_1 = __importDefault(require("classnames"));
|
|
35
34
|
const all_round_view_1 = require("./all-round-view");
|
|
35
|
+
const image_broken_svg_1 = __importDefault(require("../../../icons/image-broken.svg"));
|
|
36
|
+
const media_view_module_scss_1 = __importDefault(require("./media-view.module.scss"));
|
|
37
|
+
const hooks_1 = require("../../../hooks");
|
|
36
38
|
/**
|
|
37
39
|
* Отображает медиа-контент - 360, видео или изображение.
|
|
38
40
|
* @param props Свойства.
|
|
@@ -40,6 +42,14 @@ const all_round_view_1 = require("./all-round-view");
|
|
|
40
42
|
*/
|
|
41
43
|
function MediaView({ media, loading, videoProps }) {
|
|
42
44
|
const desktop = (0, breakpoint_1.useBreakpoint)('xs+');
|
|
45
|
+
let imageSrc = undefined;
|
|
46
|
+
if ((media === null || media === void 0 ? void 0 : media.type) === 'image') {
|
|
47
|
+
imageSrc = media.data.src;
|
|
48
|
+
}
|
|
49
|
+
else if ((media === null || media === void 0 ? void 0 : media.type) === 'video') {
|
|
50
|
+
imageSrc = media.data.thumbnail;
|
|
51
|
+
}
|
|
52
|
+
const { failed, handleError } = (0, hooks_1.useImageStub)(imageSrc);
|
|
43
53
|
const videoRef = (0, react_1.useRef)(null);
|
|
44
54
|
const videoSrc = (media === null || media === void 0 ? void 0 : media.type) === 'video' ? media.data.src : null;
|
|
45
55
|
(0, react_1.useEffect)(() => {
|
|
@@ -54,7 +64,8 @@ function MediaView({ media, loading, videoProps }) {
|
|
|
54
64
|
}
|
|
55
65
|
return (react_1.default.createElement("div", { className: (0, classnames_1.default)(media_view_module_scss_1.default.root) },
|
|
56
66
|
(media === null || media === void 0 ? void 0 : media.type) === 'image' && (react_1.default.createElement(image_overlay_1.ImageOverlay, { className: media_view_module_scss_1.default.image },
|
|
57
|
-
react_1.default.createElement(
|
|
67
|
+
failed && react_1.default.createElement(image_broken_svg_1.default, null),
|
|
68
|
+
!failed && react_1.default.createElement("img", { src: media.data.src, alt: media.data.alt || '', onError: handleError }))),
|
|
58
69
|
(media === null || media === void 0 ? void 0 : media.type) === 'video' && (react_1.default.createElement("video", Object.assign({ autoPlay: false, controls: true, controlsList: 'nodownload' }, videoProps, { ref: videoRef }),
|
|
59
70
|
react_1.default.createElement("source", { src: media.data.src }))),
|
|
60
71
|
(media === null || media === void 0 ? void 0 : media.type) === '360' && react_1.default.createElement(all_round_view_1.AllRoundView, { photos: media.data.photos, controls: desktop })));
|
|
@@ -18,9 +18,11 @@ exports.ProductBrief = void 0;
|
|
|
18
18
|
const react_1 = __importDefault(require("react"));
|
|
19
19
|
const image_overlay_1 = require("../../../../desktop/components/gallery-modal/components/image-overlay");
|
|
20
20
|
const price_1 = require("@sima-land/ui-nucleons/price");
|
|
21
|
+
const breakpoint_1 = require("@sima-land/ui-nucleons/hooks/breakpoint");
|
|
22
|
+
const hooks_1 = require("../../../hooks");
|
|
21
23
|
const bind_1 = __importDefault(require("classnames/bind"));
|
|
24
|
+
const image_broken_svg_1 = __importDefault(require("../../../icons/image-broken.svg"));
|
|
22
25
|
const product_brief_module_scss_1 = __importDefault(require("./product-brief.module.scss"));
|
|
23
|
-
const breakpoint_1 = require("@sima-land/ui-nucleons/hooks/breakpoint");
|
|
24
26
|
const cx = bind_1.default.bind(product_brief_module_scss_1.default);
|
|
25
27
|
/**
|
|
26
28
|
* Блок товара-рекомендации.
|
|
@@ -30,6 +32,7 @@ const cx = bind_1.default.bind(product_brief_module_scss_1.default);
|
|
|
30
32
|
function ProductBrief(_a) {
|
|
31
33
|
var { size: sizeProp, imageSrc, title, price, currency, footer, className, href, onLinkClick, loading } = _a, restProps = __rest(_a, ["size", "imageSrc", "title", "price", "currency", "footer", "className", "href", "onLinkClick", "loading"]);
|
|
32
34
|
const desktop = (0, breakpoint_1.useBreakpoint)('xs+');
|
|
35
|
+
const { failed, handleError } = (0, hooks_1.useImageStub)(imageSrc);
|
|
33
36
|
const size = (sizeProp !== null && sizeProp !== void 0 ? sizeProp : desktop) ? 'l' : 's';
|
|
34
37
|
if (loading) {
|
|
35
38
|
return react_1.default.createElement(ProductBriefSkeleton, { size: size });
|
|
@@ -37,7 +40,8 @@ function ProductBrief(_a) {
|
|
|
37
40
|
return (react_1.default.createElement("div", Object.assign({ className: cx('root', `size-${size}`, className) }, restProps),
|
|
38
41
|
react_1.default.createElement("a", { className: product_brief_module_scss_1.default.image, href: href, onClick: onLinkClick },
|
|
39
42
|
react_1.default.createElement(image_overlay_1.ImageOverlay, { className: product_brief_module_scss_1.default.overlay },
|
|
40
|
-
react_1.default.createElement(
|
|
43
|
+
failed && react_1.default.createElement(image_broken_svg_1.default, null),
|
|
44
|
+
!failed && react_1.default.createElement("img", { src: imageSrc, onError: handleError }))),
|
|
41
45
|
react_1.default.createElement("div", { className: product_brief_module_scss_1.default.info },
|
|
42
46
|
react_1.default.createElement("a", { className: product_brief_module_scss_1.default.title, href: href, onClick: onLinkClick }, title),
|
|
43
47
|
price && react_1.default.createElement(price_1.Price, { className: product_brief_module_scss_1.default.price, value: price, currencyGrapheme: currency })),
|
|
@@ -29,11 +29,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
29
29
|
exports.Thumbnail = exports.ThumbnailContext = void 0;
|
|
30
30
|
const react_1 = __importStar(require("react"));
|
|
31
31
|
const image_overlay_1 = require("../../../../desktop/components/gallery-modal/components/image-overlay");
|
|
32
|
-
const
|
|
32
|
+
const Play_1 = __importDefault(require("@sima-land/ui-quarks/icons/24x24/Filled/Play"));
|
|
33
33
|
const _360_svg_1 = __importDefault(require("../../../../desktop/components/gallery-modal/icons/360.svg"));
|
|
34
|
+
const price_1 = require("@sima-land/ui-nucleons/price");
|
|
35
|
+
const hooks_1 = require("../../../hooks");
|
|
34
36
|
const bind_1 = __importDefault(require("classnames/bind"));
|
|
37
|
+
const image_broken_svg_1 = __importDefault(require("../../../icons/image-broken.svg"));
|
|
35
38
|
const thumbnail_module_scss_1 = __importDefault(require("./thumbnail.module.scss"));
|
|
36
|
-
const price_1 = require("@sima-land/ui-nucleons/price");
|
|
37
39
|
exports.ThumbnailContext = (0, react_1.createContext)({});
|
|
38
40
|
const cx = bind_1.default.bind(thumbnail_module_scss_1.default);
|
|
39
41
|
/**
|
|
@@ -44,6 +46,7 @@ const cx = bind_1.default.bind(thumbnail_module_scss_1.default);
|
|
|
44
46
|
function Thumbnail({ size: sizeFromProps, type, src, alt, checked, onClick, className, loading, title, price, currency, }) {
|
|
45
47
|
var _a;
|
|
46
48
|
const { size: sizeFromContext } = (0, react_1.useContext)(exports.ThumbnailContext);
|
|
49
|
+
const { failed, handleError } = (0, hooks_1.useImageStub)(src);
|
|
47
50
|
const size = (_a = sizeFromProps !== null && sizeFromProps !== void 0 ? sizeFromProps : sizeFromContext) !== null && _a !== void 0 ? _a : 'l';
|
|
48
51
|
const rootClassName = cx('root', `size-${size}`, (type === null || type === void 0 ? void 0 : type.startsWith('icon-')) && 'iconic', checked && 'checked', loading && 'loading', className);
|
|
49
52
|
if (loading) {
|
|
@@ -54,10 +57,11 @@ function Thumbnail({ size: sizeFromProps, type, src, alt, checked, onClick, clas
|
|
|
54
57
|
return (react_1.default.createElement("button", { className: rootClassName, onClick: onClick, "data-testid": 'thumbnail' },
|
|
55
58
|
react_1.default.createElement("span", { className: cx('content') },
|
|
56
59
|
(type === null || type === void 0 ? void 0 : type.startsWith('preview-')) && (react_1.default.createElement(image_overlay_1.ImageOverlay, { className: cx('image') },
|
|
57
|
-
react_1.default.createElement(
|
|
58
|
-
|
|
60
|
+
failed && react_1.default.createElement(image_broken_svg_1.default, null),
|
|
61
|
+
!failed && react_1.default.createElement("img", { src: src, alt: alt, onError: handleError }))),
|
|
62
|
+
!failed && type === 'preview-video' && react_1.default.createElement("span", { className: cx('play') }),
|
|
59
63
|
type === 'icon-360' && react_1.default.createElement(_360_svg_1.default, { fill: 'currentColor' }),
|
|
60
|
-
type === 'icon-video' && react_1.default.createElement(
|
|
64
|
+
type === 'icon-video' && react_1.default.createElement(Play_1.default, { width: 32, height: 32, fill: 'currentColor' })),
|
|
61
65
|
(title || price) && (react_1.default.createElement("span", { className: cx('info') },
|
|
62
66
|
title && react_1.default.createElement("span", { className: cx('title') }, title),
|
|
63
67
|
price && react_1.default.createElement(price_1.Price, { className: cx('price'), value: price, currencyGrapheme: currency })))));
|
|
@@ -1,48 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
type: 'text';
|
|
4
|
-
text: string;
|
|
5
|
-
}
|
|
6
|
-
interface ColorContent {
|
|
7
|
-
type: 'color';
|
|
8
|
-
color: string;
|
|
9
|
-
}
|
|
10
|
-
interface ImageContent {
|
|
11
|
-
type: 'image';
|
|
12
|
-
src: string;
|
|
13
|
-
}
|
|
14
|
-
export type ModifierContent = TextContent | ColorContent | ImageContent;
|
|
15
|
-
export interface ModifierProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
16
|
-
/** Вывести выбранным. */
|
|
17
|
-
active?: boolean;
|
|
18
|
-
/** Вывести отключенным. */
|
|
19
|
-
disabled?: boolean;
|
|
20
|
-
/** Вывести зачеркнутым. */
|
|
21
|
-
crossedOut?: boolean;
|
|
22
|
-
/** Содержимое. */
|
|
23
|
-
content: ModifierContent;
|
|
24
|
-
/** Высота по дизайн-гайдам. */
|
|
25
|
-
size?: 's' | 'm';
|
|
26
|
-
/** Кол-во. */
|
|
27
|
-
count?: number;
|
|
28
|
-
/** Выводить ли ярлык уценки. */
|
|
29
|
-
markdown?: boolean;
|
|
30
|
-
/** Идентификатор для систем автоматизированного тестирования. */
|
|
31
|
-
'data-testid'?: string;
|
|
32
|
-
}
|
|
33
|
-
export type MoreButtonProps = Omit<ModifierProps, 'content' | 'crossedOut' | 'active' | 'count'> & {
|
|
34
|
-
count: number;
|
|
35
|
-
};
|
|
36
|
-
/**
|
|
37
|
-
* Кнопка модификатора (аналога товара).
|
|
38
|
-
* @param props Свойства.
|
|
39
|
-
* @return Элемент.
|
|
40
|
-
*/
|
|
41
|
-
export declare function Modifier({ size, active, crossedOut, disabled, content, count, className, markdown, 'data-testid': testId, ...props }: ModifierProps): JSX.Element;
|
|
42
|
-
/**
|
|
43
|
-
* Кнопка "+N" для показа полного списка модификаторов.
|
|
44
|
-
* @param props Свойства.
|
|
45
|
-
* @return Элемент.
|
|
46
|
-
*/
|
|
47
|
-
export declare function MoreButton({ count, className, 'data-testid': testId, ...rest }: MoreButtonProps): JSX.Element;
|
|
48
|
-
export {};
|
|
1
|
+
export type { TextContent, ImageContent, ColorContent, ModifierContent, ModifierProps, MoreButtonProps, } from './types';
|
|
2
|
+
export { Modifier, MoreButton } from './modifier';
|
|
@@ -1,103 +1,6 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
3
|
exports.MoreButton = exports.Modifier = void 0;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const modifiers_module_scss_1 = __importDefault(require("./modifiers.module.scss"));
|
|
45
|
-
const cx = bind_1.default.bind(modifiers_module_scss_1.default);
|
|
46
|
-
/**
|
|
47
|
-
* Кнопка модификатора (аналога товара).
|
|
48
|
-
* @param props Свойства.
|
|
49
|
-
* @return Элемент.
|
|
50
|
-
*/
|
|
51
|
-
function Modifier(_a) {
|
|
52
|
-
var { size = 'm', active, crossedOut, disabled, content, count, className, markdown, 'data-testid': testId = 'modifier' } = _a, props = __rest(_a, ["size", "active", "crossedOut", "disabled", "content", "count", "className", "markdown", 'data-testid']);
|
|
53
|
-
// текст хинта
|
|
54
|
-
const [hint, setHint] = (0, react_1.useState)('');
|
|
55
|
-
// состояние хинта
|
|
56
|
-
const [open, setOpen] = (0, react_1.useState)(false);
|
|
57
|
-
// позиционирование хинта
|
|
58
|
-
const _b = (0, hint_1.useHintFloating)({ open, onOpenChange: setOpen }), { refs } = _b, floating = __rest(_b, ["refs"]);
|
|
59
|
-
// пользовательское взаимодействие для хинта
|
|
60
|
-
const { getReferenceProps, getFloatingProps } = (0, hint_1.useHintOnHover)(floating);
|
|
61
|
-
// определение необходимости активации хинта
|
|
62
|
-
(0, react_1.useEffect)(() => {
|
|
63
|
-
const textElement = refs.reference.current;
|
|
64
|
-
if (!(textElement instanceof HTMLElement) || content.type !== 'text') {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
// eslint-disable-next-line require-jsdoc
|
|
68
|
-
const defineHint = () => setHint(isTruncated(textElement) ? content.text : '');
|
|
69
|
-
defineHint();
|
|
70
|
-
const observer = new ResizeObserver(defineHint);
|
|
71
|
-
observer.observe(textElement);
|
|
72
|
-
return () => observer.disconnect();
|
|
73
|
-
}, [content.text]);
|
|
74
|
-
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
75
|
-
react_1.default.createElement("a", Object.assign({}, props, { className: cx('root', `size-${size}`, { active, disabled }, className), "data-testid": testId }),
|
|
76
|
-
content.type === 'color' && (react_1.default.createElement("span", { className: cx('color'), role: 'banner', style: { background: content.color } })),
|
|
77
|
-
content.type === 'image' && (react_1.default.createElement("span", { className: cx('image'), role: 'banner', style: { backgroundImage: `url(${content.src})` } })),
|
|
78
|
-
content.type === 'text' && (react_1.default.createElement("span", Object.assign({ ref: refs.setReference, className: cx('text') }, getReferenceProps()), content.text)),
|
|
79
|
-
typeof count === 'number' && (react_1.default.createElement("span", { className: cx('counter') }, count > 99 ? '99+' : count)),
|
|
80
|
-
crossedOut && (react_1.default.createElement("svg", { width: '100%', height: '100%', className: cx('diagonal') },
|
|
81
|
-
react_1.default.createElement("line", { x1: '0', y1: '100%', x2: '100%', y2: '0', stroke: 'var(--modifier-border-color)' }))),
|
|
82
|
-
markdown && react_1.default.createElement("span", { className: modifiers_module_scss_1.default.corner }, "\u0423")),
|
|
83
|
-
hint && (react_1.default.createElement(hint_1.Hint, Object.assign({ open: open, hintRef: refs.setFloating, arrowRef: refs.setArrow }, getFloatingProps()), hint))));
|
|
84
|
-
}
|
|
85
|
-
exports.Modifier = Modifier;
|
|
86
|
-
/**
|
|
87
|
-
* Кнопка "+N" для показа полного списка модификаторов.
|
|
88
|
-
* @param props Свойства.
|
|
89
|
-
* @return Элемент.
|
|
90
|
-
*/
|
|
91
|
-
function MoreButton(_a) {
|
|
92
|
-
var { count, className, 'data-testid': testId = 'more-modifiers' } = _a, rest = __rest(_a, ["count", "className", 'data-testid']);
|
|
93
|
-
return (react_1.default.createElement(Modifier, Object.assign({ role: 'button', "aria-label": '\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0432\u0441\u0435 \u0430\u043D\u0430\u043B\u043E\u0433\u0438' }, rest, { className: cx('more', className), content: { type: 'text', text: `+${count}` }, "data-testid": testId })));
|
|
94
|
-
}
|
|
95
|
-
exports.MoreButton = MoreButton;
|
|
96
|
-
/**
|
|
97
|
-
* Определяет, обрезан ли текст в элементе.
|
|
98
|
-
* @param el Элемент.
|
|
99
|
-
* @return Обрезан ли текст в элементе.
|
|
100
|
-
*/
|
|
101
|
-
function isTruncated(el) {
|
|
102
|
-
return el.scrollWidth > el.clientWidth;
|
|
103
|
-
}
|
|
4
|
+
var modifier_1 = require("./modifier");
|
|
5
|
+
Object.defineProperty(exports, "Modifier", { enumerable: true, get: function () { return modifier_1.Modifier; } });
|
|
6
|
+
Object.defineProperty(exports, "MoreButton", { enumerable: true, get: function () { return modifier_1.MoreButton; } });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { ModifierProps, MoreButtonProps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Кнопка модификатора (аналога товара).
|
|
5
|
+
* @param props Свойства.
|
|
6
|
+
* @return Элемент.
|
|
7
|
+
*/
|
|
8
|
+
export declare function Modifier({ size, active, crossedOut, disabled, content, count, className, markdown, 'data-testid': testId, ...props }: ModifierProps): JSX.Element;
|
|
9
|
+
/**
|
|
10
|
+
* Кнопка "+N" для показа полного списка модификаторов.
|
|
11
|
+
* @param props Свойства.
|
|
12
|
+
* @return Элемент.
|
|
13
|
+
*/
|
|
14
|
+
export declare function MoreButton({ count, className, 'data-testid': testId, ...rest }: MoreButtonProps): JSX.Element;
|
|
@@ -0,0 +1,117 @@
|
|
|
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.MoreButton = exports.Modifier = void 0;
|
|
41
|
+
const react_1 = __importStar(require("react"));
|
|
42
|
+
const hint_1 = require("@sima-land/ui-nucleons/hint");
|
|
43
|
+
const hooks_1 = require("../../hooks");
|
|
44
|
+
const ImageBroken_1 = __importDefault(require("@sima-land/ui-quarks/icons/40x40/Stroked/ImageBroken"));
|
|
45
|
+
const bind_1 = __importDefault(require("classnames/bind"));
|
|
46
|
+
const modifier_module_scss_1 = __importDefault(require("./modifier.module.scss"));
|
|
47
|
+
const cx = bind_1.default.bind(modifier_module_scss_1.default);
|
|
48
|
+
/**
|
|
49
|
+
* Кнопка модификатора (аналога товара).
|
|
50
|
+
* @param props Свойства.
|
|
51
|
+
* @return Элемент.
|
|
52
|
+
*/
|
|
53
|
+
function Modifier(_a) {
|
|
54
|
+
var { size = 'm', active, crossedOut, disabled, content, count, className, markdown, 'data-testid': testId = 'modifier' } = _a, props = __rest(_a, ["size", "active", "crossedOut", "disabled", "content", "count", "className", "markdown", 'data-testid']);
|
|
55
|
+
// текст хинта
|
|
56
|
+
const [hint, setHint] = (0, react_1.useState)('');
|
|
57
|
+
// состояние хинта
|
|
58
|
+
const [open, setOpen] = (0, react_1.useState)(false);
|
|
59
|
+
// позиционирование хинта
|
|
60
|
+
const _b = (0, hint_1.useHintFloating)({ open, onOpenChange: setOpen }), { refs } = _b, floating = __rest(_b, ["refs"]);
|
|
61
|
+
// пользовательское взаимодействие для хинта
|
|
62
|
+
const { getReferenceProps, getFloatingProps } = (0, hint_1.useHintOnHover)(floating);
|
|
63
|
+
// определение необходимости активации хинта
|
|
64
|
+
(0, react_1.useEffect)(() => {
|
|
65
|
+
const textElement = refs.reference.current;
|
|
66
|
+
if (!(textElement instanceof HTMLElement) || content.type !== 'text') {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// eslint-disable-next-line require-jsdoc
|
|
70
|
+
const defineHint = () => setHint(isTruncated(textElement) ? content.text : '');
|
|
71
|
+
defineHint();
|
|
72
|
+
const observer = new ResizeObserver(defineHint);
|
|
73
|
+
observer.observe(textElement);
|
|
74
|
+
return () => observer.disconnect();
|
|
75
|
+
}, [content.text]);
|
|
76
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
77
|
+
react_1.default.createElement("a", Object.assign({}, props, { className: cx('root', `size-${size}`, { active, disabled }, className), "data-testid": testId }),
|
|
78
|
+
content.type === 'color' && (react_1.default.createElement("span", { className: cx('color'), role: 'banner', style: { background: content.color } })),
|
|
79
|
+
content.type === 'image' && react_1.default.createElement(Image, { src: content.src }),
|
|
80
|
+
content.type === 'text' && (react_1.default.createElement("span", Object.assign({ ref: refs.setReference, className: cx('text') }, getReferenceProps()), content.text)),
|
|
81
|
+
typeof count === 'number' && (react_1.default.createElement("span", { className: cx('counter') }, count > 99 ? '99+' : count)),
|
|
82
|
+
crossedOut && (react_1.default.createElement("svg", { width: '100%', height: '100%', className: cx('diagonal') },
|
|
83
|
+
react_1.default.createElement("line", { x1: '0', y1: '100%', x2: '100%', y2: '0', stroke: 'var(--modifier-border-color)' }))),
|
|
84
|
+
markdown && react_1.default.createElement("span", { className: modifier_module_scss_1.default.corner }, "\u0423")),
|
|
85
|
+
hint && (react_1.default.createElement(hint_1.Hint, Object.assign({ open: open, hintRef: refs.setFloating, arrowRef: refs.setArrow }, getFloatingProps()), hint))));
|
|
86
|
+
}
|
|
87
|
+
exports.Modifier = Modifier;
|
|
88
|
+
/**
|
|
89
|
+
* Картинка с заглушкой в случае ошибки загрузки.
|
|
90
|
+
* @param props Свойства.
|
|
91
|
+
* @return Элемент.
|
|
92
|
+
*/
|
|
93
|
+
function Image(_a) {
|
|
94
|
+
var { src, onError } = _a, rest = __rest(_a, ["src", "onError"]);
|
|
95
|
+
const { failed, handleError } = (0, hooks_1.useImageStub)(src, onError);
|
|
96
|
+
return (react_1.default.createElement("span", { className: cx('image-wrapper', { fail: failed }) },
|
|
97
|
+
failed && react_1.default.createElement(ImageBroken_1.default, null),
|
|
98
|
+
react_1.default.createElement("img", Object.assign({ className: cx('image') }, rest, { src: src, onError: handleError }))));
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Кнопка "+N" для показа полного списка модификаторов.
|
|
102
|
+
* @param props Свойства.
|
|
103
|
+
* @return Элемент.
|
|
104
|
+
*/
|
|
105
|
+
function MoreButton(_a) {
|
|
106
|
+
var { count, className, 'data-testid': testId = 'more-modifiers' } = _a, rest = __rest(_a, ["count", "className", 'data-testid']);
|
|
107
|
+
return (react_1.default.createElement(Modifier, Object.assign({ role: 'button', "aria-label": '\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0432\u0441\u0435 \u0430\u043D\u0430\u043B\u043E\u0433\u0438' }, rest, { className: cx('more', className), content: { type: 'text', text: `+${count}` }, "data-testid": testId })));
|
|
108
|
+
}
|
|
109
|
+
exports.MoreButton = MoreButton;
|
|
110
|
+
/**
|
|
111
|
+
* Определяет, обрезан ли текст в элементе.
|
|
112
|
+
* @param el Элемент.
|
|
113
|
+
* @return Обрезан ли текст в элементе.
|
|
114
|
+
*/
|
|
115
|
+
function isTruncated(el) {
|
|
116
|
+
return el.scrollWidth > el.clientWidth;
|
|
117
|
+
}
|
|
@@ -97,6 +97,27 @@
|
|
|
97
97
|
background-size: cover;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
.image-wrapper {
|
|
101
|
+
border-radius: inherit;
|
|
102
|
+
display: flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
justify-content: center;
|
|
105
|
+
position: absolute;
|
|
106
|
+
top: 0;
|
|
107
|
+
left: 0;
|
|
108
|
+
width: 100%;
|
|
109
|
+
height: 100%;
|
|
110
|
+
background: colors.$basic-gray4;
|
|
111
|
+
&.fail {
|
|
112
|
+
--modifier-media-opacity: 0;
|
|
113
|
+
}
|
|
114
|
+
> svg {
|
|
115
|
+
width: calc(48%);
|
|
116
|
+
height: calc(48%);
|
|
117
|
+
fill: colors.$basic-gray24;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
100
121
|
.text {
|
|
101
122
|
padding: var(--modifier-padding);
|
|
102
123
|
display: block;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { AnchorHTMLAttributes } from 'react';
|
|
2
|
+
export interface TextContent {
|
|
3
|
+
type: 'text';
|
|
4
|
+
text: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ColorContent {
|
|
7
|
+
type: 'color';
|
|
8
|
+
color: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ImageContent {
|
|
11
|
+
type: 'image';
|
|
12
|
+
src: string;
|
|
13
|
+
}
|
|
14
|
+
export type ModifierContent = TextContent | ColorContent | ImageContent;
|
|
15
|
+
export interface ModifierProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
16
|
+
/** Вывести выбранным. */
|
|
17
|
+
active?: boolean;
|
|
18
|
+
/** Вывести отключенным. */
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
/** Вывести зачеркнутым. */
|
|
21
|
+
crossedOut?: boolean;
|
|
22
|
+
/** Содержимое. */
|
|
23
|
+
content: ModifierContent;
|
|
24
|
+
/** Высота по дизайн-гайдам. */
|
|
25
|
+
size?: 's' | 'm';
|
|
26
|
+
/** Кол-во. */
|
|
27
|
+
count?: number;
|
|
28
|
+
/** Выводить ли ярлык уценки. */
|
|
29
|
+
markdown?: boolean;
|
|
30
|
+
/** Идентификатор для систем автоматизированного тестирования. */
|
|
31
|
+
'data-testid'?: string;
|
|
32
|
+
}
|
|
33
|
+
export type MoreButtonProps = Omit<ModifierProps, 'content' | 'crossedOut' | 'active' | 'count'> & {
|
|
34
|
+
count: number;
|
|
35
|
+
};
|