@sima-land/moleculas 12.3.0 → 13.0.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.
Files changed (83) hide show
  1. package/common/components/adult-block/{adult-block.d.ts → index.d.ts} +1 -2
  2. package/common/components/adult-block/{adult-block.js → index.js} +2 -1
  3. package/common/components/badge-list/index.d.ts +2 -6
  4. package/common/components/badge-list/index.js +26 -5
  5. package/common/components/product-info/index.d.ts +11 -49
  6. package/common/components/product-info/index.js +24 -35
  7. package/common/components/product-info/parts.d.ts +41 -0
  8. package/common/components/product-info/parts.js +176 -0
  9. package/common/components/product-info/product-info.module.scss +154 -31
  10. package/common/components/product-info/types.d.ts +5 -0
  11. package/common/components/product-info/types.js +2 -0
  12. package/common/components/product-info/utils.d.ts +3 -0
  13. package/{mobile/components/product-slider → common/components/product-info}/utils.js +2 -2
  14. package/common/components/quick-view-button/index.js +1 -1
  15. package/common/components/wish-button/index.js +1 -1
  16. package/common/icons/eighteen-plus.svg +3 -1
  17. package/desktop/components/breadcrumbs/index.js +5 -5
  18. package/desktop/components/gallery-modal/components/all-round-view.js +11 -11
  19. package/desktop/components/gallery-modal/components/gallery-modal.js +7 -7
  20. package/desktop/components/gallery-modal/components/image-overlay.js +1 -1
  21. package/desktop/components/gallery-modal/components/review-info.js +1 -1
  22. package/desktop/components/gallery-modal/utils.js +5 -5
  23. package/desktop/components/item-image/index.d.ts +1 -1
  24. package/desktop/components/item-image/index.js +8 -6
  25. package/desktop/components/modifiers/button/index.js +2 -2
  26. package/desktop/components/modifiers/group/index.js +12 -12
  27. package/desktop/components/modifiers/widget/index.js +2 -2
  28. package/desktop/components/person-modal/index.d.ts +0 -1
  29. package/desktop/components/person-modal/index.js +1 -2
  30. package/desktop/components/product-card/index.d.ts +11 -24
  31. package/desktop/components/product-card/index.js +43 -31
  32. package/desktop/components/product-card/product-card.module.scss +7 -48
  33. package/desktop/components/product-carousel/hover-card.d.ts +1 -4
  34. package/desktop/components/product-carousel/hover-card.js +22 -10
  35. package/desktop/components/product-carousel/index.d.ts +5 -9
  36. package/desktop/components/product-carousel/index.js +29 -28
  37. package/desktop/components/product-carousel/utils.js +10 -10
  38. package/desktop/components/promotion-card/banner-title.js +5 -5
  39. package/desktop/components/promotion-card/estimate.js +5 -3
  40. package/desktop/components/promotion-card/index.d.ts +3 -1
  41. package/desktop/components/promotion-card/index.js +6 -6
  42. package/desktop/components/promotion-card/placeholder.js +4 -4
  43. package/mobile/components/garland-calculator-button/garland-calculator-button.module.scss +32 -3
  44. package/mobile/components/garland-calculator-button/index.d.ts +2 -3
  45. package/mobile/components/garland-calculator-button/index.js +9 -14
  46. package/mobile/components/item-image/index.d.ts +4 -4
  47. package/mobile/components/item-image/index.js +10 -11
  48. package/mobile/components/modifiers-list/modifier-item/index.js +7 -7
  49. package/mobile/components/person-screen/index.d.ts +3 -3
  50. package/mobile/components/person-screen/index.js +16 -14
  51. package/mobile/components/product-actions-alert/index.d.ts +2 -5
  52. package/mobile/components/product-actions-alert/index.js +2 -5
  53. package/mobile/components/product-slider/index.d.ts +6 -9
  54. package/mobile/components/product-slider/index.js +17 -14
  55. package/mobile/components/product-slider/{item.module.scss → product-slider.module.scss} +1 -12
  56. package/mobile/components/pseudo-input/index.d.ts +5 -8
  57. package/mobile/components/pseudo-input/index.js +21 -10
  58. package/mobile/components/pseudo-input/pseudo-input.module.scss +6 -9
  59. package/mobile/components/search-bar/index.js +25 -25
  60. package/mobile/components/select-screen/index.d.ts +2 -2
  61. package/mobile/components/select-screen/index.js +7 -18
  62. package/package.json +30 -30
  63. package/README.md +0 -86
  64. package/common/__fixtures__/list-item.d.ts +0 -53
  65. package/common/__fixtures__/list-item.js +0 -107
  66. package/common/helpers/get-contained-items.d.ts +0 -7
  67. package/common/helpers/get-contained-items.js +0 -18
  68. package/common/icons/filled-rate.svg +0 -3
  69. package/common/icons/full-left-arrow.svg +0 -1
  70. package/common/icons/full-right-arrow.svg +0 -1
  71. package/common/icons/grid-small.svg +0 -11
  72. package/common/icons/grid.svg +0 -6
  73. package/common/icons/list.svg +0 -5
  74. package/common/icons/page.svg +0 -1
  75. package/common/icons/step-arrows.svg +0 -4
  76. package/common/icons/triple-dots.svg +0 -3
  77. package/common/icons/white-spinner.svg +0 -3
  78. package/desktop/components/breadcrumbs/items.d.ts +0 -3
  79. package/desktop/components/breadcrumbs/items.js +0 -157
  80. package/mobile/components/garland-calculator-button/tree-in-circle.svg +0 -6
  81. package/mobile/components/product-slider/item.d.ts +0 -18
  82. package/mobile/components/product-slider/item.js +0 -58
  83. package/mobile/components/product-slider/utils.d.ts +0 -4
@@ -1,29 +1,16 @@
1
1
  import React from 'react';
2
- import { ProductInfoProps } from '../../../common/components/product-info';
3
- export interface ProductCardProps extends ProductInfoProps, React.HTMLAttributes<HTMLDivElement> {
4
- /** Будет вызвана при уходе курсора с элемента. */
5
- onMouseLeave?: () => void;
6
- /** Будет вызвана при нажатии на кнопку-иконку быстрого просмотра. */
7
- onQuickViewClick?: () => void;
2
+ import { ProductInfo, ProductInfoProps } from '../../../common/components/product-info';
3
+ import { PlateProps } from '@sima-land/ui-nucleons/plate';
4
+ export declare type ProductCardChildren = React.ReactElement<ProductInfoProps, typeof ProductInfo>;
5
+ export interface ProductCardProps extends React.HTMLAttributes<HTMLDivElement> {
6
+ children: ProductCardChildren;
8
7
  }
8
+ export declare const HoverCard: React.ForwardRefExoticComponent<PlateProps & React.RefAttributes<HTMLDivElement | null>>;
9
+ export declare const ProductCard: ({ children, className, ...props }: ProductCardProps) => JSX.Element;
9
10
  /**
10
- * Компонент-слот для вывода контрола товара в корзине.
11
- * @param props Свойства.
11
+ * Фильтрует содержимое карточки для вывода состояния без наведения.
12
+ * @param el Элемент.
12
13
  * @return Элемент.
13
14
  */
14
- declare const CartControlSlot: ({ children, stepText, markupText, loading, }: {
15
- children?: React.ReactNode;
16
- stepText?: string | undefined;
17
- markupText?: string | undefined;
18
- loading?: boolean | undefined;
19
- }) => JSX.Element;
20
- export interface ProductCardComponent extends React.ForwardRefExoticComponent<React.PropsWithoutRef<ProductCardProps> & React.RefAttributes<HTMLDivElement>> {
21
- CartControl: typeof CartControlSlot;
22
- }
23
- /**
24
- * Карточка товара, появляющаяся над элементом карусели.
25
- * @param props Свойства компонента.
26
- * @return Элемент.
27
- */
28
- export declare const ProductCard: ProductCardComponent;
29
- export {};
15
+ export declare const reduceBaseInfo: (el: ProductCardChildren) => React.FunctionComponentElement<ProductInfoProps>;
16
+ export declare const reduceHoverInfo: (el: ProductCardChildren) => React.FunctionComponentElement<ProductInfoProps>;
@@ -33,41 +33,53 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
33
33
  return (mod && mod.__esModule) ? mod : { "default": mod };
34
34
  };
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.ProductCard = void 0;
36
+ exports.reduceHoverInfo = exports.reduceBaseInfo = exports.ProductCard = exports.HoverCard = void 0;
37
37
  const react_1 = __importStar(require("react"));
38
38
  const product_info_1 = require("../../../common/components/product-info");
39
- const with_hint_1 = require("@sima-land/ui-nucleons/with-hint");
40
- const quick_view_button_1 = require("../../../common/components/quick-view-button");
41
- const shapes_1 = require("@sima-land/ui-nucleons/styling/shapes");
42
- const shadows_1 = require("@sima-land/ui-nucleons/styling/shadows");
43
- const define_slots_1 = require("@sima-land/ui-nucleons/helpers/define-slots");
44
- const with_prevent_1 = __importDefault(require("@sima-land/ui-nucleons/helpers/with-prevent"));
45
- const bind_1 = __importDefault(require("classnames/bind"));
39
+ const plate_1 = require("@sima-land/ui-nucleons/plate");
40
+ const classnames_1 = __importDefault(require("classnames"));
46
41
  const product_card_module_scss_1 = __importDefault(require("./product-card.module.scss"));
47
- const cx = bind_1.default.bind(product_card_module_scss_1.default);
48
- /**
49
- * Компонент-слот для вывода контрола товара в корзине.
50
- * @param props Свойства.
51
- * @return Элемент.
52
- */
53
- const CartControlSlot = ({ children, stepText, markupText, loading, }) => (react_1.default.createElement("div", { className: cx('control-wrapper', { loading }) },
54
- react_1.default.createElement("div", { className: cx('control') }, !loading && children),
55
- (loading || stepText) && react_1.default.createElement("div", { className: cx('unit-text') }, !loading && stepText),
56
- !loading && markupText && react_1.default.createElement("div", { className: cx('bottom-text') }, markupText)));
42
+ exports.HoverCard = (0, react_1.forwardRef)((props, ref) => (react_1.default.createElement(plate_1.Plate, Object.assign({}, props, { ref: ref, rounds: 'm', shadow: 'z4', className: (0, classnames_1.default)(product_card_module_scss_1.default.card, props.className), "data-testid": 'product-card:hover-card' }))));
43
+ const ProductCard = (_a) => {
44
+ var { children, className } = _a, props = __rest(_a, ["children", "className"]);
45
+ const [hovered, toggle] = (0, react_1.useState)(false);
46
+ return (react_1.default.createElement("div", Object.assign({}, props, { "data-testid": 'product-card', className: (0, classnames_1.default)(product_card_module_scss_1.default.root, className) }),
47
+ react_1.default.createElement("div", { "data-testid": 'product-card:info', onMouseEnter: () => toggle(true) }, (0, exports.reduceBaseInfo)(children)),
48
+ hovered && (react_1.default.createElement(exports.HoverCard, { onMouseLeave: () => toggle(false) }, (0, exports.reduceHoverInfo)(children)))));
49
+ };
50
+ exports.ProductCard = ProductCard;
57
51
  /**
58
- * Карточка товара, появляющаяся над элементом карусели.
59
- * @param props Свойства компонента.
52
+ * Фильтрует содержимое карточки для вывода состояния без наведения.
53
+ * @param el Элемент.
60
54
  * @return Элемент.
61
55
  */
62
- exports.ProductCard = react_1.forwardRef((_a, rootRef) => {
63
- var { data, onLinkClick, onQuickViewClick, children,
64
- // div props
65
- className } = _a, rootProps = __rest(_a, ["data", "onLinkClick", "onQuickViewClick", "children", "className"]);
66
- const { cartControl } = define_slots_1.defineSlots(children, { cartControl: CartControlSlot });
67
- return (react_1.default.createElement("div", Object.assign({ ref: rootRef, className: cx('root', shapes_1.SmallRounds.all, shadows_1.BoxShadow.z4, className) }, rootProps),
68
- react_1.default.createElement(product_info_1.ProductInfo, { data: data, onLinkClick: onLinkClick },
69
- react_1.default.createElement(product_info_1.ProductInfo.OnImage, null,
70
- react_1.default.createElement(with_hint_1.WithHint, { hint: '\u0411\u044B\u0441\u0442\u0440\u044B\u0439 \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440', direction: 'left' }, (ref, toggle) => (react_1.default.createElement(quick_view_button_1.QuickViewButton, { ref: ref, onMouseEnter: () => toggle(true), onMouseLeave: () => toggle(false), className: cx('quick-view-button'), onClick: with_prevent_1.default(onQuickViewClick) }))))),
71
- cartControl));
56
+ const reduceBaseInfo = (el) => (0, react_1.cloneElement)(el, {
57
+ children: react_1.Children.map(el.props.children, child => {
58
+ let result;
59
+ if ((0, react_1.isValidElement)(child)) {
60
+ switch (child.type) {
61
+ // игнорируем кнопки-иконки на изображении
62
+ case product_info_1.Parts.Image:
63
+ result = (0, react_1.cloneElement)(child, { children: undefined });
64
+ break;
65
+ // игнорируем футер
66
+ case product_info_1.Parts.Footer:
67
+ result = null;
68
+ break;
69
+ // остальное оставляем без изменений
70
+ default:
71
+ result = child;
72
+ break;
73
+ }
74
+ }
75
+ return result;
76
+ }),
77
+ });
78
+ exports.reduceBaseInfo = reduceBaseInfo;
79
+ const reduceHoverInfo = (el) => (0, react_1.cloneElement)(el, {
80
+ // при наведении у изображения строго непрозрачность = 1
81
+ children: react_1.Children.map(el.props.children, child => (0, react_1.isValidElement)(child) && child.type === product_info_1.Parts.Image
82
+ ? (0, react_1.cloneElement)(child, { opacity: 1 })
83
+ : child),
72
84
  });
73
- exports.ProductCard.CartControl = CartControlSlot;
85
+ exports.reduceHoverInfo = reduceHoverInfo;
@@ -1,53 +1,12 @@
1
- @use 'node_modules/@sima-land/ui-nucleons/colors';
2
-
3
1
  .root {
4
- padding: 16px 16px 24px;
5
- background: #fff;
2
+ position: relative;
3
+ max-width: 400px;
6
4
  }
7
5
 
8
- .quick-view-button {
6
+ .card {
9
7
  position: absolute;
10
- top: 8px;
11
- right: 8px;
12
- cursor: pointer;
13
- }
14
-
15
- .control-wrapper {
16
- display: flex;
17
- flex-wrap: wrap;
18
- margin-top: 16px;
19
- &.loading {
20
- .control {
21
- background: colors.$gray4;
22
- border-radius: 4px;
23
- }
24
- .unit-text::before {
25
- content: '';
26
- display: block;
27
- background: colors.$gray4;
28
- border-radius: 4px;
29
- width: 48px;
30
- height: 16px;
31
- }
32
- }
33
- }
34
-
35
- .control {
36
- width: 122px;
37
- height: 40px;
38
- margin-right: 16px;
39
- }
40
-
41
- .unit-text {
42
- padding-top: 12px; // чтобы при переносе на следующую строку правильно отступил от кнопки
43
- font-size: 12px;
44
- line-height: 16px;
45
- color: colors.$gray38;
46
- }
47
-
48
- .bottom-text {
49
- margin-top: 12px;
50
- font-size: 12px;
51
- line-height: 16px;
52
- color: colors.$gray38;
8
+ top: -16px;
9
+ left: -16px;
10
+ padding: 16px 16px 24px;
11
+ width: calc(100% + 32px);
53
12
  }
@@ -1,15 +1,12 @@
1
1
  import React from 'react';
2
2
  import { ProductInfoProps } from '../../../common/components/product-info';
3
- import { ProductCard } from '../product-card';
4
3
  export interface HoverCardProps extends ProductInfoProps {
5
4
  targetRef: React.RefObject<HTMLElement | null>;
6
5
  onMouseLeave?: () => void;
7
- onQuickViewClick?: () => void;
8
- renderCartControl?: (Slot: typeof ProductCard.CartControl) => React.ReactNode;
9
6
  }
10
7
  /**
11
8
  * Карточка товара, появляющаяся над элементом карусели.
12
9
  * @param props Свойства компонента.
13
10
  * @return Элемент.
14
11
  */
15
- export declare const HoverCard: ({ data, targetRef, onMouseLeave, onQuickViewClick, onLinkClick, renderCartControl, }: HoverCardProps) => JSX.Element;
12
+ export declare const HoverCard: ({ targetRef, onMouseLeave, ...productInfoProps }: HoverCardProps) => JSX.Element;
@@ -18,6 +18,17 @@ var __importStar = (this && this.__importStar) || function (mod) {
18
18
  __setModuleDefault(result, mod);
19
19
  return result;
20
20
  };
21
+ var __rest = (this && this.__rest) || function (s, e) {
22
+ var t = {};
23
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
24
+ t[p] = s[p];
25
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
26
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
27
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
28
+ t[p[i]] = s[p[i]];
29
+ }
30
+ return t;
31
+ };
21
32
  var __importDefault = (this && this.__importDefault) || function (mod) {
22
33
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
34
  };
@@ -25,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
25
36
  exports.HoverCard = void 0;
26
37
  const react_1 = __importStar(require("react"));
27
38
  const utils_1 = require("@sima-land/ui-nucleons/with-tooltip/utils");
39
+ const product_info_1 = require("../../../common/components/product-info");
28
40
  const bind_1 = __importDefault(require("classnames/bind"));
29
41
  const hover_card_module_scss_1 = __importDefault(require("./hover-card.module.scss"));
30
42
  const shapes_1 = require("@sima-land/ui-nucleons/styling/shapes");
@@ -36,25 +48,25 @@ const cx = bind_1.default.bind(hover_card_module_scss_1.default);
36
48
  * @param props Свойства компонента.
37
49
  * @return Элемент.
38
50
  */
39
- const HoverCard = ({ data, targetRef, onMouseLeave, onQuickViewClick, onLinkClick, renderCartControl, }) => {
40
- const rootRef = react_1.useRef(null);
41
- const [ready, setReady] = react_1.useState(false);
42
- react_1.useLayoutEffect(() => {
51
+ const HoverCard = (_a) => {
52
+ var { targetRef, onMouseLeave } = _a, productInfoProps = __rest(_a, ["targetRef", "onMouseLeave"]);
53
+ const rootRef = (0, react_1.useRef)(null);
54
+ const [ready, setReady] = (0, react_1.useState)(false);
55
+ (0, react_1.useLayoutEffect)(() => {
43
56
  if (targetRef.current && rootRef.current) {
44
57
  const element = rootRef.current;
45
58
  const rect = targetRef.current.getBoundingClientRect();
46
- const correction = utils_1.getOriginCorrection(rootRef.current);
59
+ const correction = (0, utils_1.getOriginCorrection)(rootRef.current);
47
60
  element.style.left = `${correction.x + rect.left - 16}px`;
48
61
  element.style.top = `${correction.y + rect.top - 16}px`;
49
62
  element.style.width = `${rect.width + 32}px`;
50
63
  setReady(true);
51
64
  }
52
- }, [data]);
53
- return (react_1.default.createElement(product_card_1.ProductCard, { data: data, onLinkClick: onLinkClick, onQuickViewClick: onQuickViewClick,
54
- // div props
55
- ref: rootRef, "data-testid": 'product-carousel:hover-card', className: cx('root', !ready && 'hidden', shapes_1.SmallRounds.all, shadows_1.BoxShadow.z4), onMouseLeave: () => {
65
+ });
66
+ return (react_1.default.createElement(product_card_1.HoverCard, { ref: rootRef, style: { padding: '16px 16px 24px 16px' }, className: cx('root', !ready && 'hidden', shapes_1.SmallRounds.all, shadows_1.BoxShadow.z4), onMouseLeave: () => {
56
67
  setReady(false);
57
68
  onMouseLeave && onMouseLeave();
58
- } }, renderCartControl === null || renderCartControl === void 0 ? void 0 : renderCartControl(product_card_1.ProductCard.CartControl)));
69
+ } },
70
+ react_1.default.createElement(product_info_1.ProductInfo, Object.assign({}, productInfoProps))));
59
71
  };
60
72
  exports.HoverCard = HoverCard;
@@ -1,6 +1,5 @@
1
1
  import React from 'react';
2
- import { HoverCardProps } from './hover-card';
3
- import { ProductInfoProps } from '../../../common/components/product-info';
2
+ import { ProductInfo, ProductInfoProps } from '../../../common/components/product-info';
4
3
  interface ItemSize {
5
4
  xs?: 2 | 3 | 4;
6
5
  s?: 2 | 3 | 4;
@@ -8,12 +7,13 @@ interface ItemSize {
8
7
  l?: 2 | 3 | 4;
9
8
  xl?: 2 | 3 | 4;
10
9
  }
10
+ export declare type ItemElement = React.ReactElement<ProductInfoProps, typeof ProductInfo>;
11
11
  export interface ProductCarouselProps {
12
12
  /** CSS-класс для корневого элемента. */
13
13
  className?: string;
14
14
  /** Элементы карусели. */
15
- children?: React.ReactNode;
16
- /** Найстройки размера элемента карусели. */
15
+ children?: ItemElement | ItemElement[];
16
+ /** Настройки размера элемента карусели. */
17
17
  itemSize?: ItemSize;
18
18
  /** Сработает при попадании карусели во viewport. */
19
19
  onInViewport?: () => void;
@@ -22,14 +22,10 @@ export interface ProductCarouselProps {
22
22
  /** Нужно ли показывать всплывающую карточку при наведении на элемент карусели. */
23
23
  withHoverCard?: boolean;
24
24
  }
25
- declare type CarouselItemProps = ProductInfoProps & Pick<HoverCardProps, 'renderCartControl' | 'onQuickViewClick'>;
26
25
  /**
27
26
  * Карусель рекомендованных товаров.
28
27
  * @param props Свойства.
29
28
  * @return Элемент.
30
29
  */
31
- export declare const ProductCarousel: {
32
- ({ className, itemSize, onInViewport, onNeedRequest, withHoverCard, children, }: ProductCarouselProps): JSX.Element;
33
- Item: (props: CarouselItemProps) => null;
34
- };
30
+ export declare const ProductCarousel: ({ className, itemSize, onInViewport, onNeedRequest, withHoverCard, children, }: ProductCarouselProps) => JSX.Element;
35
31
  export {};
@@ -39,38 +39,34 @@ const getSizeClasses = (size = {}) => [
39
39
  `size-l-${size.l || 2}`,
40
40
  `size-xl-${size.xl || 2}`,
41
41
  ];
42
- /**
43
- * Компонент для проброса данных элемента карусели в компонент карусели.
44
- * Экспериментальный вариант.
45
- * @return Null.
46
- */
47
- const Item = () => null;
48
42
  /**
49
43
  * Карусель рекомендованных товаров.
50
44
  * @param props Свойства.
51
45
  * @return Элемент.
52
46
  */
53
47
  const ProductCarousel = ({ className, itemSize, onInViewport, onNeedRequest, withHoverCard, children, }) => {
54
- const [activeItemIndex, setActiveItemIndex] = react_1.useState(null);
55
- const cardShow = utils_1.useAllowFlag();
56
- const needBigArrows = media_1.useMedia('(min-width: 1600px)');
57
- const sectionRef = react_1.useRef(null);
58
- const stubRef = react_1.useRef(null);
59
- const targetItemRef = react_1.useRef(null);
60
- const items = react_1.Children.toArray(children).reduce((acc, item) => {
61
- react_1.isValidElement(item) && item.type === Item && acc.push(item.props);
62
- return acc;
63
- }, []);
48
+ const [activeItemIndex, setActiveItemIndex] = (0, react_1.useState)(null);
49
+ const cardShow = (0, utils_1.useAllowFlag)();
50
+ const needBigArrows = (0, media_1.useMedia)('(min-width: 1600px)');
51
+ const rootRef = (0, react_1.useRef)(null);
52
+ const targetItemRef = (0, react_1.useRef)(null);
53
+ let items;
54
+ if (Array.isArray(children)) {
55
+ items = children;
56
+ }
57
+ else {
58
+ items = children ? [children] : [];
59
+ }
64
60
  // вычисляем ширину элемента карусели для позиционирования стрелок
65
- const itemWidth = utils_1.useChildWidth(sectionRef, `.${cx('item')}`, [items.length]);
61
+ const itemWidth = (0, utils_1.useChildWidth)(rootRef, `.${cx('item')}`, [items.length]);
66
62
  // инициируем загрузку данных, когда компонент почти попал в зону видимости
67
- utils_1.useViewport(stubRef, onNeedRequest, {
63
+ (0, utils_1.useViewport)(rootRef, onNeedRequest, {
68
64
  rootMargin: '200px 0px 200px 0px',
69
65
  });
70
66
  // отправляем статистку, когда компонент попадает в зону видимости
71
- utils_1.useViewport(sectionRef, onInViewport);
72
- return Array.isArray(items) && items.length > 0 ? (react_1.default.createElement("div", { ref: sectionRef, className: cx('root', className) },
73
- react_1.default.createElement(carousel_1.Carousel, Object.assign({ step: 3, draggable: false,
67
+ (0, utils_1.useViewport)(rootRef, onInViewport);
68
+ return (react_1.default.createElement("div", { ref: rootRef, className: cx('root', className) },
69
+ items.length > 0 && (react_1.default.createElement(carousel_1.Carousel, Object.assign({ step: 3, draggable: false,
74
70
  // докидываем индекс чтобы позже брать актуальные данные из списка по нему
75
71
  items: items.map((item, index) => [item, index]) }, (itemWidth !== null && {
76
72
  withControls: true,
@@ -86,16 +82,21 @@ const ProductCarousel = ({ className, itemSize, onInViewport, onNeedRequest, wit
86
82
  targetItemRef.current = e.currentTarget;
87
83
  setActiveItemIndex(index);
88
84
  }
89
- } },
90
- react_1.default.createElement(product_info_1.ProductInfo, { data: item.data, onLinkClick: item.onLinkClick }))),
85
+ } }, (0, react_1.cloneElement)(item, {
86
+ children: react_1.Children.toArray(item.props.children).reduce((list, child) => {
87
+ if ((0, react_1.isValidElement)(child)) {
88
+ child.type === product_info_1.Parts.Image &&
89
+ list.push(withHoverCard ? (0, react_1.cloneElement)(child, { children: undefined }) : child);
90
+ child.type !== product_info_1.Parts.Footer && list.push(child);
91
+ }
92
+ return list;
93
+ }, []),
94
+ }))),
91
95
  // длительность прокрутки в Carousel - 320, делаем слегка с запасом
92
96
  // @todo после восстановления проверить позицию курсора чтобы показать карточку (если будет критично)
93
- onChangeTargetIndex: () => cardShow.disallowFor(360) })),
97
+ onChangeTargetIndex: () => cardShow.disallowFor(360) }))),
94
98
  withHoverCard &&
95
99
  activeItemIndex !== null &&
96
- [items[activeItemIndex]].map(item => (react_1.default.createElement(hover_card_1.HoverCard, { key: activeItemIndex, targetRef: targetItemRef, onMouseLeave: () => setActiveItemIndex(null),
97
- // данные элемента карусели
98
- data: item.data, onLinkClick: item.onLinkClick, onQuickViewClick: item.onQuickViewClick, renderCartControl: item.renderCartControl }))))) : (react_1.default.createElement("div", { ref: stubRef }));
100
+ [items[activeItemIndex]].map(item => (react_1.default.createElement(hover_card_1.HoverCard, Object.assign({ key: activeItemIndex, targetRef: targetItemRef, onMouseLeave: () => setActiveItemIndex(null) }, item.props))))));
99
101
  };
100
102
  exports.ProductCarousel = ProductCarousel;
101
- exports.ProductCarousel.Item = Item;
@@ -13,7 +13,7 @@ const on_1 = __importDefault(require("@sima-land/ui-nucleons/helpers/on"));
13
13
  * @param options Опции IntersectionObserver.
14
14
  */
15
15
  const useViewport = (ref, callback, options) => {
16
- react_1.useEffect(() => {
16
+ (0, react_1.useEffect)(() => {
17
17
  const element = ref.current;
18
18
  if (element) {
19
19
  const observer = new IntersectionObserver(entries => {
@@ -33,15 +33,15 @@ exports.useViewport = useViewport;
33
33
  * @return Объект работы с флагом.
34
34
  */
35
35
  const useAllowFlag = () => {
36
- const ref = react_1.useRef(true);
37
- const timerIdRef = react_1.useRef();
38
- const allowed = react_1.useCallback(() => ref.current, []);
39
- const disallowFor = react_1.useCallback(timeout => {
36
+ const ref = (0, react_1.useRef)(true);
37
+ const timerIdRef = (0, react_1.useRef)();
38
+ const allowed = (0, react_1.useCallback)(() => ref.current, []);
39
+ const disallowFor = (0, react_1.useCallback)(timeout => {
40
40
  window.clearTimeout(timerIdRef.current);
41
41
  ref.current = false;
42
42
  timerIdRef.current = window.setTimeout(() => (ref.current = true), timeout);
43
43
  }, []);
44
- const controlRef = react_1.useRef({ allowed, disallowFor });
44
+ const controlRef = (0, react_1.useRef)({ allowed, disallowFor });
45
45
  return controlRef.current;
46
46
  };
47
47
  exports.useAllowFlag = useAllowFlag;
@@ -53,16 +53,16 @@ exports.useAllowFlag = useAllowFlag;
53
53
  * @return Ширина.
54
54
  */
55
55
  const useChildWidth = (ref, selector, deps) => {
56
- const [itemWidth, setItemWidth] = react_1.useState(null);
57
- const update = react_1.useCallback(() => {
56
+ const [itemWidth, setItemWidth] = (0, react_1.useState)(null);
57
+ const update = (0, react_1.useCallback)(() => {
58
58
  if (ref.current) {
59
59
  const child = ref.current.querySelector(selector);
60
60
  child && setItemWidth(child.clientWidth);
61
61
  }
62
62
  }, [ref]);
63
- react_1.useEffect(() => {
63
+ (0, react_1.useEffect)(() => {
64
64
  ref.current && update();
65
- return on_1.default(window, 'resize', update);
65
+ return (0, on_1.default)(window, 'resize', update);
66
66
  }, deps);
67
67
  return itemWidth;
68
68
  };
@@ -21,7 +21,7 @@ const BannerTitle = ({ promotionType, volumeDiscount, dueDate }) => {
21
21
  break;
22
22
  case 'volume-discount':
23
23
  if (volumeDiscount) {
24
- content = (react_1.default.createElement("div", { className: banner_title_module_scss_1.default.discount },
24
+ content = (react_1.default.createElement("div", { className: banner_title_module_scss_1.default.discount, "data-testid": 'promotion-card:banner-discount' },
25
25
  "\u0441\u043A\u0438\u0434\u043A\u0438 \u0434\u043E",
26
26
  react_1.default.createElement("div", { className: banner_title_module_scss_1.default.percent },
27
27
  volumeDiscount,
@@ -32,14 +32,14 @@ const BannerTitle = ({ promotionType, volumeDiscount, dueDate }) => {
32
32
  return react_1.default.createElement(react_1.default.Fragment, null, content);
33
33
  };
34
34
  exports.BannerTitle = BannerTitle;
35
- const CustomTimer = ({ dueDate }) => (react_1.default.createElement(timer_1.Timer, { date: date_fns_1.formatISO(dueDate), timeout: 1000 * 60, format: ({ days, hours, minutes }) => (react_1.default.createElement("div", { className: banner_title_module_scss_1.default.timer },
35
+ const CustomTimer = ({ dueDate }) => (react_1.default.createElement(timer_1.Timer, { date: (0, date_fns_1.formatISO)(dueDate), timeout: 1000 * 60, format: ({ days, hours, minutes }) => (react_1.default.createElement("div", { className: banner_title_module_scss_1.default.timer, "data-testid": 'promotion-card:banner-timer' },
36
36
  react_1.default.createElement(TimerPart, { label: '\u0434\u043D\u0435\u0439', value: Math.min(99, days) }),
37
- react_1.default.createElement(Divider, null),
37
+ react_1.default.createElement(TimerDivider, null),
38
38
  react_1.default.createElement(TimerPart, { label: '\u0447\u0430\u0441\u043E\u0432', value: hours % 24 }),
39
- react_1.default.createElement(Divider, null),
39
+ react_1.default.createElement(TimerDivider, null),
40
40
  react_1.default.createElement(TimerPart, { label: '\u043C\u0438\u043D\u0443\u0442', value: minutes % 60 }))) }));
41
41
  exports.CustomTimer = CustomTimer;
42
42
  const TimerPart = ({ label, value }) => (react_1.default.createElement("div", { className: banner_title_module_scss_1.default.part },
43
43
  react_1.default.createElement("div", null, `${value}`.padStart(2, '0')),
44
44
  react_1.default.createElement("div", { className: banner_title_module_scss_1.default.label }, label)));
45
- const Divider = () => react_1.default.createElement("div", { className: banner_title_module_scss_1.default.divider }, ":");
45
+ const TimerDivider = () => react_1.default.createElement("div", { className: banner_title_module_scss_1.default.divider }, ":");
@@ -24,12 +24,14 @@ const toTimePart = (n) => `${n}`.padStart(2, '0');
24
24
  const Estimate = ({ dueDate }) => {
25
25
  const now = new Date();
26
26
  let content = null;
27
- if (date_fns_1.getYear(dueDate) !== date_fns_1.getYear(now)) {
28
- const text = [date_fns_1.getDate, date_fns_1.getMonth, date_fns_1.getYear].map(fn => toTimePart(fn(dueDate))).join('.');
27
+ if ((0, date_fns_1.getYear)(dueDate) !== (0, date_fns_1.getYear)(now)) {
28
+ const text = [date_fns_1.getDate, (d) => (0, date_fns_1.getMonth)(d) + 1, date_fns_1.getYear]
29
+ .map(fn => toTimePart(fn(dueDate)))
30
+ .join('.');
29
31
  content = `До ${text}`;
30
32
  }
31
33
  else {
32
- content = `До ${date_fns_1.getDate(dueDate)} ${MONTH_SHORT_NAME[date_fns_1.getMonth(dueDate)]}`;
34
+ content = `До ${(0, date_fns_1.getDate)(dueDate)} ${MONTH_SHORT_NAME[(0, date_fns_1.getMonth)(dueDate)]}`;
33
35
  }
34
36
  return react_1.default.createElement(react_1.default.Fragment, null, content);
35
37
  };
@@ -17,6 +17,8 @@ export interface PromotionCardProps extends React.HTMLAttributes<HTMLDivElement>
17
17
  promotionType?: PromotionType;
18
18
  /** Скидка за объем, указывается при соответствующем типе акции. */
19
19
  volumeDiscount?: number;
20
+ /** Идентификатор для систем автоматизированного тестирования. */
21
+ 'data-testid'?: string;
20
22
  }
21
23
  /**
22
24
  * Карточка акции.
@@ -24,6 +26,6 @@ export interface PromotionCardProps extends React.HTMLAttributes<HTMLDivElement>
24
26
  * @return Элемент.
25
27
  */
26
28
  export declare const PromotionCard: {
27
- ({ promotionType, href, imageSrc, title, subtitle, dueDate, volumeDiscount, postfix, className, ...restProps }: PromotionCardProps): JSX.Element;
29
+ ({ promotionType, href, imageSrc, title, subtitle, dueDate, volumeDiscount, postfix, className, "data-testid": testId, ...restProps }: PromotionCardProps): JSX.Element;
28
30
  Placeholder: (props: React.HTMLAttributes<HTMLDivElement>) => JSX.Element;
29
31
  };
@@ -55,13 +55,13 @@ const PromotionName = {
55
55
  const PromotionCard = (_a) => {
56
56
  var { promotionType, href, imageSrc, title, subtitle, dueDate, volumeDiscount, postfix = promotionType ? PromotionName[promotionType] : undefined,
57
57
  // div props
58
- className } = _a, restProps = __rest(_a, ["promotionType", "href", "imageSrc", "title", "subtitle", "dueDate", "volumeDiscount", "postfix", "className"]);
58
+ className, 'data-testid': testId = 'promotion-card' } = _a, restProps = __rest(_a, ["promotionType", "href", "imageSrc", "title", "subtitle", "dueDate", "volumeDiscount", "postfix", "className", 'data-testid']);
59
59
  // время нужно выводить только на клиенте - вводим состояние
60
- const [mounted, setMounted] = react_1.useState(false);
61
- react_1.useEffect(() => {
60
+ const [mounted, setMounted] = (0, react_1.useState)(false);
61
+ (0, react_1.useEffect)(() => {
62
62
  setMounted(true);
63
63
  }, []);
64
- return (react_1.default.createElement("div", Object.assign({ className: classnames_1.default(className, index_module_scss_1.default.root) }, restProps),
64
+ return (react_1.default.createElement("div", Object.assign({ className: (0, classnames_1.default)(className, index_module_scss_1.default.root) }, restProps, { "data-testid": testId }),
65
65
  react_1.default.createElement("a", { href: href, className: index_module_scss_1.default.link },
66
66
  react_1.default.createElement("div", { className: index_module_scss_1.default.banner },
67
67
  react_1.default.createElement("img", { src: imageSrc, alt: title, className: index_module_scss_1.default.image }),
@@ -72,10 +72,10 @@ const PromotionCard = (_a) => {
72
72
  react_1.default.createElement("h4", { className: index_module_scss_1.default.title }, title),
73
73
  subtitle && react_1.default.createElement("p", { className: index_module_scss_1.default.subtitle }, subtitle)),
74
74
  react_1.default.createElement("div", { className: index_module_scss_1.default.footer },
75
- mounted && (react_1.default.createElement("time", { className: index_module_scss_1.default.timer },
75
+ mounted && (react_1.default.createElement("time", { className: index_module_scss_1.default.timer, "data-testid": 'promotion-card:due-date' },
76
76
  react_1.default.createElement(watch_1.default, { className: index_module_scss_1.default['timer-svg'] }),
77
77
  react_1.default.createElement(estimate_1.Estimate, { dueDate: dueDate }))),
78
- postfix && react_1.default.createElement("div", { className: index_module_scss_1.default.postfix }, postfix))))));
78
+ postfix && (react_1.default.createElement("div", { "data-testid": 'promotion-card:postfix', className: index_module_scss_1.default.postfix }, postfix)))))));
79
79
  };
80
80
  exports.PromotionCard = PromotionCard;
81
81
  exports.PromotionCard.Placeholder = placeholder_1.PromotionCardPlaceholder;
@@ -12,8 +12,8 @@ const classnames_1 = __importDefault(require("classnames"));
12
12
  * @param props Свойства div.
13
13
  * @return Элемент.
14
14
  */
15
- const PromotionCardPlaceholder = (props) => (react_1.default.createElement("div", Object.assign({}, props),
16
- react_1.default.createElement("div", { className: classnames_1.default(placeholder_module_scss_1.default.part, placeholder_module_scss_1.default.banner) }),
17
- react_1.default.createElement("div", { className: classnames_1.default(placeholder_module_scss_1.default.part, placeholder_module_scss_1.default.text) }),
18
- react_1.default.createElement("div", { className: classnames_1.default(placeholder_module_scss_1.default.part, placeholder_module_scss_1.default.text) })));
15
+ const PromotionCardPlaceholder = (props) => (react_1.default.createElement("div", Object.assign({ "data-testid": 'promotion-card:placeholder' }, props),
16
+ react_1.default.createElement("div", { className: (0, classnames_1.default)(placeholder_module_scss_1.default.part, placeholder_module_scss_1.default.banner) }),
17
+ react_1.default.createElement("div", { className: (0, classnames_1.default)(placeholder_module_scss_1.default.part, placeholder_module_scss_1.default.text) }),
18
+ react_1.default.createElement("div", { className: (0, classnames_1.default)(placeholder_module_scss_1.default.part, placeholder_module_scss_1.default.text) })));
19
19
  exports.PromotionCardPlaceholder = PromotionCardPlaceholder;
@@ -1,4 +1,33 @@
1
- .wrapper {
2
- box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08), 0 0 4px rgba(0, 0, 0, 0.04);
3
- border-radius: 8px;
1
+ @use 'node_modules/@sima-land/ui-nucleons/colors';
2
+
3
+ .root {
4
+ padding: 24px;
5
+ display: flex;
6
+ justify-content: space-between;
7
+ }
8
+
9
+ .main {
10
+ margin-right: 20px;
11
+ }
12
+
13
+ .text {
14
+ font-size: 14px;
15
+ line-height: 20px;
16
+ margin-bottom: 12px;
17
+ }
18
+
19
+ .icon {
20
+ flex-shrink: 0;
21
+ width: 48px;
22
+ height: 48px;
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ background: rgba(colors.$additional-amber, 0.5);
27
+ border-radius: 50%;
28
+ }
29
+
30
+ .svg {
31
+ display: block;
32
+ fill: #fff;
4
33
  }
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  export interface GarlandCalculatorButtonProps {
3
- /** Обработчик нажатия на кнопку рассчитать. */
4
- onClick?: React.MouseEventHandler;
3
+ /** Обработчик клика. */
4
+ onClick?: React.MouseEventHandler<HTMLAnchorElement>;
5
5
  /** CSS-класс. */
6
6
  className?: string;
7
7
  }
@@ -11,4 +11,3 @@ export interface GarlandCalculatorButtonProps {
11
11
  * @return Элемент.
12
12
  */
13
13
  export declare const GarlandCalculatorButton: ({ className, onClick }: GarlandCalculatorButtonProps) => JSX.Element;
14
- export default GarlandCalculatorButton;