@sima-land/moleculas 12.1.0 → 12.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/common/components/adult-block/adult-block.d.ts +2 -2
  2. package/common/components/adult-block/adult-block.js +2 -2
  3. package/common/components/badge/badge.module.scss +1 -1
  4. package/common/components/badge/index.d.ts +4 -0
  5. package/common/components/badge/index.js +2 -6
  6. package/common/components/badge-list/badge-list.module.scss +0 -1
  7. package/common/components/badge-list/index.d.ts +6 -2
  8. package/common/components/badge-list/index.js +4 -2
  9. package/common/components/product-info/index.d.ts +5 -0
  10. package/common/components/product-info/index.js +5 -4
  11. package/common/components/product-info/product-info.module.scss +10 -0
  12. package/common/components/quick-view-button/index.js +1 -1
  13. package/common/components/wish-button/index.js +1 -1
  14. package/common/helpers/format-qty-string.js +1 -3
  15. package/common/helpers/get-contained-items.js +2 -1
  16. package/desktop/components/breadcrumbs/breadcrumbs.module.scss +2 -2
  17. package/desktop/components/breadcrumbs/index.d.ts +4 -0
  18. package/desktop/components/breadcrumbs/index.js +5 -5
  19. package/desktop/components/gallery-modal/components/all-round-view.js +13 -13
  20. package/desktop/components/gallery-modal/components/all-round-view.module.scss +1 -1
  21. package/desktop/components/gallery-modal/components/gallery-modal.d.ts +7 -0
  22. package/desktop/components/gallery-modal/components/gallery-modal.js +13 -12
  23. package/desktop/components/gallery-modal/components/image-overlay.js +1 -1
  24. package/desktop/components/gallery-modal/components/image-overlay.module.scss +1 -1
  25. package/desktop/components/gallery-modal/components/review-info.d.ts +1 -1
  26. package/desktop/components/gallery-modal/components/review-info.js +1 -1
  27. package/desktop/components/gallery-modal/components/thumbnail.d.ts +1 -1
  28. package/desktop/components/gallery-modal/components/thumbnail.js +3 -3
  29. package/desktop/components/gallery-modal/utils.d.ts +1 -1
  30. package/desktop/components/gallery-modal/utils.js +6 -6
  31. package/desktop/components/item-image/index.d.ts +1 -1
  32. package/desktop/components/item-image/index.js +8 -9
  33. package/desktop/components/item-image/item-image.module.scss +4 -4
  34. package/desktop/components/modifiers/button/index.js +4 -4
  35. package/desktop/components/modifiers/group/index.js +19 -18
  36. package/desktop/components/modifiers/widget/index.js +2 -2
  37. package/desktop/components/person-modal/index.d.ts +16 -14
  38. package/desktop/components/person-modal/index.js +5 -16
  39. package/desktop/components/product-card/index.d.ts +5 -2
  40. package/desktop/components/product-card/index.js +7 -7
  41. package/desktop/components/product-card/product-card.module.scss +14 -0
  42. package/desktop/components/product-carousel/hover-card.js +4 -4
  43. package/desktop/components/product-carousel/index.d.ts +6 -0
  44. package/desktop/components/product-carousel/index.js +35 -34
  45. package/desktop/components/product-carousel/product-carousel.module.scss +1 -1
  46. package/desktop/components/product-carousel/utils.js +11 -11
  47. package/desktop/components/promotion-card/banner-title.js +7 -7
  48. package/desktop/components/promotion-card/estimate.js +4 -6
  49. package/desktop/components/promotion-card/index.d.ts +11 -1
  50. package/desktop/components/promotion-card/index.js +7 -7
  51. package/desktop/components/promotion-card/index.module.scss +2 -2
  52. package/desktop/components/promotion-card/placeholder.js +4 -4
  53. package/mobile/components/garland-calculator-button/garland-calculator-button.module.scss +1 -1
  54. package/mobile/components/item-image/index.d.ts +1 -1
  55. package/mobile/components/item-image/index.js +5 -7
  56. package/mobile/components/item-image/item-image.module.scss +3 -3
  57. package/mobile/components/modifiers-list/index.d.ts +1 -1
  58. package/mobile/components/modifiers-list/index.js +1 -1
  59. package/mobile/components/modifiers-list/modifier-item/index.js +7 -7
  60. package/mobile/components/modifiers-list/modifiers-list.module.scss +0 -1
  61. package/mobile/components/person-screen/index.d.ts +1 -1
  62. package/mobile/components/person-screen/index.js +3 -3
  63. package/mobile/components/person-screen/person-screen.module.scss +0 -1
  64. package/mobile/components/product-actions-alert/index.js +6 -5
  65. package/mobile/components/product-row/index.d.ts +1 -1
  66. package/mobile/components/product-row/index.js +19 -24
  67. package/mobile/components/product-row/product-row.module.scss +0 -1
  68. package/mobile/components/product-slider/index.d.ts +1 -1
  69. package/mobile/components/product-slider/index.js +7 -7
  70. package/mobile/components/product-slider/item.js +2 -2
  71. package/mobile/components/product-slider/utils.js +1 -1
  72. package/mobile/components/search-bar/index.d.ts +1 -1
  73. package/mobile/components/search-bar/index.js +31 -31
  74. package/mobile/components/search-bar/search-bar.module.scss +9 -3
  75. package/mobile/components/select-screen/index.js +5 -6
  76. package/package.json +39 -31
  77. package/README.md +0 -86
@@ -54,7 +54,7 @@
54
54
  height: 2px;
55
55
  right: 2px;
56
56
  background: #9e9e9e;
57
- opacity: .32;
57
+ opacity: 0.32;
58
58
  border-radius: 4px;
59
59
  }
60
60
  &.selected {
@@ -83,7 +83,7 @@
83
83
  left: 0;
84
84
  right: 0;
85
85
  bottom: 0;
86
- background-color: rgba(0, 0, 0, .04);
86
+ background-color: rgba(0, 0, 0, 0.04);
87
87
  }
88
88
  }
89
89
  }
@@ -108,13 +108,13 @@
108
108
  left: -5px;
109
109
  width: calc(100% + 10px);
110
110
  height: calc(100% + 10px);
111
- background: rgba(255, 255, 255, .5);
111
+ background: rgba(255, 255, 255, 0.5);
112
112
  z-index: 4;
113
113
  display: flex;
114
114
  justify-content: center;
115
115
  align-items: center;
116
116
  @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
117
- background: rgba(255, 255, 255, .95);
117
+ background: rgba(255, 255, 255, 0.95);
118
118
  }
119
119
  }
120
120
 
@@ -24,7 +24,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.ModifierButton = void 0;
26
26
  const react_1 = __importStar(require("react"));
27
- const isNumber_1 = __importDefault(require("lodash/isNumber"));
27
+ const lodash_1 = require("lodash");
28
28
  const modifier_button_module_scss_1 = __importDefault(require("./modifier-button.module.scss"));
29
29
  const bind_1 = __importDefault(require("classnames/bind"));
30
30
  const constants_1 = require("../../../../common/constants");
@@ -33,14 +33,14 @@ const typesList = Object.values(constants_1.MODIFIERS_TYPES);
33
33
  /**
34
34
  * Компонент модификатора.
35
35
  */
36
- exports.ModifierButton = react_1.forwardRef(function ModifierButton({ count = null, content, selected, color, image, type: typeProp = 'text', className, onClick, isMarkdown: hasMarkdown, squared = [constants_1.MODIFIERS_TYPES.color, constants_1.MODIFIERS_TYPES.image].includes(typeProp), 'data-testid': testId = 'modifier-button', }, ref) {
36
+ exports.ModifierButton = (0, react_1.forwardRef)(function ModifierButton({ count = null, content, selected, color, image, type: typeProp = 'text', className, onClick, isMarkdown: hasMarkdown, squared = [constants_1.MODIFIERS_TYPES.color, constants_1.MODIFIERS_TYPES.image].includes(typeProp), 'data-testid': testId = 'modifier-button', }, ref) {
37
37
  const type = typesList.includes(typeProp) ? typeProp : 'text';
38
- const hasCount = isNumber_1.default(count) && count >= 0;
38
+ const hasCount = (0, lodash_1.isNumber)(count) && count >= 0;
39
39
  return (react_1.default.createElement("div", { ref: ref, className: cx('modifier-button', squared && 'squared', selected && 'selected', className), style: color ? { background: color } : undefined, onClick: onClick, "data-testid": testId },
40
40
  (hasCount || hasMarkdown) && (react_1.default.createElement("span", { className: cx('label', hasCount && 'with-count'), "data-testid": 'modifier-button:badge' },
41
41
  hasCount && (count > 99 ? '99+' : count),
42
42
  hasMarkdown && (hasCount ? '(У)' : 'У'))),
43
43
  [constants_1.MODIFIERS_TYPES.text, constants_1.MODIFIERS_TYPES.image].includes(type) && (react_1.default.createElement("div", { className: cx('content') },
44
44
  type === constants_1.MODIFIERS_TYPES.text && (react_1.default.createElement("span", { className: cx('text', !squared && 'guttered') }, String(content || ''))),
45
- type === constants_1.MODIFIERS_TYPES.image && (react_1.default.createElement("img", { src: image, alt: content, className: cx('image') }))))));
45
+ type === constants_1.MODIFIERS_TYPES.image && react_1.default.createElement("img", { src: image, alt: content, className: cx('image') })))));
46
46
  });
@@ -24,7 +24,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.ModifiersGroup = exports.createHiddenNodeIndexFinder = void 0;
26
26
  const react_1 = __importStar(require("react"));
27
- const debounce_1 = __importDefault(require("lodash/debounce"));
27
+ const lodash_1 = require("lodash");
28
28
  const prop_1 = __importDefault(require("lodash/fp/prop"));
29
29
  const button_1 = require("../button");
30
30
  const on_1 = __importDefault(require("@sima-land/ui-nucleons/helpers/on"));
@@ -48,13 +48,13 @@ const createHiddenNodeIndexFinder = (container) => (childNode) => {
48
48
  return childOffsetTop - container.offsetTop >= container.clientHeight;
49
49
  };
50
50
  exports.createHiddenNodeIndexFinder = createHiddenNodeIndexFinder;
51
- const defaultGetItemType = prop_1.default('type');
52
- const defaultGetItemCount = prop_1.default('count');
53
- const defaultGetItemColor = prop_1.default('color');
54
- const defaultGetItemContent = prop_1.default('content');
55
- const defaultIsSelectedItem = prop_1.default('selected');
56
- const defaultGetItemImage = prop_1.default('image');
57
- const defaultGetIsMarkdown = prop_1.default('isMarkdown');
51
+ const defaultGetItemType = (0, prop_1.default)('type');
52
+ const defaultGetItemCount = (0, prop_1.default)('count');
53
+ const defaultGetItemColor = (0, prop_1.default)('color');
54
+ const defaultGetItemContent = (0, prop_1.default)('content');
55
+ const defaultIsSelectedItem = (0, prop_1.default)('selected');
56
+ const defaultGetItemImage = (0, prop_1.default)('image');
57
+ const defaultGetIsMarkdown = (0, prop_1.default)('isMarkdown');
58
58
  /**
59
59
  * Возвращает компонент списка модификаторов.
60
60
  * @param {Object} props Свойства компонента.
@@ -82,12 +82,12 @@ class ModifiersGroup extends react_1.Component {
82
82
  lastVisibleChildIndex: null,
83
83
  };
84
84
  this.windowWidth = 0;
85
- this.containerRef = react_1.createRef();
86
- this.showingButtonRef = react_1.createRef();
85
+ this.containerRef = (0, react_1.createRef)();
86
+ this.showingButtonRef = (0, react_1.createRef)();
87
87
  /**
88
88
  * Сбрасывает информацию о видимых и невидимых дочерних узлах контейнера.
89
89
  */
90
- this.resetVisibleNodesData = debounce_1.default(() => {
90
+ this.resetVisibleNodesData = (0, lodash_1.debounce)(() => {
91
91
  if (document.documentElement.offsetWidth !== this.windowWidth) {
92
92
  this.windowWidth = document.documentElement.offsetWidth;
93
93
  this.setState({
@@ -107,7 +107,7 @@ class ModifiersGroup extends react_1.Component {
107
107
  const showingButtonEl = this.showingButtonRef.current;
108
108
  if (containerEl && showingButtonEl) {
109
109
  const childList = [...containerEl.children];
110
- const firstHiddenNodeIndex = childList.findIndex(exports.createHiddenNodeIndexFinder(containerEl));
110
+ const firstHiddenNodeIndex = childList.findIndex((0, exports.createHiddenNodeIndexFinder)(containerEl));
111
111
  if (firstHiddenNodeIndex !== -1) {
112
112
  const lastVisibleElIndex = firstHiddenNodeIndex - 1;
113
113
  const lastVisibleNode = childList[lastVisibleElIndex];
@@ -115,7 +115,7 @@ class ModifiersGroup extends react_1.Component {
115
115
  const rightBound = lastVisibleNode.getBoundingClientRect().right;
116
116
  const rightContainerBound = containerEl.getBoundingClientRect().right;
117
117
  // проверяем, хватает ли кнопке места (с запасом в половину) после последнего видимого дочернего элемента
118
- const isShowingButtonFit = rightContainerBound - rightBound >= (showingButtonEl.clientWidth * 1.5);
118
+ const isShowingButtonFit = rightContainerBound - rightBound >= showingButtonEl.clientWidth * 1.5;
119
119
  this.setState({
120
120
  // если хватает места - ставим кнопку после последнего, иначе - вместо
121
121
  lastVisibleChildIndex: lastVisibleElIndex + (isShowingButtonFit ? 1 : 0),
@@ -143,7 +143,7 @@ class ModifiersGroup extends react_1.Component {
143
143
  */
144
144
  componentDidMount() {
145
145
  this.windowWidth = document.documentElement.offsetWidth;
146
- this.removeGlobalListener = on_1.default(window, 'resize', this.resetVisibleNodesData);
146
+ this.removeGlobalListener = (0, on_1.default)(window, 'resize', this.resetVisibleNodesData);
147
147
  this.checkVisibleNodes();
148
148
  }
149
149
  /**
@@ -166,10 +166,11 @@ class ModifiersGroup extends react_1.Component {
166
166
  const { needShowAll, hasHiddenNodes, lastVisibleChildIndex } = this.state;
167
167
  const needHideModifiers = !needShowAll && lastVisibleChildIndex !== null;
168
168
  return (react_1.default.createElement("div", { className: cx('modifiers-group', !needShowAll && 'height-limited'), ref: this.containerRef, "data-testid": testId },
169
- Array.isArray(items) && items.map((item, index) => {
170
- const isDisplayed = !needHideModifiers || index < lastVisibleChildIndex;
171
- return (react_1.default.createElement(button_1.ModifierButton, { key: index, type: getItemType(item), count: getItemCount(item), color: getItemColor(item), content: getItemContent(item), selected: isSelectedItem(item), image: getItemImage(item), onClick: () => onSelectItem && onSelectItem(item, isSelectedItem(item)), className: cx('guttered', !isDisplayed && 'not-display'), isMarkdown: isMarkdown(item) }));
172
- }),
169
+ Array.isArray(items) &&
170
+ items.map((item, index) => {
171
+ const isDisplayed = !needHideModifiers || index < lastVisibleChildIndex;
172
+ return (react_1.default.createElement(button_1.ModifierButton, { key: index, type: getItemType(item), count: getItemCount(item), color: getItemColor(item), content: getItemContent(item), selected: isSelectedItem(item), image: getItemImage(item), onClick: () => onSelectItem && onSelectItem(item, isSelectedItem(item)), className: cx('guttered', !isDisplayed && 'not-display'), isMarkdown: isMarkdown(item) }));
173
+ }),
173
174
  Array.isArray(items) && hasHiddenNodes && !needShowAll && (react_1.default.createElement(button_1.ModifierButton, { squared: true, ref: this.showingButtonRef, content: `+${items.length - lastVisibleChildIndex}`, className: cx('show-all-button', 'guttered', lastVisibleChildIndex === null && 'invisible'), onClick: this.handleShowAllClick, "data-testid": 'modifier-group:show-all-button' }))));
174
175
  }
175
176
  }
@@ -27,9 +27,9 @@ const ModifiersWidget = ({ title, items, sizesTableUrl, onSelectItem, }) => (rea
27
27
  " (",
28
28
  items.length,
29
29
  "\u00A0",
30
- get_declination_1.default(items.length, ['вариант', 'варианта', 'вариантов']),
30
+ (0, get_declination_1.default)(items.length, ['вариант', 'варианта', 'вариантов']),
31
31
  "):")),
32
- react_1.default.createElement(group_1.ModifiersGroup, { items: items, getItemContent: prop_1.default('name'), getItemCount: prop_1.default('inCartQty'), onSelectItem: (item, selected) => {
32
+ react_1.default.createElement(group_1.ModifiersGroup, { items: items, getItemContent: (0, prop_1.default)('name'), getItemCount: (0, prop_1.default)('inCartQty'), onSelectItem: (item, selected) => {
33
33
  !selected && onSelectItem && onSelectItem(item);
34
34
  } }),
35
35
  Boolean(sizesTableUrl) && (react_1.default.createElement(link_1.Link, { href: sizesTableUrl, className: cx('modifiers-link') }, "\u0422\u0430\u0431\u043B\u0438\u0446\u0430 \u0440\u0430\u0437\u043C\u0435\u0440\u043E\u0432"))));
@@ -1,37 +1,39 @@
1
1
  /// <reference types="react" />
2
2
  import { ModalProps } from '@sima-land/ui-nucleons/modal';
3
3
  export interface PersonModalProps {
4
+ /** Имя. */
4
5
  name: string;
6
+ /** Должность. */
5
7
  appointment: string;
8
+ /** Ссылка на фото. */
6
9
  photoUrl?: string;
10
+ /** Почта. */
7
11
  email?: string;
12
+ /** Skype. */
8
13
  skype?: string;
14
+ /** Социальные сети. */
9
15
  social?: any[];
16
+ /** Ссылка на рабочий номер телефона. */
10
17
  phoneHref?: string;
18
+ /** Рабочий номер телефона. */
11
19
  phoneText?: string;
20
+ /** Ссылка на сотовый номер телефона. */
12
21
  secondPhoneHref?: string;
22
+ /** Сотовый номер телефона. */
13
23
  secondPhoneText?: string;
24
+ /** Свойства ссылки под именем. */
14
25
  arbitraryLinkProps?: any;
26
+ /** Сработает при нажатии на кнопку "Закрыть". */
15
27
  onClose: () => void;
28
+ /** Нужно ли блокировать прокрутку пока открыто окно. */
16
29
  withScrollDisable?: ModalProps['withScrollDisable'];
30
+ /** Опции блокировки прокрутки. */
17
31
  scrollDisableOptions?: ModalProps['scrollDisableOptions'];
18
32
  }
19
33
  /**
20
- * Компонент карточки персоны.
34
+ * Компонент модального окна сотрудника.
21
35
  * @param props Свойства.
22
- * @param props.name Имя.
23
- * @param props.appointment Должность.
24
- * @param props.photoUrl Ссылка на фото.
25
- * @param props.email Почта.
26
- * @param props.skype Skype.
27
- * @param props.social Социальные сети.
28
- * @param props.phoneHref Ссылка на рабочий номер телефона.
29
- * @param props.phoneText Рабочий номер телефона.
30
- * @param props.secondPhoneHref Ссылка на сотовый номер телефона.
31
- * @param props.secondPhoneText Сотовый номер телефона.
32
- * @param props.arbitraryLinkProps Свойства ссылки под именем.
33
- * @param props.onClose Сработает при нажатии на кнопку "Закрыть".
34
- * @return Компонент.
36
+ * @return Элемент.
35
37
  */
36
38
  export declare const PersonModal: ({ name, appointment, photoUrl, email, skype, social, phoneHref, phoneText, secondPhoneHref, secondPhoneText, arbitraryLinkProps, onClose, withScrollDisable, scrollDisableOptions, }: PersonModalProps) => JSX.Element;
37
39
  export default PersonModal;
@@ -16,24 +16,12 @@ const modal_1 = require("@sima-land/ui-nucleons/modal");
16
16
  const keydown_1 = require("@sima-land/ui-nucleons/hooks/keydown");
17
17
  const cx = bind_1.default.bind(person_modal_module_scss_1.default);
18
18
  /**
19
- * Компонент карточки персоны.
19
+ * Компонент модального окна сотрудника.
20
20
  * @param props Свойства.
21
- * @param props.name Имя.
22
- * @param props.appointment Должность.
23
- * @param props.photoUrl Ссылка на фото.
24
- * @param props.email Почта.
25
- * @param props.skype Skype.
26
- * @param props.social Социальные сети.
27
- * @param props.phoneHref Ссылка на рабочий номер телефона.
28
- * @param props.phoneText Рабочий номер телефона.
29
- * @param props.secondPhoneHref Ссылка на сотовый номер телефона.
30
- * @param props.secondPhoneText Сотовый номер телефона.
31
- * @param props.arbitraryLinkProps Свойства ссылки под именем.
32
- * @param props.onClose Сработает при нажатии на кнопку "Закрыть".
33
- * @return Компонент.
21
+ * @return Элемент.
34
22
  */
35
23
  const PersonModal = ({ name, appointment, photoUrl, email, skype, social, phoneHref, phoneText, secondPhoneHref, secondPhoneText, arbitraryLinkProps, onClose, withScrollDisable, scrollDisableOptions, }) => {
36
- keydown_1.useKeydown('Escape', onClose);
24
+ (0, keydown_1.useKeydown)('Escape', onClose);
37
25
  return (react_1.default.createElement(modal_1.Modal, { size: 's', withScrollDisable: withScrollDisable, scrollDisableOptions: scrollDisableOptions, onClose: onClose },
38
26
  react_1.default.createElement(modal_1.Modal.Body, null,
39
27
  react_1.default.createElement("div", { className: cx('main') },
@@ -50,7 +38,8 @@ const PersonModal = ({ name, appointment, photoUrl, email, skype, social, phoneH
50
38
  'Email: ',
51
39
  react_1.default.createElement(link_1.Link, { color: 'gray87', href: `mailto:${email}`, children: email }))),
52
40
  Array.isArray(social) && social.length > 0 && (react_1.default.createElement("span", { className: cx('contacts-item') },
53
- "\u0421\u043E\u0446. \u0441\u0435\u0442\u0438: ",
41
+ "\u0421\u043E\u0446. \u0441\u0435\u0442\u0438:",
42
+ ' ',
54
43
  social.map((props, key) => (react_1.default.createElement(link_1.Link, Object.assign({ className: cx('inline'), key: key, color: 'gray87', target: '_blank', rel: 'nofollow' }, props)))))),
55
44
  skype && (react_1.default.createElement("span", { className: cx('contacts-item') },
56
45
  'Skype: ',
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
2
  import { ProductInfoProps } from '../../../common/components/product-info';
3
3
  export interface ProductCardProps extends ProductInfoProps, React.HTMLAttributes<HTMLDivElement> {
4
+ /** Будет вызвана при уходе курсора с элемента. */
4
5
  onMouseLeave?: () => void;
6
+ /** Будет вызвана при нажатии на кнопку-иконку быстрого просмотра. */
5
7
  onQuickViewClick?: () => void;
6
8
  }
7
9
  /**
@@ -9,10 +11,11 @@ export interface ProductCardProps extends ProductInfoProps, React.HTMLAttributes
9
11
  * @param props Свойства.
10
12
  * @return Элемент.
11
13
  */
12
- declare const CartControlSlot: ({ children, stepText, markupText, }: {
13
- children: React.ReactNode;
14
+ declare const CartControlSlot: ({ children, stepText, markupText, loading, }: {
15
+ children?: React.ReactNode;
14
16
  stepText?: string | undefined;
15
17
  markupText?: string | undefined;
18
+ loading?: boolean | undefined;
16
19
  }) => JSX.Element;
17
20
  export interface ProductCardComponent extends React.ForwardRefExoticComponent<React.PropsWithoutRef<ProductCardProps> & React.RefAttributes<HTMLDivElement>> {
18
21
  CartControl: typeof CartControlSlot;
@@ -50,24 +50,24 @@ const cx = bind_1.default.bind(product_card_module_scss_1.default);
50
50
  * @param props Свойства.
51
51
  * @return Элемент.
52
52
  */
53
- const CartControlSlot = ({ children, stepText, markupText, }) => (react_1.default.createElement("div", { className: cx('control-wrapper') },
54
- react_1.default.createElement("div", { className: cx('control') }, children),
55
- stepText && (react_1.default.createElement("div", { className: cx('unit-text') }, stepText)),
56
- markupText && (react_1.default.createElement("div", { className: cx('bottom-text') }, markupText))));
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)));
57
57
  /**
58
58
  * Карточка товара, появляющаяся над элементом карусели.
59
59
  * @param props Свойства компонента.
60
60
  * @return Элемент.
61
61
  */
62
- exports.ProductCard = react_1.forwardRef((_a, rootRef) => {
62
+ exports.ProductCard = (0, react_1.forwardRef)((_a, rootRef) => {
63
63
  var { data, onLinkClick, onQuickViewClick, children,
64
64
  // div props
65
65
  className } = _a, rootProps = __rest(_a, ["data", "onLinkClick", "onQuickViewClick", "children", "className"]);
66
- const { cartControl } = define_slots_1.defineSlots(children, { cartControl: CartControlSlot });
66
+ const { cartControl } = (0, define_slots_1.defineSlots)(children, { cartControl: CartControlSlot });
67
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
68
  react_1.default.createElement(product_info_1.ProductInfo, { data: data, onLinkClick: onLinkClick },
69
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) }))))),
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: (0, with_prevent_1.default)(onQuickViewClick) }))))),
71
71
  cartControl));
72
72
  });
73
73
  exports.ProductCard.CartControl = CartControlSlot;
@@ -16,6 +16,20 @@
16
16
  display: flex;
17
17
  flex-wrap: wrap;
18
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
+ }
19
33
  }
20
34
 
21
35
  .control {
@@ -37,13 +37,13 @@ const cx = bind_1.default.bind(hover_card_module_scss_1.default);
37
37
  * @return Элемент.
38
38
  */
39
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(() => {
40
+ const rootRef = (0, react_1.useRef)(null);
41
+ const [ready, setReady] = (0, react_1.useState)(false);
42
+ (0, react_1.useLayoutEffect)(() => {
43
43
  if (targetRef.current && rootRef.current) {
44
44
  const element = rootRef.current;
45
45
  const rect = targetRef.current.getBoundingClientRect();
46
- const correction = utils_1.getOriginCorrection(rootRef.current);
46
+ const correction = (0, utils_1.getOriginCorrection)(rootRef.current);
47
47
  element.style.left = `${correction.x + rect.left - 16}px`;
48
48
  element.style.top = `${correction.y + rect.top - 16}px`;
49
49
  element.style.width = `${rect.width + 32}px`;
@@ -9,11 +9,17 @@ interface ItemSize {
9
9
  xl?: 2 | 3 | 4;
10
10
  }
11
11
  export interface ProductCarouselProps {
12
+ /** CSS-класс для корневого элемента. */
12
13
  className?: string;
14
+ /** Элементы карусели. */
13
15
  children?: React.ReactNode;
16
+ /** Найстройки размера элемента карусели. */
14
17
  itemSize?: ItemSize;
18
+ /** Сработает при попадании карусели во viewport. */
15
19
  onInViewport?: () => void;
20
+ /** Сработает при попадании карусели в область достаточно близкую к viewport'у. */
16
21
  onNeedRequest?: () => void;
22
+ /** Нужно ли показывать всплывающую карточку при наведении на элемент карусели. */
17
23
  withHoverCard?: boolean;
18
24
  }
19
25
  declare type CarouselItemProps = ProductInfoProps & Pick<HoverCardProps, 'renderCartControl' | 'onQuickViewClick'>;
@@ -51,50 +51,51 @@ const Item = () => null;
51
51
  * @return Элемент.
52
52
  */
53
53
  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);
54
+ const [activeItemIndex, setActiveItemIndex] = (0, react_1.useState)(null);
55
+ const cardShow = (0, utils_1.useAllowFlag)();
56
+ const needBigArrows = (0, media_1.useMedia)('(min-width: 1600px)');
57
+ const sectionRef = (0, react_1.useRef)(null);
58
+ const stubRef = (0, react_1.useRef)(null);
59
+ const targetItemRef = (0, react_1.useRef)(null);
60
60
  const items = react_1.Children.toArray(children).reduce((acc, item) => {
61
- react_1.isValidElement(item) && item.type === Item && acc.push(item.props);
61
+ (0, react_1.isValidElement)(item) && item.type === Item && acc.push(item.props);
62
62
  return acc;
63
63
  }, []);
64
64
  // вычисляем ширину элемента карусели для позиционирования стрелок
65
- const itemWidth = utils_1.useChildWidth(sectionRef, `.${cx('item')}`, []);
65
+ const itemWidth = (0, utils_1.useChildWidth)(sectionRef, `.${cx('item')}`, [items.length]);
66
66
  // инициируем загрузку данных, когда компонент почти попал в зону видимости
67
- utils_1.useViewport(stubRef, onNeedRequest, {
67
+ (0, utils_1.useViewport)(stubRef, onNeedRequest, {
68
68
  rootMargin: '200px 0px 200px 0px',
69
69
  });
70
70
  // отправляем статистку, когда компонент попадает в зону видимости
71
- utils_1.useViewport(sectionRef, onInViewport);
72
- return Array.isArray(items) && items.length > 0
73
- ? (react_1.default.createElement("div", { ref: sectionRef, className: cx('root', className) },
74
- react_1.default.createElement(carousel_1.Carousel, Object.assign({ step: 3, draggable: false,
75
- // докидываем индекс чтобы позже брать актуальные данные из списка по нему
76
- items: items.map((item, index) => [item, index]) }, (itemWidth !== null && {
77
- withControls: true,
78
- controlProps: {
79
- size: needBigArrows ? 'l' : 's',
80
- style: {
81
- zIndex: 1,
82
- top: `${itemWidth / 2}px`,
83
- },
71
+ (0, 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,
74
+ // докидываем индекс чтобы позже брать актуальные данные из списка по нему
75
+ items: items.map((item, index) => [item, index]) }, (itemWidth !== null && {
76
+ withControls: true,
77
+ controlProps: {
78
+ size: needBigArrows ? 'l' : 's',
79
+ style: {
80
+ zIndex: 1,
81
+ top: `${itemWidth / 2}px`,
84
82
  },
85
- }), { renderItem: ([item, index]) => (react_1.default.createElement("div", { "data-testid": 'product-carousel:item', className: cx('item', getSizeClasses(itemSize)), onMouseEnter: e => {
86
- if (cardShow.allowed()) {
87
- targetItemRef.current = e.currentTarget;
88
- setActiveItemIndex(index);
89
- }
90
- } },
91
- react_1.default.createElement(product_info_1.ProductInfo, { data: item.data, onLinkClick: item.onLinkClick }))),
92
- // длительность прокрутки в Carousel - 320, делаем слегка с запасом
93
- // @todo после восстановления проверить позицию курсора чтобы показать карточку (если будет критично)
94
- onChangeTargetIndex: () => cardShow.disallowFor(360) })),
95
- withHoverCard && activeItemIndex !== null && (react_1.default.createElement(hover_card_1.HoverCard, { targetRef: targetItemRef, onMouseLeave: () => setActiveItemIndex(null),
83
+ },
84
+ }), { renderItem: ([item, index]) => (react_1.default.createElement("div", { "data-testid": 'product-carousel:item', className: cx('item', getSizeClasses(itemSize)), onMouseEnter: e => {
85
+ if (cardShow.allowed()) {
86
+ targetItemRef.current = e.currentTarget;
87
+ setActiveItemIndex(index);
88
+ }
89
+ } },
90
+ react_1.default.createElement(product_info_1.ProductInfo, { data: item.data, onLinkClick: item.onLinkClick }))),
91
+ // длительность прокрутки в Carousel - 320, делаем слегка с запасом
92
+ // @todo после восстановления проверить позицию курсора чтобы показать карточку (если будет критично)
93
+ onChangeTargetIndex: () => cardShow.disallowFor(360) })),
94
+ withHoverCard &&
95
+ activeItemIndex !== null &&
96
+ [items[activeItemIndex]].map(item => (react_1.default.createElement(hover_card_1.HoverCard, { key: activeItemIndex, targetRef: targetItemRef, onMouseLeave: () => setActiveItemIndex(null),
96
97
  // данные элемента карусели
97
- data: items[activeItemIndex].data, onLinkClick: items[activeItemIndex].onLinkClick, onQuickViewClick: items[activeItemIndex].onQuickViewClick, renderCartControl: items[activeItemIndex].renderCartControl })))) : (react_1.default.createElement("div", { ref: stubRef }));
98
+ data: item.data, onLinkClick: item.onLinkClick, onQuickViewClick: item.onQuickViewClick, renderCartControl: item.renderCartControl }))))) : (react_1.default.createElement("div", { ref: stubRef }));
98
99
  };
99
100
  exports.ProductCarousel = ProductCarousel;
100
101
  exports.ProductCarousel.Item = Item;
@@ -4,7 +4,7 @@
4
4
  $breakpoints: 'xs', 's', 'm', 'l', 'xl';
5
5
  $gutter: 32px;
6
6
 
7
- @mixin sizes ($key) {
7
+ @mixin sizes($key) {
8
8
  // в соответствии с гайдами ширина может быть 2/3/4
9
9
  @for $i from 2 through 4 {
10
10
  .size-#{$key}-#{$i} {
@@ -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
- timerIdRef.current = window.setTimeout(() => ref.current = true, timeout);
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
  };
@@ -16,12 +16,12 @@ const BannerTitle = ({ promotionType, volumeDiscount, dueDate }) => {
16
16
  break;
17
17
  case 'special':
18
18
  if (dueDate) {
19
- content = (react_1.default.createElement(exports.CustomTimer, { dueDate: dueDate }));
19
+ content = react_1.default.createElement(exports.CustomTimer, { dueDate: dueDate });
20
20
  }
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,
@@ -29,17 +29,17 @@ const BannerTitle = ({ promotionType, volumeDiscount, dueDate }) => {
29
29
  }
30
30
  break;
31
31
  }
32
- return (react_1.default.createElement(react_1.default.Fragment, null, content));
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,15 +24,13 @@ 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]
29
- .map(fn => toTimePart(fn(dueDate)))
30
- .join('.');
27
+ if ((0, date_fns_1.getYear)(dueDate) !== (0, 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('.');
31
29
  content = `До ${text}`;
32
30
  }
33
31
  else {
34
- content = `До ${date_fns_1.getDate(dueDate)} ${MONTH_SHORT_NAME[date_fns_1.getMonth(dueDate)]}`;
32
+ content = `До ${(0, date_fns_1.getDate)(dueDate)} ${MONTH_SHORT_NAME[(0, date_fns_1.getMonth)(dueDate)]}`;
35
33
  }
36
- return (react_1.default.createElement(react_1.default.Fragment, null, content));
34
+ return react_1.default.createElement(react_1.default.Fragment, null, content);
37
35
  };
38
36
  exports.Estimate = Estimate;
@@ -1,14 +1,24 @@
1
1
  import React from 'react';
2
2
  import { PromotionType } from './types';
3
3
  export interface PromotionCardProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ /** Ссылка, переход по которой будет произведён при нажатии на карточку. */
4
5
  href?: string;
6
+ /** Заголовок, выводится под баннером. */
5
7
  title: string;
8
+ /** Подзаголовок. */
6
9
  subtitle?: string;
10
+ /** Дополнительный текст, выводится в правом нижнем углу. */
7
11
  postfix?: string;
12
+ /** Путь к изображению для баннера. */
8
13
  imageSrc: string;
14
+ /** Дата окончания акции. */
9
15
  dueDate: Date;
16
+ /** Тип акции. */
10
17
  promotionType?: PromotionType;
18
+ /** Скидка за объем, указывается при соответствующем типе акции. */
11
19
  volumeDiscount?: number;
20
+ /** Идентификатор для систем автоматизированного тестирования. */
21
+ 'data-testid'?: string;
12
22
  }
13
23
  /**
14
24
  * Карточка акции.
@@ -16,6 +26,6 @@ export interface PromotionCardProps extends React.HTMLAttributes<HTMLDivElement>
16
26
  * @return Элемент.
17
27
  */
18
28
  export declare const PromotionCard: {
19
- ({ 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;
20
30
  Placeholder: (props: React.HTMLAttributes<HTMLDivElement>) => JSX.Element;
21
31
  };