@sima-land/moleculas 13.4.0 → 14.2.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 (31) hide show
  1. package/common/components/intractive-image/index.d.ts +1 -1
  2. package/common/components/intractive-image/index.js +3 -4
  3. package/common/constants.d.ts +2 -5
  4. package/common/constants.js +2 -2
  5. package/common/types.d.ts +1 -0
  6. package/common/types.js +2 -0
  7. package/desktop/components/modifiers/button/index.d.ts +5 -3
  8. package/desktop/components/modifiers/button/index.js +5 -7
  9. package/desktop/components/modifiers/button/modifier-button.module.scss +4 -0
  10. package/desktop/components/modifiers/group/index.d.ts +11 -81
  11. package/desktop/components/modifiers/group/index.js +29 -133
  12. package/desktop/components/modifiers/group/modifiers-group.module.scss +19 -31
  13. package/desktop/components/modifiers/group/utils.d.ts +20 -0
  14. package/desktop/components/modifiers/group/utils.js +73 -0
  15. package/desktop/components/modifiers/modifiers.scss +5 -0
  16. package/desktop/components/product-carousel/index.js +1 -1
  17. package/mobile/components/modifiers-list/modifier-item/index.d.ts +2 -1
  18. package/mobile/components/modifiers-list/modifier-item/index.js +3 -3
  19. package/mobile/components/search-bar/index.d.ts +15 -17
  20. package/mobile/components/search-bar/index.js +3 -14
  21. package/package.json +1 -1
  22. package/desktop/components/item-image/index.d.ts +0 -88
  23. package/desktop/components/item-image/index.js +0 -135
  24. package/desktop/components/item-image/item-image.module.scss +0 -158
  25. package/desktop/components/modifiers/widget/index.d.ts +0 -18
  26. package/desktop/components/modifiers/widget/index.js +0 -36
  27. package/desktop/components/modifiers/widget/modifiers-widget.module.scss +0 -10
  28. package/desktop/components/modifiers/widget/readme.md +0 -53
  29. package/mobile/components/item-image/index.d.ts +0 -56
  30. package/mobile/components/item-image/index.js +0 -88
  31. package/mobile/components/item-image/item-image.module.scss +0 -79
@@ -11,7 +11,7 @@ export interface InteractiveImagePointProps extends React.AnchorHTMLAttributes<H
11
11
  y: number;
12
12
  'data-testid'?: string;
13
13
  }
14
- export declare const InteractiveImage: ({ children, "data-testid": testId, className, ...rest }: InteractiveImageProps) => JSX.Element;
14
+ export declare const InteractiveImage: React.ForwardRefExoticComponent<InteractiveImageProps & React.RefAttributes<HTMLDivElement>>;
15
15
  export declare const Parts: {
16
16
  readonly Image: React.ForwardRefExoticComponent<InteractiveImageImageProps & React.RefAttributes<HTMLImageElement>>;
17
17
  readonly Point: React.ForwardRefExoticComponent<InteractiveImagePointProps & React.RefAttributes<HTMLAnchorElement>>;
@@ -37,11 +37,10 @@ exports.Parts = exports.InteractiveImage = void 0;
37
37
  const react_1 = __importStar(require("react"));
38
38
  const classnames_1 = __importDefault(require("classnames"));
39
39
  const interactive_image_module_scss_1 = __importDefault(require("./interactive-image.module.scss"));
40
- const InteractiveImage = (_a) => {
40
+ exports.InteractiveImage = (0, react_1.forwardRef)((_a, ref) => {
41
41
  var { children, 'data-testid': testId, className } = _a, rest = __rest(_a, ["children", 'data-testid', "className"]);
42
- return (react_1.default.createElement("div", Object.assign({ className: (0, classnames_1.default)(interactive_image_module_scss_1.default.root, className) }, rest, { "data-testid": testId }), react_1.Children.toArray(children).filter(child => (0, react_1.isValidElement)(child) && (child.type === Image || child.type === Point))));
43
- };
44
- exports.InteractiveImage = InteractiveImage;
42
+ return (react_1.default.createElement("div", Object.assign({ ref: ref, className: (0, classnames_1.default)(interactive_image_module_scss_1.default.root, className) }, rest, { "data-testid": testId }), react_1.Children.toArray(children).filter(child => (0, react_1.isValidElement)(child) && (child.type === Image || child.type === Point))));
43
+ });
45
44
  const Image = (0, react_1.forwardRef)((_a, ref) => {
46
45
  var { className, 'data-testid': testId = 'interactive-image:image' } = _a, rest = __rest(_a, ["className", 'data-testid']);
47
46
  return (react_1.default.createElement("img", Object.assign({ ref: ref, className: (0, classnames_1.default)(interactive_image_module_scss_1.default.image, className), "data-testid": testId }, rest)));
@@ -1,5 +1,2 @@
1
- export declare const MODIFIERS_TYPES: {
2
- text: string;
3
- image: string;
4
- color: string;
5
- };
1
+ import { ModifierType } from './types';
2
+ export declare const MODIFIER_TYPE: Record<ModifierType, ModifierType>;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MODIFIERS_TYPES = void 0;
4
- exports.MODIFIERS_TYPES = {
3
+ exports.MODIFIER_TYPE = void 0;
4
+ exports.MODIFIER_TYPE = {
5
5
  text: 'text',
6
6
  image: 'image',
7
7
  color: 'color',
@@ -0,0 +1 @@
1
+ export declare type ModifierType = 'text' | 'image' | 'color';
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,15 +1,17 @@
1
- import React from 'react';
1
+ import React, { CSSProperties } from 'react';
2
+ import { ModifierType } from '../../../../common/types';
2
3
  export interface Modifier {
3
4
  count?: number;
4
5
  content: string;
5
6
  color?: string;
6
7
  image?: string;
7
- type?: 'text' | 'image';
8
+ type?: ModifierType;
8
9
  isMarkdown?: boolean;
9
10
  selected?: boolean;
10
11
  }
11
12
  export interface ModifierButtonProps extends Modifier {
12
13
  className?: string;
14
+ style?: CSSProperties;
13
15
  onClick?: React.MouseEventHandler<HTMLDivElement>;
14
16
  squared?: boolean;
15
17
  'data-testid'?: string;
@@ -17,4 +19,4 @@ export interface ModifierButtonProps extends Modifier {
17
19
  /**
18
20
  * Компонент модификатора.
19
21
  */
20
- export declare const ModifierButton: React.ForwardRefExoticComponent<ModifierButtonProps & React.RefAttributes<HTMLDivElement | null>>;
22
+ export declare const ModifierButton: React.ForwardRefExoticComponent<ModifierButtonProps & React.RefAttributes<HTMLDivElement>>;
@@ -29,18 +29,16 @@ const modifier_button_module_scss_1 = __importDefault(require("./modifier-button
29
29
  const bind_1 = __importDefault(require("classnames/bind"));
30
30
  const constants_1 = require("../../../../common/constants");
31
31
  const cx = bind_1.default.bind(modifier_button_module_scss_1.default);
32
- const typesList = Object.values(constants_1.MODIFIERS_TYPES);
33
32
  /**
34
33
  * Компонент модификатора.
35
34
  */
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
- const type = typesList.includes(typeProp) ? typeProp : 'text';
35
+ exports.ModifierButton = (0, react_1.forwardRef)(function ModifierButton({ count = null, content, selected, color, image, type = 'text', className, style, onClick, isMarkdown: hasMarkdown, squared = [constants_1.MODIFIER_TYPE.color, constants_1.MODIFIER_TYPE.image].includes(type), 'data-testid': testId = 'modifier-button', }, ref) {
38
36
  const hasCount = (0, lodash_1.isNumber)(count) && count >= 0;
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 },
37
+ return (react_1.default.createElement("div", { ref: ref, className: cx('modifier-button', squared && 'squared', selected && 'selected', className), style: color ? Object.assign(Object.assign({}, style), { background: color }) : style, onClick: onClick, "data-testid": testId },
40
38
  (hasCount || hasMarkdown) && (react_1.default.createElement("span", { className: cx('label', hasCount && 'with-count'), "data-testid": 'modifier-button:badge' },
41
39
  hasCount && (count > 99 ? '99+' : count),
42
40
  hasMarkdown && (hasCount ? '(У)' : 'У'))),
43
- [constants_1.MODIFIERS_TYPES.text, constants_1.MODIFIERS_TYPES.image].includes(type) && (react_1.default.createElement("div", { className: cx('content') },
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') })))));
41
+ [constants_1.MODIFIER_TYPE.text, constants_1.MODIFIER_TYPE.image].includes(type) && (react_1.default.createElement("div", { className: cx('content') },
42
+ type === constants_1.MODIFIER_TYPE.text && (react_1.default.createElement("span", { className: cx('text', !squared && 'guttered') }, String(content || ''))),
43
+ type === constants_1.MODIFIER_TYPE.image && react_1.default.createElement("img", { src: image, alt: content, className: cx('image') })))));
46
44
  });
@@ -6,6 +6,8 @@
6
6
  max-width: 100%;
7
7
  position: relative;
8
8
  display: inline-block;
9
+ font-size: 0;
10
+ line-height: 0;
9
11
  border-radius: 4px;
10
12
  min-width: $minSize;
11
13
  height: $minSize;
@@ -47,6 +49,8 @@
47
49
  }
48
50
 
49
51
  .text {
52
+ font-size: 14px;
53
+ line-height: 20px;
50
54
  text-overflow: ellipsis;
51
55
  white-space: nowrap;
52
56
  overflow: hidden;
@@ -1,85 +1,15 @@
1
- /// <reference types="lodash" />
2
- import React, { Component } from 'react';
3
- import { ModifierButtonProps, Modifier } from '../button';
4
- declare type ModifierPredicate<T> = (item: Modifier) => T;
5
- interface ModifiersGroupState {
6
- needShowAll: boolean;
7
- hasHiddenNodes: boolean;
8
- lastVisibleChildIndex: number | null;
9
- }
1
+ import { MouseEventHandler, ReactNode } from 'react';
10
2
  export interface ModifiersGroupProps {
11
- /** Список модификаторов. */
12
- items?: Modifier[];
13
- /** Флаг необходимости отображения всех модификаторов. */
14
- needShowAll?: boolean;
15
- /** Будет вызвана при выборе модификатора, получит сам модификатор. */
16
- onSelectItem?: (item: Modifier, selected: boolean) => void;
17
- /** Возвращает тип модификатора ("text" или "image", по умолчанию "text"). */
18
- getItemType?: ModifierPredicate<ModifierButtonProps['type']>;
19
- /** Возвращает число с количеством модификатора (по умолчанию свойство "count"). */
20
- getItemCount?: ModifierPredicate<number>;
21
- /** Возвращает цвет модификатора (по умолчанию свойство "color"). */
22
- getItemColor?: ModifierPredicate<string>;
23
- /** Возвращает содержимое модификатора (по умолчанию свойство "content"). */
24
- getItemContent?: ModifierPredicate<string>;
25
- /** Показывает, выбран ли модификатор (по умолчанию свойство "selected"). */
26
- isSelectedItem?: ModifierPredicate<boolean>;
27
- /** Возвращает URL изображения модификатора (по умолчанию свойство "image"). */
28
- getItemImage?: ModifierPredicate<string>;
29
- /** Имеет ли товар уценку. */
30
- isMarkdown?: ModifierPredicate<boolean>;
31
- /** Сработает при нажатии на кнопку отображения всех модификаторов. */
32
- onClickShowAll?: () => void;
33
- /** Идентификатор для систем автоматизированного тестирования. */
34
- 'data-testid'?: string;
3
+ /** Кнопки модификаторов. */
4
+ children?: ReactNode;
5
+ /** Надо ли сразу показывать весь список не ограничивая двумя строками. */
6
+ defaultOpened?: boolean;
7
+ /** Будет вызвана после нажатия на кнопку показа всех модификаторов. */
8
+ onShowAll?: MouseEventHandler<HTMLDivElement>;
35
9
  }
36
10
  /**
37
- * Создает функцию, находящую индекс элемента, который не должен быть виден.
38
- * @param container Контейнер.
39
- * @return Функция.
40
- */
41
- export declare const createHiddenNodeIndexFinder: (container: HTMLElement) => (childNode: HTMLElement) => boolean;
42
- /**
43
- * Компонент списка модификаторов.
11
+ * Блок списка кнопок модификаторов.
12
+ * @param props Свойства.
13
+ * @return Элемент.
44
14
  */
45
- export declare class ModifiersGroup extends Component<ModifiersGroupProps, ModifiersGroupState> {
46
- removeGlobalListener: any;
47
- state: {
48
- needShowAll: boolean;
49
- hasHiddenNodes: boolean;
50
- lastVisibleChildIndex: null;
51
- };
52
- windowWidth: number;
53
- containerRef: React.RefObject<HTMLDivElement>;
54
- showingButtonRef: React.RefObject<HTMLDivElement>;
55
- /**
56
- * @inheritdoc
57
- */
58
- componentDidMount(): void;
59
- /**
60
- * @inheritdoc
61
- */
62
- componentDidUpdate(): void;
63
- /**
64
- * @inheritdoc
65
- */
66
- componentWillUnmount(): void;
67
- /**
68
- * Сбрасывает информацию о видимых и невидимых дочерних узлах контейнера.
69
- */
70
- resetVisibleNodesData: import("lodash").DebouncedFunc<() => void>;
71
- /**
72
- * Обновляет информацию о видимых и невидимых дочерних узлах контейнера,
73
- * необходимую для показа кнопки раскрытия полного списка модификаторов.
74
- */
75
- checkVisibleNodes: () => void;
76
- /**
77
- * Обработчик нажатия на кнопку отображения всех модификаторов.
78
- */
79
- handleShowAllClick: () => void;
80
- /**
81
- * @inheritdoc
82
- */
83
- render(): JSX.Element;
84
- }
85
- export {};
15
+ export declare const ModifiersGroup: ({ children, defaultOpened, onShowAll, }: ModifiersGroupProps) => JSX.Element;
@@ -22,144 +22,40 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.ModifiersGroup = exports.createHiddenNodeIndexFinder = void 0;
25
+ exports.ModifiersGroup = void 0;
26
26
  const react_1 = __importStar(require("react"));
27
- const lodash_1 = require("lodash");
28
- const prop_1 = __importDefault(require("lodash/fp/prop"));
29
27
  const button_1 = require("../button");
30
- const on_1 = __importDefault(require("@sima-land/ui-nucleons/helpers/on"));
31
- const modifiers_group_module_scss_1 = __importDefault(require("./modifiers-group.module.scss"));
28
+ const utils_1 = require("./utils");
32
29
  const bind_1 = __importDefault(require("classnames/bind"));
30
+ const modifiers_group_module_scss_1 = __importDefault(require("./modifiers-group.module.scss"));
33
31
  const cx = bind_1.default.bind(modifiers_group_module_scss_1.default);
34
32
  /**
35
- * Внешний отступ кнопки.
36
- */
37
- const MODIFIER_GUTTER = 4;
38
- /**
39
- * Создает функцию, находящую индекс элемента, который не должен быть виден.
40
- * @param container Контейнер.
41
- * @return Функция.
33
+ * Блок списка кнопок модификаторов.
34
+ * @param props Свойства.
35
+ * @return Элемент.
42
36
  */
43
- const createHiddenNodeIndexFinder = (container) => (childNode) => {
44
- let childOffsetTop = childNode.offsetTop;
45
- if (childNode.classList.contains(cx('invisible'))) {
46
- childOffsetTop -= MODIFIER_GUTTER;
47
- }
48
- return childOffsetTop - container.offsetTop >= container.clientHeight;
37
+ const ModifiersGroup = ({ children, defaultOpened = false, onShowAll, }) => {
38
+ const [opened, setOpened] = (0, react_1.useState)(defaultOpened);
39
+ const rootRef = (0, react_1.useRef)(null);
40
+ const openerRef = (0, react_1.useRef)(null);
41
+ const showAll = (0, react_1.useCallback)(event => {
42
+ setOpened(true);
43
+ onShowAll === null || onShowAll === void 0 ? void 0 : onShowAll(event);
44
+ }, [onShowAll]);
45
+ const viewState = (0, utils_1.useViewState)(rootRef, openerRef);
46
+ const items = react_1.Children.toArray(children).reduce((result, child, index) => {
47
+ if ((0, react_1.isValidElement)(child) && child.type === button_1.ModifierButton) {
48
+ const needHideItems = viewState.lastVisibleIndex !== -1 && !opened;
49
+ const isDisplayed = !needHideItems || index < viewState.lastVisibleIndex;
50
+ result.push(react_1.default.createElement(GroupItem, { key: index, hidden: !isDisplayed }, child));
51
+ }
52
+ return result;
53
+ }, []);
54
+ return (react_1.default.createElement("div", { ref: rootRef, className: cx('root', { opened }) },
55
+ items,
56
+ !opened && (react_1.default.createElement(GroupItem, { ref: openerRef, invisible: viewState.lastVisibleIndex === -1 },
57
+ react_1.default.createElement(OpenButton, { count: items.length - viewState.lastVisibleIndex, onClick: showAll })))));
49
58
  };
50
- exports.createHiddenNodeIndexFinder = createHiddenNodeIndexFinder;
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
- /**
59
- * Компонент списка модификаторов.
60
- */
61
- class ModifiersGroup extends react_1.Component {
62
- constructor() {
63
- super(...arguments);
64
- this.state = {
65
- // нажата ли кнопка показа всех модификаторов
66
- needShowAll: this.props.needShowAll || false,
67
- // есть ли дочерние узлы контейнера, которые скрыты из за ограничения его высоты
68
- hasHiddenNodes: true,
69
- // индекс последнего видимого элемента в ограниченном по высоте контейнере
70
- lastVisibleChildIndex: null,
71
- };
72
- this.windowWidth = 0;
73
- this.containerRef = (0, react_1.createRef)();
74
- this.showingButtonRef = (0, react_1.createRef)();
75
- /**
76
- * Сбрасывает информацию о видимых и невидимых дочерних узлах контейнера.
77
- */
78
- this.resetVisibleNodesData = (0, lodash_1.debounce)(() => {
79
- if (document.documentElement.offsetWidth !== this.windowWidth) {
80
- this.windowWidth = document.documentElement.offsetWidth;
81
- this.setState({
82
- hasHiddenNodes: true,
83
- lastVisibleChildIndex: null,
84
- });
85
- }
86
- }, 250, { leading: true });
87
- /**
88
- * Обновляет информацию о видимых и невидимых дочерних узлах контейнера,
89
- * необходимую для показа кнопки раскрытия полного списка модификаторов.
90
- */
91
- this.checkVisibleNodes = () => {
92
- // если не указан индекс последнего видимого узла
93
- if (this.state.lastVisibleChildIndex === null) {
94
- const containerEl = this.containerRef.current;
95
- const showingButtonEl = this.showingButtonRef.current;
96
- if (containerEl && showingButtonEl) {
97
- const childList = [...containerEl.children];
98
- const firstHiddenNodeIndex = childList.findIndex((0, exports.createHiddenNodeIndexFinder)(containerEl));
99
- if (firstHiddenNodeIndex !== -1) {
100
- const lastVisibleElIndex = firstHiddenNodeIndex - 1;
101
- const lastVisibleNode = childList[lastVisibleElIndex];
102
- if (lastVisibleNode) {
103
- const rightBound = lastVisibleNode.getBoundingClientRect().right;
104
- const rightContainerBound = containerEl.getBoundingClientRect().right;
105
- // проверяем, хватает ли кнопке места (с запасом в половину) после последнего видимого дочернего элемента
106
- const isShowingButtonFit = rightContainerBound - rightBound >= showingButtonEl.clientWidth * 1.5;
107
- this.setState({
108
- // если хватает места - ставим кнопку после последнего, иначе - вместо
109
- lastVisibleChildIndex: lastVisibleElIndex + (isShowingButtonFit ? 1 : 0),
110
- hasHiddenNodes: true,
111
- });
112
- }
113
- }
114
- else {
115
- this.setState({ hasHiddenNodes: false });
116
- }
117
- }
118
- }
119
- };
120
- /**
121
- * Обработчик нажатия на кнопку отображения всех модификаторов.
122
- */
123
- this.handleShowAllClick = () => {
124
- const { onClickShowAll } = this.props;
125
- this.setState({ needShowAll: true });
126
- onClickShowAll && onClickShowAll();
127
- };
128
- }
129
- /**
130
- * @inheritdoc
131
- */
132
- componentDidMount() {
133
- this.windowWidth = document.documentElement.offsetWidth;
134
- this.removeGlobalListener = (0, on_1.default)(window, 'resize', this.resetVisibleNodesData);
135
- this.checkVisibleNodes();
136
- }
137
- /**
138
- * @inheritdoc
139
- */
140
- componentDidUpdate() {
141
- this.checkVisibleNodes();
142
- }
143
- /**
144
- * @inheritdoc
145
- */
146
- componentWillUnmount() {
147
- this.removeGlobalListener();
148
- }
149
- /**
150
- * @inheritdoc
151
- */
152
- render() {
153
- const { items, onSelectItem, getItemType = defaultGetItemType, getItemCount = defaultGetItemCount, getItemColor = defaultGetItemColor, getItemContent = defaultGetItemContent, isSelectedItem = defaultIsSelectedItem, getItemImage = defaultGetItemImage, isMarkdown = defaultGetIsMarkdown, 'data-testid': testId = 'modifiers-group', } = this.props;
154
- const { needShowAll, hasHiddenNodes, lastVisibleChildIndex } = this.state;
155
- const needHideModifiers = !needShowAll && lastVisibleChildIndex !== null;
156
- return (react_1.default.createElement("div", { className: cx('modifiers-group', !needShowAll && 'height-limited'), ref: this.containerRef, "data-testid": testId },
157
- Array.isArray(items) &&
158
- items.map((item, index) => {
159
- const isDisplayed = !needHideModifiers || index < lastVisibleChildIndex;
160
- 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) }));
161
- }),
162
- 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' }))));
163
- }
164
- }
165
59
  exports.ModifiersGroup = ModifiersGroup;
60
+ const GroupItem = (0, react_1.forwardRef)(({ children, hidden, invisible }, ref) => (react_1.default.createElement("div", { ref: ref, className: cx('item', { hidden, invisible }), "data-testid": 'modifier-group:item' }, children)));
61
+ const OpenButton = ({ count, onClick }) => (react_1.default.createElement(button_1.ModifierButton, { squared: true, onClick: onClick, content: `+${count}`, className: cx('opener'), "data-testid": 'modifier-group:show-all-button' }));
@@ -1,43 +1,31 @@
1
1
  @use 'node_modules/@sima-land/ui-nucleons/colors';
2
- @import '../modifiers';
2
+ @use '../modifiers';
3
3
 
4
- .modifiers-group {
4
+ .root {
5
+ position: relative;
5
6
  display: flex;
6
7
  flex-wrap: wrap;
7
- margin: -$gutter;
8
+ flex-direction: row;
9
+ margin: -#{modifiers.$gutter};
10
+ font-size: 0;
8
11
  overflow: hidden;
9
- &.height-limited {
10
- max-height: $groupMaxHeight;
12
+ &:not(.opened) {
13
+ max-height: modifiers.$groupMaxHeight;
11
14
  }
12
15
  }
13
16
 
14
- .modifiers-title {
15
- font-size: 14px;
16
- font-weight: normal;
17
- margin: 0 0 16px;
18
- }
19
-
20
- .modifiers-link {
21
- display: inline-block;
22
- margin: 16px 0;
23
- }
24
-
25
- .guttered {
26
- margin: $gutter;
17
+ .item {
18
+ height: modifiers.$minSize;
19
+ margin: modifiers.$gutter;
20
+ max-width: 100%;
21
+ &.hidden {
22
+ display: none;
23
+ }
24
+ &.invisible {
25
+ opacity: 0;
26
+ }
27
27
  }
28
28
 
29
- .show-all-button {
30
- width: $minSize;
29
+ .opener {
31
30
  color: colors.$basic-blue;
32
31
  }
33
-
34
- .not-display {
35
- display: none;
36
- }
37
-
38
- .invisible {
39
- opacity: 0;
40
- height: 0;
41
- margin-top: 0;
42
- margin-bottom: 0;
43
- }
@@ -0,0 +1,20 @@
1
+ import { RefObject } from 'react';
2
+ export interface ViewState {
3
+ phase: 'default' | 'ready';
4
+ lastVisibleIndex: number;
5
+ }
6
+ export declare const initialViewState: ViewState;
7
+ /**
8
+ * Хук состояния отображения блока модификаторов.
9
+ * @param containerRef Ref элемента-контейнера.
10
+ * @param openerRef Ref элемента кнопки, показывающей весь список модификаторов.
11
+ * @return Состояние.
12
+ */
13
+ export declare function useViewState(containerRef: RefObject<HTMLElement>, openerRef: RefObject<HTMLElement>): ViewState;
14
+ /**
15
+ * Определяет индекс последней видимой кнопки-модификатора для свернутого списка модификаторов.
16
+ * @param containerRef Ref элемента-контейнера.
17
+ * @param openerRef Ref элемента кнопки, показывающей весь список модификаторов.
18
+ * @return Состояние.
19
+ */
20
+ export declare function defineViewState(containerRef: RefObject<HTMLElement>, openerRef: RefObject<HTMLElement>): ViewState;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.defineViewState = exports.useViewState = exports.initialViewState = void 0;
7
+ const react_1 = require("react");
8
+ const hooks_1 = require("@sima-land/ui-nucleons/hooks");
9
+ const on_1 = __importDefault(require("@sima-land/ui-nucleons/helpers/on"));
10
+ exports.initialViewState = {
11
+ phase: 'default',
12
+ lastVisibleIndex: -1,
13
+ };
14
+ /**
15
+ * Хук состояния отображения блока модификаторов.
16
+ * @param containerRef Ref элемента-контейнера.
17
+ * @param openerRef Ref элемента кнопки, показывающей весь список модификаторов.
18
+ * @return Состояние.
19
+ */
20
+ function useViewState(containerRef, openerRef) {
21
+ const [viewState, setViewState] = (0, react_1.useState)(exports.initialViewState);
22
+ (0, react_1.useEffect)(() => {
23
+ const off = (0, on_1.default)(window, 'resize', () => {
24
+ setViewState(exports.initialViewState);
25
+ });
26
+ return off;
27
+ }, []);
28
+ (0, hooks_1.useIsomorphicLayoutEffect)(() => {
29
+ if (viewState.phase === 'default') {
30
+ setViewState(defineViewState(containerRef, openerRef));
31
+ }
32
+ }, [viewState.phase, containerRef, openerRef]);
33
+ return viewState;
34
+ }
35
+ exports.useViewState = useViewState;
36
+ /**
37
+ * Определяет индекс последней видимой кнопки-модификатора для свернутого списка модификаторов.
38
+ * @param containerRef Ref элемента-контейнера.
39
+ * @param openerRef Ref элемента кнопки, показывающей весь список модификаторов.
40
+ * @return Состояние.
41
+ */
42
+ function defineViewState(containerRef, openerRef) {
43
+ const state = {
44
+ phase: 'ready',
45
+ lastVisibleIndex: -1,
46
+ };
47
+ const container = containerRef.current;
48
+ const opener = openerRef.current;
49
+ if (container && opener) {
50
+ const parentRect = container.getBoundingClientRect();
51
+ const childList = [...container.children];
52
+ const firstHiddenNodeIndex = childList
53
+ .filter(child => child !== opener)
54
+ .findIndex(child => {
55
+ const childRect = child.getBoundingClientRect();
56
+ return childRect.top - parentRect.top >= parentRect.height;
57
+ });
58
+ if (firstHiddenNodeIndex !== -1) {
59
+ const lastVisibleIndex = firstHiddenNodeIndex - 1;
60
+ const lastVisible = childList[lastVisibleIndex];
61
+ if (lastVisible) {
62
+ const rightBound = lastVisible.getBoundingClientRect().right;
63
+ const rightContainerBound = container.getBoundingClientRect().right;
64
+ // проверяем, хватает ли кнопке места (с запасом в половину) после последнего видимого дочернего элемента
65
+ const isOpenerFit = rightContainerBound - rightBound >= opener.clientWidth * 1.5;
66
+ // если хватает места - ставим кнопку после последнего, иначе - вместо
67
+ state.lastVisibleIndex = lastVisibleIndex + Number(isOpenerFit);
68
+ }
69
+ }
70
+ }
71
+ return state;
72
+ }
73
+ exports.defineViewState = defineViewState;
@@ -1,3 +1,8 @@
1
+ // внешний отступ вокруг каждого элемента группы
1
2
  $gutter: 4px;
3
+
4
+ // размер квадратного модификатора
2
5
  $minSize: 48px;
6
+
7
+ // максимальная высота блока с ограничением в 2 строки
3
8
  $groupMaxHeight: ($minSize * 2) + ($gutter * 4);
@@ -71,7 +71,7 @@ const ProductCarousel = ({ className, itemSize, onInViewport, onNeedRequest, wit
71
71
  items.length > 0 && (react_1.default.createElement(carousel_1.Carousel, Object.assign({ step: 3, draggable: false,
72
72
  // докидываем индекс чтобы позже брать актуальные данные из списка по нему
73
73
  items: items.map((item, index) => [item, index]) }, (itemWidth !== null && {
74
- withControls: true,
74
+ withControls: undefined,
75
75
  controlProps: {
76
76
  size: needBigArrows ? 'l' : 's',
77
77
  style: {
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
+ import { ModifierType } from '../../../../common/types';
2
3
  export interface ModifierItemProps {
3
4
  name: string;
4
5
  selected?: boolean;
5
- type?: 'text' | 'image' | 'color';
6
+ type?: ModifierType;
6
7
  color?: string;
7
8
  image?: string;
8
9
  price: number;
@@ -7,10 +7,10 @@ exports.ModifierItem = exports.cx = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const modifier_item_module_scss_1 = __importDefault(require("./modifier-item.module.scss"));
9
9
  const bind_1 = __importDefault(require("classnames/bind"));
10
- const constants_1 = require("../../../../common/constants");
11
10
  const price_1 = require("@sima-land/ui-nucleons/price");
12
11
  const text_1 = require("@sima-land/ui-nucleons/text");
13
12
  const check_1 = __importDefault(require("@sima-land/ui-quarks/icons/24x24/Stroked/check"));
13
+ const constants_1 = require("../../../../common/constants");
14
14
  exports.cx = bind_1.default.bind(modifier_item_module_scss_1.default);
15
15
  /**
16
16
  * Компонент модификатора в списке.
@@ -27,8 +27,8 @@ exports.cx = bind_1.default.bind(modifier_item_module_scss_1.default);
27
27
  * @return Элемент.
28
28
  */
29
29
  const ModifierItem = ({ name, selected, type = 'text', color, image, price, currencyGrapheme, additionalText, onClick, }) => (react_1.default.createElement("div", { onClick: onClick, className: (0, exports.cx)('wrapper', selected && 'selected') },
30
- Boolean(type === constants_1.MODIFIERS_TYPES.image && image) && (react_1.default.createElement("img", { className: (0, exports.cx)('adornment'), src: image, alt: name })),
31
- Boolean(type === constants_1.MODIFIERS_TYPES.color && color) && (react_1.default.createElement("div", { className: (0, exports.cx)('adornment', 'color'), style: { backgroundColor: color } })),
30
+ Boolean(type === constants_1.MODIFIER_TYPE.image && image) && (react_1.default.createElement("img", { className: (0, exports.cx)('adornment'), src: image, alt: name })),
31
+ Boolean(type === constants_1.MODIFIER_TYPE.color && color) && (react_1.default.createElement("div", { className: (0, exports.cx)('adornment', 'color'), style: { backgroundColor: color } })),
32
32
  react_1.default.createElement("div", { className: (0, exports.cx)('info') },
33
33
  react_1.default.createElement("span", { className: (0, exports.cx)('name') }, name),
34
34
  Boolean(price) && (react_1.default.createElement(price_1.Price, { value: price, currencyGrapheme: currencyGrapheme, className: (0, exports.cx)('price', additionalText && 'additional') })),