@doyourjob/gravity-ui-page-constructor 5.31.253 → 5.31.256

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.
@@ -1,11 +1,16 @@
1
1
  import React from 'react';
2
2
  import { LogoRotatorBlockProps } from '../../models';
3
+ import type { LogoRotatorSwapAnimation } from './utils';
3
4
  type Props = {
4
5
  src: string;
5
6
  url?: string;
6
- hidden: boolean;
7
+ previousSrc?: string;
8
+ previousUrl?: string;
9
+ swapAnimation: LogoRotatorSwapAnimation;
7
10
  colSizes: LogoRotatorBlockProps['colSizes'];
11
+ onMouseEnter?: () => void;
12
+ onMouseLeave?: () => void;
8
13
  };
9
- export declare const Item: ({ url, src, hidden, colSizes }: Props) => JSX.Element;
10
- declare const _default: React.MemoExoticComponent<({ url, src, hidden, colSizes }: Props) => JSX.Element>;
14
+ export declare const Item: ({ url, src, previousSrc, previousUrl, swapAnimation, colSizes, onMouseEnter, onMouseLeave, }: Props) => JSX.Element;
15
+ declare const _default: React.MemoExoticComponent<({ url, src, previousSrc, previousUrl, swapAnimation, colSizes, onMouseEnter, onMouseLeave, }: Props) => JSX.Element>;
11
16
  export default _default;
@@ -7,18 +7,23 @@ const uikit_1 = require("@gravity-ui/uikit");
7
7
  const components_1 = require("../../components");
8
8
  const grid_1 = require("../../grid");
9
9
  const utils_1 = require("../../utils");
10
+ const utils_2 = require("./utils");
10
11
  const b = (0, utils_1.block)('logo-rotator-block');
11
12
  const defaultColSizes = { all: 3 };
12
- const Item = ({ url, src, hidden, colSizes }) => {
13
- const renderItem = react_1.default.useMemo(() => {
14
- if (url) {
15
- return (react_1.default.createElement(uikit_1.Link, { href: url, className: b('item', { hidden }) },
16
- react_1.default.createElement(components_1.Image, { src: src, className: b('image'), alt: "", "aria-hidden": "true" })));
13
+ const getLayerClassName = (layer, swapAnimation, link = false) => `${link ? `${b('item-link')} ` : ''}${b('logo-layer', (0, utils_2.getLayerModifiers)(layer, swapAnimation))}`;
14
+ const Item = ({ url, src, previousSrc, previousUrl, swapAnimation, colSizes, onMouseEnter, onMouseLeave, }) => {
15
+ const renderLayer = (layerSrc, layer, layerUrl) => {
16
+ const image = react_1.default.createElement(components_1.Image, { src: layerSrc, className: b('image'), alt: "", "aria-hidden": "true" });
17
+ if (layerUrl) {
18
+ return (react_1.default.createElement(uikit_1.Link, { href: layerUrl, className: getLayerClassName(layer, swapAnimation, true) }, image));
17
19
  }
18
- return (react_1.default.createElement("div", { className: b('item', { hidden }) },
19
- react_1.default.createElement(components_1.Image, { src: src, className: b('image'), alt: "", "aria-hidden": "true" })));
20
- }, [hidden, src, url]);
21
- return react_1.default.createElement(grid_1.Col, { sizes: colSizes || defaultColSizes }, renderItem);
20
+ return react_1.default.createElement("div", { className: getLayerClassName(layer, swapAnimation) }, image);
21
+ };
22
+ const isSwapping = Boolean(previousSrc);
23
+ return (react_1.default.createElement(grid_1.Col, { sizes: colSizes || defaultColSizes },
24
+ react_1.default.createElement("div", { className: b('item', { swapping: isSwapping }), onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave },
25
+ previousSrc && renderLayer(previousSrc, 'from', previousUrl),
26
+ renderLayer(src, previousSrc ? 'to' : 'current', url))));
22
27
  };
23
28
  exports.Item = Item;
24
29
  exports.default = react_1.default.memo(exports.Item);
@@ -30,19 +30,44 @@ unpredictable css rules order in build */
30
30
  gap: 16px;
31
31
  }
32
32
  .pc-logo-rotator-block__item {
33
+ position: relative;
33
34
  display: block;
34
35
  height: 100px;
35
- transition: opacity 0.5s ease 0.1s;
36
- opacity: 1;
37
- }
38
- .pc-logo-rotator-block__item_hidden {
39
- opacity: 0;
36
+ overflow: hidden;
40
37
  }
41
38
  @media (max-width: 769px) {
42
39
  .pc-logo-rotator-block__item {
43
40
  height: 80px;
44
41
  }
45
42
  }
43
+ .pc-logo-rotator-block__item-link, .pc-logo-rotator-block__row-item-link {
44
+ display: block;
45
+ height: 100%;
46
+ }
47
+ .pc-logo-rotator-block__logo-layer {
48
+ display: block;
49
+ width: 100%;
50
+ height: 100%;
51
+ }
52
+ .pc-logo-rotator-block__item_swapping .pc-logo-rotator-block__logo-layer, .pc-logo-rotator-block__row-item_swapping .pc-logo-rotator-block__logo-layer {
53
+ position: absolute;
54
+ inset: 0;
55
+ }
56
+ .pc-logo-rotator-block__logo-layer_from {
57
+ pointer-events: none;
58
+ }
59
+ .pc-logo-rotator-block__logo-layer_fade-from {
60
+ animation: logo-rotator-fade-out 0.5s ease 0.1s both;
61
+ }
62
+ .pc-logo-rotator-block__logo-layer_fade-to {
63
+ animation: logo-rotator-fade-in 0.5s ease 0.6s both;
64
+ }
65
+ .pc-logo-rotator-block__logo-layer_morph-from {
66
+ animation: logo-rotator-morph-out 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
67
+ }
68
+ .pc-logo-rotator-block__logo-layer_morph-to {
69
+ animation: logo-rotator-morph-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
70
+ }
46
71
  .pc-logo-rotator-block__image {
47
72
  display: block;
48
73
  width: 100%;
@@ -57,13 +82,57 @@ unpredictable css rules order in build */
57
82
  gap: 46px 16px;
58
83
  }
59
84
  .pc-logo-rotator-block__row-item {
85
+ position: relative;
60
86
  display: block;
61
87
  height: 36px;
62
88
  min-width: 160px;
63
89
  flex: 1;
64
- transition: opacity 0.5s ease 0.1s;
65
- opacity: 1;
90
+ overflow: hidden;
66
91
  }
67
- .pc-logo-rotator-block__row-item_hidden {
68
- opacity: 0;
92
+
93
+ @keyframes logo-rotator-fade-out {
94
+ from {
95
+ opacity: 1;
96
+ }
97
+ to {
98
+ opacity: 0;
99
+ }
100
+ }
101
+ @keyframes logo-rotator-fade-in {
102
+ from {
103
+ opacity: 0;
104
+ }
105
+ to {
106
+ opacity: 1;
107
+ }
108
+ }
109
+ @keyframes logo-rotator-morph-out {
110
+ from {
111
+ clip-path: inset(0 0 0 0);
112
+ transform: translateX(0) scaleX(1);
113
+ opacity: 1;
114
+ }
115
+ to {
116
+ clip-path: inset(0 100% 0 0);
117
+ transform: translateX(-8%) scaleX(0.9);
118
+ opacity: 0;
119
+ }
120
+ }
121
+ @keyframes logo-rotator-morph-in {
122
+ from {
123
+ clip-path: inset(0 0 0 100%);
124
+ transform: translateX(8%) scaleX(0.9);
125
+ opacity: 0;
126
+ }
127
+ to {
128
+ clip-path: inset(0 0 0 0);
129
+ transform: translateX(0) scaleX(1);
130
+ opacity: 1;
131
+ }
132
+ }
133
+ @media (prefers-reduced-motion: reduce) {
134
+ .pc-logo-rotator-block__logo-layer_fade-from, .pc-logo-rotator-block__logo-layer_fade-to, .pc-logo-rotator-block__logo-layer_morph-from, .pc-logo-rotator-block__logo-layer_morph-to {
135
+ animation-duration: 1ms;
136
+ animation-delay: 0ms;
137
+ }
69
138
  }
@@ -6,12 +6,16 @@ const react_1 = tslib_1.__importStar(require("react"));
6
6
  const uikit_1 = require("@gravity-ui/uikit");
7
7
  const components_1 = require("../../components");
8
8
  const AnimateBlock_1 = tslib_1.__importDefault(require("../../components/AnimateBlock/AnimateBlock"));
9
+ const constants_1 = require("../../constants");
9
10
  const grid_1 = require("../../grid");
11
+ const useWindowBreakpoint_1 = tslib_1.__importDefault(require("../../hooks/useWindowBreakpoint"));
10
12
  const utils_1 = require("../../utils");
11
13
  const Item_1 = tslib_1.__importDefault(require("./Item"));
14
+ const utils_2 = require("./utils");
12
15
  const b = (0, utils_1.block)('logo-rotator-block');
13
16
  const DEFAULT_MIN_ROTATE_COUNT = 2;
14
17
  const DEFAULT_MAX_ROTATE_COUNT = 4;
18
+ const emptyTransitions = (count) => Array(count).fill(undefined);
15
19
  const pickRandomSlots = (slotIndices, count) => {
16
20
  const shuffled = [...slotIndices];
17
21
  for (let i = shuffled.length - 1; i > 0; i--) {
@@ -21,46 +25,47 @@ const pickRandomSlots = (slotIndices, count) => {
21
25
  return shuffled.slice(0, count);
22
26
  };
23
27
  const LogoRotatorBlock = (props) => {
24
- const { animated, title, theme, items, count, minRotateCount = DEFAULT_MIN_ROTATE_COUNT, maxRotateCount = DEFAULT_MAX_ROTATE_COUNT, colSizes, rowMode, } = props;
28
+ const { animated, title, theme, items, countMobile, countDesktop, minRotateCount = DEFAULT_MIN_ROTATE_COUNT, maxRotateCount = DEFAULT_MAX_ROTATE_COUNT, swapAnimation = utils_2.DEFAULT_SWAP_ANIMATION, colSizes, rowMode, } = props;
29
+ const breakpoint = (0, useWindowBreakpoint_1.default)();
30
+ const activeCount = countDesktop !== undefined && breakpoint >= constants_1.BREAKPOINTS.md ? countDesktop : countMobile;
25
31
  // Индексы логотипов, которые участвуют в ротации (не статичные)
26
32
  const rotatableIndices = (0, react_1.useMemo)(() => items.map((item, i) => (item.isStatic ? -1 : i)).filter((i) => i !== -1), [items]);
27
33
  // Инициализация слотов: статичные вставляются в начало, остальные по порядку
28
- const [slots, setSlots] = (0, react_1.useState)(() => {
29
- var _a;
30
- const staticIdxList = items
31
- .map((item, i) => (item.isStatic ? i : -1))
32
- .filter((i) => i !== -1);
33
- const rotatableIdxList = items
34
- .map((item, i) => (item.isStatic ? -1 : i))
35
- .filter((i) => i !== -1);
36
- const initial = [];
37
- let rotatablePointer = 0;
38
- for (let slot = 0; slot < count; slot++) {
39
- if (slot < staticIdxList.length) {
40
- initial.push(staticIdxList[slot]);
41
- }
42
- else {
43
- initial.push((_a = rotatableIdxList[rotatablePointer++]) !== null && _a !== void 0 ? _a : 0);
44
- }
45
- }
46
- return initial;
47
- });
48
- const [hidden, setHidden] = (0, react_1.useState)(() => Array(count).fill(false));
49
- const nextIndexRef = (0, react_1.useRef)(count - 1);
50
- const isHoveredRef = (0, react_1.useRef)(false);
34
+ const [slots, setSlots] = (0, react_1.useState)(() => (0, utils_2.getInitialSlots)(items, activeCount));
35
+ const [transitions, setTransitions] = (0, react_1.useState)(() => emptyTransitions(activeCount));
36
+ const nextIndexRef = (0, react_1.useRef)(activeCount - 1);
37
+ const hoveredSlotIndicesRef = (0, react_1.useRef)(new Set());
51
38
  // Держим актуальные slots в ref, чтобы не пересоздавать интервал при каждом изменении
52
39
  const slotsRef = (0, react_1.useRef)(slots);
53
40
  (0, react_1.useEffect)(() => {
54
41
  slotsRef.current = slots;
55
42
  }, [slots]);
43
+ (0, react_1.useEffect)(() => {
44
+ const initialSlots = (0, utils_2.getInitialSlots)(items, activeCount);
45
+ slotsRef.current = initialSlots;
46
+ setSlots(initialSlots);
47
+ setTransitions(emptyTransitions(activeCount));
48
+ nextIndexRef.current = activeCount - 1;
49
+ hoveredSlotIndicesRef.current.clear();
50
+ }, [activeCount, items]);
51
+ const handleSlotMouseEnter = (0, react_1.useCallback)((slotIndex) => {
52
+ hoveredSlotIndicesRef.current.add(slotIndex);
53
+ }, []);
54
+ const handleSlotMouseLeave = (0, react_1.useCallback)((slotIndex) => {
55
+ hoveredSlotIndicesRef.current.delete(slotIndex);
56
+ }, []);
56
57
  (0, react_1.useEffect)(() => {
57
58
  let timeout = null;
58
59
  const interval = setInterval(() => {
59
- if (isHoveredRef.current)
60
- return;
61
- // Выбираем только не-статичные слоты для замены
62
- const rotatableSlotIndices = slotsRef.current
63
- .map((itemIdx, slotIdx) => { var _a; return (((_a = items[itemIdx]) === null || _a === void 0 ? void 0 : _a.isStatic) ? -1 : slotIdx); })
60
+ const currentSlots = slotsRef.current;
61
+ // Выбираем только не-статичные слоты, на которые не наведен указатель
62
+ const rotatableSlotIndices = currentSlots
63
+ .map((itemIdx, slotIdx) => {
64
+ var _a;
65
+ return ((_a = items[itemIdx]) === null || _a === void 0 ? void 0 : _a.isStatic) || hoveredSlotIndicesRef.current.has(slotIdx)
66
+ ? -1
67
+ : slotIdx;
68
+ })
64
69
  .filter((i) => i !== -1);
65
70
  if (rotatableSlotIndices.length === 0)
66
71
  return;
@@ -68,35 +73,56 @@ const LogoRotatorBlock = (props) => {
68
73
  const rotateMax = Math.max(minRotateCount, maxRotateCount);
69
74
  const rotateCount = rotateMin + Math.floor(Math.random() * (rotateMax - rotateMin + 1));
70
75
  const slotIndices = pickRandomSlots(rotatableSlotIndices, Math.min(rotateCount, rotatableSlotIndices.length));
71
- setHidden((prev) => {
76
+ let available = rotatableIndices.filter((i) => !currentSlots.includes(i));
77
+ const blockedTargetSrcs = new Set();
78
+ const nextTransitions = [];
79
+ slotIndices.forEach((slotIndex) => {
80
+ var _a, _b;
81
+ if (available.length === 0)
82
+ return;
83
+ const currentItemIndex = currentSlots[slotIndex];
84
+ const currentSrc = (_a = items[currentItemIndex]) === null || _a === void 0 ? void 0 : _a.src;
85
+ const slotAvailable = available.filter((i) => {
86
+ var _a;
87
+ const src = (_a = items[i]) === null || _a === void 0 ? void 0 : _a.src;
88
+ return src !== currentSrc && !blockedTargetSrcs.has(src);
89
+ });
90
+ if (slotAvailable.length === 0)
91
+ return;
92
+ const newValue = slotAvailable[nextIndexRef.current % slotAvailable.length];
93
+ nextIndexRef.current++;
94
+ available = available.filter((i) => i !== newValue);
95
+ const newSrc = (_b = items[newValue]) === null || _b === void 0 ? void 0 : _b.src;
96
+ if (newSrc) {
97
+ blockedTargetSrcs.add(newSrc);
98
+ }
99
+ nextTransitions.push({ slotIndex, from: currentItemIndex, to: newValue });
100
+ });
101
+ if (nextTransitions.length === 0)
102
+ return;
103
+ setTransitions((prev) => {
72
104
  const next = [...prev];
73
- slotIndices.forEach((slotIndex) => {
74
- next[slotIndex] = true;
105
+ nextTransitions.forEach(({ slotIndex, from, to }) => {
106
+ next[slotIndex] = { from, to };
75
107
  });
76
108
  return next;
77
109
  });
78
110
  timeout = setTimeout(() => {
79
111
  setSlots((prevSlots) => {
80
112
  const newSlots = [...prevSlots];
81
- let available = rotatableIndices.filter((i) => !newSlots.includes(i));
82
- slotIndices.forEach((slotIndex) => {
83
- if (available.length === 0)
84
- return;
85
- const newValue = available[nextIndexRef.current % available.length];
86
- nextIndexRef.current++;
87
- newSlots[slotIndex] = newValue;
88
- available = available.filter((i) => i !== newValue);
113
+ nextTransitions.forEach(({ slotIndex, to }) => {
114
+ newSlots[slotIndex] = to;
89
115
  });
90
116
  return newSlots;
91
117
  });
92
- setHidden((prev) => {
118
+ setTransitions((prev) => {
93
119
  const next = [...prev];
94
- slotIndices.forEach((slotIndex) => {
95
- next[slotIndex] = false;
120
+ nextTransitions.forEach(({ slotIndex }) => {
121
+ next[slotIndex] = undefined;
96
122
  });
97
123
  return next;
98
124
  });
99
- }, 500);
125
+ }, utils_2.SWAP_ANIMATION_DURATIONS[swapAnimation]);
100
126
  }, 2000);
101
127
  return () => {
102
128
  clearInterval(interval);
@@ -104,29 +130,49 @@ const LogoRotatorBlock = (props) => {
104
130
  clearTimeout(timeout);
105
131
  }
106
132
  };
107
- }, [count, items, maxRotateCount, minRotateCount, rotatableIndices]);
108
- const renderItems = (0, react_1.useMemo)(() => slots.map((slot, index) => (react_1.default.createElement(Item_1.default, { key: index, colSizes: colSizes, url: items[slot].url, src: items[slot].src, hidden: hidden[index] }))), [colSizes, hidden, items, slots]);
133
+ }, [activeCount, items, maxRotateCount, minRotateCount, rotatableIndices, swapAnimation]);
134
+ const renderItems = (0, react_1.useMemo)(() => slots.map((slot, index) => {
135
+ var _a;
136
+ const transition = transitions[index];
137
+ const item = items[(_a = transition === null || transition === void 0 ? void 0 : transition.to) !== null && _a !== void 0 ? _a : slot];
138
+ const previousItem = transition ? items[transition.from] : undefined;
139
+ return (react_1.default.createElement(Item_1.default, { key: index, colSizes: colSizes, url: item.url, src: item.src, previousUrl: previousItem === null || previousItem === void 0 ? void 0 : previousItem.url, previousSrc: previousItem === null || previousItem === void 0 ? void 0 : previousItem.src, swapAnimation: swapAnimation, onMouseEnter: () => handleSlotMouseEnter(index), onMouseLeave: () => handleSlotMouseLeave(index) }));
140
+ }), [
141
+ colSizes,
142
+ handleSlotMouseEnter,
143
+ handleSlotMouseLeave,
144
+ items,
145
+ slots,
146
+ swapAnimation,
147
+ transitions,
148
+ ]);
109
149
  const titleProps = !title || typeof title === 'string'
110
150
  ? { text: title, textSize: 'l' }
111
151
  : title;
112
152
  const hasTitle = Boolean(title);
113
153
  return (react_1.default.createElement(AnimateBlock_1.default, { className: b({ theme }), animate: animated },
114
- react_1.default.createElement("div", { className: b('root'), onMouseEnter: () => {
115
- isHoveredRef.current = true;
116
- }, onMouseLeave: () => {
117
- isHoveredRef.current = false;
118
- } },
154
+ react_1.default.createElement("div", { className: b('root') },
119
155
  hasTitle && (react_1.default.createElement(components_1.Title, { title: titleProps, className: b('title'), colSizes: { all: 12 } })),
120
156
  rowMode ? (react_1.default.createElement("div", { className: b('row-items') }, slots.map((slot, index) => {
121
- const item = items[slot];
122
- if (!item)
157
+ var _a;
158
+ const transition = transitions[index];
159
+ const item = items[(_a = transition === null || transition === void 0 ? void 0 : transition.to) !== null && _a !== void 0 ? _a : slot];
160
+ const previousItem = transition ? items[transition.from] : undefined;
161
+ if (item === undefined)
123
162
  return null;
124
- if (item.url) {
125
- return (react_1.default.createElement(uikit_1.Link, { key: index, href: item.url, className: b('row-item', { hidden: hidden[index] }) },
126
- react_1.default.createElement(components_1.ImageBase, { src: item.src, className: b('image'), alt: "", "aria-hidden": "true" })));
127
- }
128
- return (react_1.default.createElement("div", { key: index, className: b('row-item', { hidden: hidden[index] }) },
129
- react_1.default.createElement(components_1.ImageBase, { src: item.src, className: b('image'), alt: "", "aria-hidden": "true" })));
163
+ const renderRowLayer = (layerItem, layer) => {
164
+ const layerClassName = b('logo-layer', (0, utils_2.getLayerModifiers)(layer, swapAnimation));
165
+ if (layerItem.url) {
166
+ return (react_1.default.createElement(uikit_1.Link, { href: layerItem.url, className: `${b('row-item-link')} ${layerClassName}` },
167
+ react_1.default.createElement(components_1.ImageBase, { src: layerItem.src, className: b('image'), alt: "", "aria-hidden": "true" })));
168
+ }
169
+ return (react_1.default.createElement("div", { className: layerClassName },
170
+ react_1.default.createElement(components_1.ImageBase, { src: layerItem.src, className: b('image'), alt: "", "aria-hidden": "true" })));
171
+ };
172
+ const content = (react_1.default.createElement(react_1.default.Fragment, null,
173
+ previousItem && renderRowLayer(previousItem, 'from'),
174
+ renderRowLayer(item, previousItem ? 'to' : 'current')));
175
+ return (react_1.default.createElement("div", { key: index, className: b('row-item', { swapping: Boolean(previousItem) }), onMouseEnter: () => handleSlotMouseEnter(index), onMouseLeave: () => handleSlotMouseLeave(index) }, content));
130
176
  }))) : (react_1.default.createElement(grid_1.Grid, { className: b('items') },
131
177
  react_1.default.createElement(grid_1.Row, { className: b('row') }, renderItems))))));
132
178
  };
@@ -182,7 +182,10 @@ export declare const LogoRotatorBlock: {
182
182
  };
183
183
  };
184
184
  };
185
- count: {
185
+ countMobile: {
186
+ type: string;
187
+ };
188
+ countDesktop: {
186
189
  type: string;
187
190
  };
188
191
  minRotateCount: {
@@ -191,6 +194,10 @@ export declare const LogoRotatorBlock: {
191
194
  maxRotateCount: {
192
195
  type: string;
193
196
  };
197
+ swapAnimation: {
198
+ type: string;
199
+ enum: string[];
200
+ };
194
201
  colSizes: {
195
202
  type: string;
196
203
  additionalProperties: {
@@ -5,7 +5,7 @@ const common_1 = require("../../schema/validators/common");
5
5
  exports.LogoRotatorBlock = {
6
6
  'logo-rotator-block': {
7
7
  additionalProperties: false,
8
- required: ['items', 'count'],
8
+ required: ['items', 'countMobile'],
9
9
  properties: Object.assign(Object.assign(Object.assign({}, common_1.BaseProps), common_1.AnimatableProps), { title: {
10
10
  oneOf: [
11
11
  {
@@ -27,12 +27,17 @@ exports.LogoRotatorBlock = {
27
27
  isStatic: { type: 'boolean' },
28
28
  },
29
29
  },
30
- }, count: {
30
+ }, countMobile: {
31
+ type: 'number',
32
+ }, countDesktop: {
31
33
  type: 'number',
32
34
  }, minRotateCount: {
33
35
  type: 'number',
34
36
  }, maxRotateCount: {
35
37
  type: 'number',
38
+ }, swapAnimation: {
39
+ type: 'string',
40
+ enum: ['fade', 'morph'],
36
41
  }, colSizes: {
37
42
  type: 'object',
38
43
  additionalProperties: common_1.containerSizesObject,
@@ -0,0 +1,7 @@
1
+ import { LogoRotatorBlockProps } from '../../models';
2
+ export type LogoRotatorLayer = 'current' | 'from' | 'to';
3
+ export type LogoRotatorSwapAnimation = NonNullable<LogoRotatorBlockProps['swapAnimation']>;
4
+ export declare const DEFAULT_SWAP_ANIMATION: LogoRotatorSwapAnimation;
5
+ export declare const SWAP_ANIMATION_DURATIONS: Record<LogoRotatorSwapAnimation, number>;
6
+ export declare const getLayerModifiers: (layer: LogoRotatorLayer, swapAnimation: LogoRotatorSwapAnimation) => Record<string, boolean>;
7
+ export declare const getInitialSlots: (items: LogoRotatorBlockProps['items'], count: number) => number[];
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getInitialSlots = exports.getLayerModifiers = exports.SWAP_ANIMATION_DURATIONS = exports.DEFAULT_SWAP_ANIMATION = void 0;
4
+ exports.DEFAULT_SWAP_ANIMATION = 'fade';
5
+ exports.SWAP_ANIMATION_DURATIONS = {
6
+ fade: 1100,
7
+ morph: 500,
8
+ };
9
+ const getLayerModifiers = (layer, swapAnimation) => {
10
+ const animationModifier = layer === 'current' ? undefined : `${swapAnimation}-${layer}`;
11
+ const modifiers = { [layer]: true };
12
+ if (animationModifier) {
13
+ modifiers[animationModifier] = true;
14
+ }
15
+ return modifiers;
16
+ };
17
+ exports.getLayerModifiers = getLayerModifiers;
18
+ const getInitialSlots = (items, count) => {
19
+ var _a;
20
+ const staticIdxList = items.map((item, i) => (item.isStatic ? i : -1)).filter((i) => i !== -1);
21
+ const rotatableIdxList = items
22
+ .map((item, i) => (item.isStatic ? -1 : i))
23
+ .filter((i) => i !== -1);
24
+ const initial = [];
25
+ let rotatablePointer = 0;
26
+ for (let slot = 0; slot < count; slot++) {
27
+ if (slot < staticIdxList.length) {
28
+ initial.push(staticIdxList[slot]);
29
+ }
30
+ else {
31
+ initial.push((_a = rotatableIdxList[rotatablePointer++]) !== null && _a !== void 0 ? _a : 0);
32
+ }
33
+ }
34
+ return initial;
35
+ };
36
+ exports.getInitialSlots = getInitialSlots;
@@ -302,9 +302,11 @@ export interface LogoRotatorBlockProps extends Animatable {
302
302
  src: string;
303
303
  isStatic?: boolean;
304
304
  }[];
305
- count: number;
305
+ countMobile: number;
306
+ countDesktop?: number;
306
307
  minRotateCount?: number;
307
308
  maxRotateCount?: number;
309
+ swapAnimation?: 'fade' | 'morph';
308
310
  colSizes?: Partial<Record<GridColumnSize, number>>;
309
311
  theme?: TextTheme;
310
312
  rowMode?: boolean;
@@ -1,12 +1,17 @@
1
1
  import React from 'react';
2
2
  import { LogoRotatorBlockProps } from '../../models';
3
+ import type { LogoRotatorSwapAnimation } from './utils';
3
4
  import './LogoRotator.css';
4
5
  type Props = {
5
6
  src: string;
6
7
  url?: string;
7
- hidden: boolean;
8
+ previousSrc?: string;
9
+ previousUrl?: string;
10
+ swapAnimation: LogoRotatorSwapAnimation;
8
11
  colSizes: LogoRotatorBlockProps['colSizes'];
12
+ onMouseEnter?: () => void;
13
+ onMouseLeave?: () => void;
9
14
  };
10
- export declare const Item: ({ url, src, hidden, colSizes }: Props) => JSX.Element;
11
- declare const _default: React.MemoExoticComponent<({ url, src, hidden, colSizes }: Props) => JSX.Element>;
15
+ export declare const Item: ({ url, src, previousSrc, previousUrl, swapAnimation, colSizes, onMouseEnter, onMouseLeave, }: Props) => JSX.Element;
16
+ declare const _default: React.MemoExoticComponent<({ url, src, previousSrc, previousUrl, swapAnimation, colSizes, onMouseEnter, onMouseLeave, }: Props) => JSX.Element>;
12
17
  export default _default;
@@ -3,18 +3,23 @@ import { Link } from '@gravity-ui/uikit';
3
3
  import { Image } from '../../components';
4
4
  import { Col } from '../../grid';
5
5
  import { block } from '../../utils';
6
+ import { getLayerModifiers } from './utils';
6
7
  import './LogoRotator.css';
7
8
  const b = block('logo-rotator-block');
8
9
  const defaultColSizes = { all: 3 };
9
- export const Item = ({ url, src, hidden, colSizes }) => {
10
- const renderItem = React.useMemo(() => {
11
- if (url) {
12
- return (React.createElement(Link, { href: url, className: b('item', { hidden }) },
13
- React.createElement(Image, { src: src, className: b('image'), alt: "", "aria-hidden": "true" })));
10
+ const getLayerClassName = (layer, swapAnimation, link = false) => `${link ? `${b('item-link')} ` : ''}${b('logo-layer', getLayerModifiers(layer, swapAnimation))}`;
11
+ export const Item = ({ url, src, previousSrc, previousUrl, swapAnimation, colSizes, onMouseEnter, onMouseLeave, }) => {
12
+ const renderLayer = (layerSrc, layer, layerUrl) => {
13
+ const image = React.createElement(Image, { src: layerSrc, className: b('image'), alt: "", "aria-hidden": "true" });
14
+ if (layerUrl) {
15
+ return (React.createElement(Link, { href: layerUrl, className: getLayerClassName(layer, swapAnimation, true) }, image));
14
16
  }
15
- return (React.createElement("div", { className: b('item', { hidden }) },
16
- React.createElement(Image, { src: src, className: b('image'), alt: "", "aria-hidden": "true" })));
17
- }, [hidden, src, url]);
18
- return React.createElement(Col, { sizes: colSizes || defaultColSizes }, renderItem);
17
+ return React.createElement("div", { className: getLayerClassName(layer, swapAnimation) }, image);
18
+ };
19
+ const isSwapping = Boolean(previousSrc);
20
+ return (React.createElement(Col, { sizes: colSizes || defaultColSizes },
21
+ React.createElement("div", { className: b('item', { swapping: isSwapping }), onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave },
22
+ previousSrc && renderLayer(previousSrc, 'from', previousUrl),
23
+ renderLayer(src, previousSrc ? 'to' : 'current', url))));
19
24
  };
20
25
  export default React.memo(Item);