@cntrl-site/sdk-nextjs 1.0.19-alpha.2 → 1.0.19-alpha.4

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 (51) hide show
  1. package/lib/components/Article.js +1 -1
  2. package/lib/components/Item.js +31 -36
  3. package/lib/components/items/CodeEmbedItem.js +11 -15
  4. package/lib/components/items/CustomItem.js +7 -12
  5. package/lib/components/items/GroupItem.js +11 -15
  6. package/lib/components/items/ImageItem.js +21 -24
  7. package/lib/components/items/RectangleItem.js +24 -18
  8. package/lib/components/items/RichTextItem.js +19 -14
  9. package/lib/components/items/VideoItem.js +22 -23
  10. package/lib/components/items/VimeoEmbed.js +16 -22
  11. package/lib/components/items/YoutubeEmbed.js +16 -23
  12. package/lib/components/useItemPosition.js +10 -5
  13. package/lib/interactions/CSSPropertyNameMap.js +38 -0
  14. package/lib/interactions/InteractionsRegistry.js +158 -0
  15. package/lib/interactions/ItemInteractionCtrl.js +55 -0
  16. package/lib/interactions/getTransition.js +21 -0
  17. package/lib/interactions/types.js +2 -0
  18. package/lib/interactions/useItemInteractionCtrl.js +17 -0
  19. package/lib/provider/InteractionsContext.js +17 -39
  20. package/lib/utils/getStyleFromItemStateAndParams.js +9 -0
  21. package/package.json +1 -1
  22. package/src/components/Article.tsx +2 -2
  23. package/src/components/Item.tsx +40 -34
  24. package/src/components/items/CodeEmbedItem.tsx +11 -14
  25. package/src/components/items/CustomItem.tsx +8 -13
  26. package/src/components/items/GroupItem.tsx +12 -14
  27. package/src/components/items/ImageItem.tsx +29 -25
  28. package/src/components/items/RectangleItem.tsx +30 -16
  29. package/src/components/items/RichTextItem.tsx +26 -20
  30. package/src/components/items/VideoItem.tsx +31 -25
  31. package/src/components/items/VimeoEmbed.tsx +21 -21
  32. package/src/components/items/YoutubeEmbed.tsx +17 -23
  33. package/src/components/useItemPosition.ts +12 -5
  34. package/src/interactions/CSSPropertyNameMap.ts +38 -0
  35. package/src/interactions/InteractionsRegistry.ts +193 -0
  36. package/src/interactions/ItemInteractionCtrl.ts +56 -0
  37. package/src/interactions/getTransition.ts +27 -0
  38. package/src/interactions/types.ts +32 -0
  39. package/src/interactions/useItemInteractionCtrl.ts +18 -0
  40. package/src/provider/InteractionsContext.old.tsx +65 -0
  41. package/src/provider/InteractionsContext.test.tsx +7 -7
  42. package/src/provider/InteractionsContext.tsx +17 -54
  43. package/src/utils/getStyleFromItemStateAndParams.ts +8 -0
  44. package/lib/components/useStatesClassNames.js +0 -18
  45. package/lib/components/useStatesTransitions.js +0 -89
  46. package/lib/utils/StateStyles/StateStyles.js +0 -89
  47. package/lib/utils/getStatesCSS.js +0 -16
  48. package/src/components/useStatesClassNames.ts +0 -23
  49. package/src/components/useStatesTransitions.ts +0 -95
  50. package/src/utils/StateStyles/StateStyles.ts +0 -99
  51. package/src/utils/getStatesCSS.ts +0 -24
@@ -1,45 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InteractionsProvider = exports.InteractionsContext = void 0;
3
+ exports.useInteractionsRegistry = exports.InteractionsProvider = exports.InteractionsContext = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
- const defaultState = {
7
- interactionsStatesMap: {},
8
- interactions: [],
9
- transitionTo: () => { },
10
- getItemTrigger: () => null
11
- };
12
- exports.InteractionsContext = (0, react_1.createContext)(defaultState);
13
- const InteractionsProvider = ({ interactions, children }) => {
14
- const defaultStatesMap = interactions.reduce((map, { id, startStateId }) => {
15
- map[id] = startStateId;
16
- return map;
17
- }, {});
18
- const [interactionsStatesMap, setInteractionsStatesMap] = (0, react_1.useState)(defaultStatesMap);
19
- const transitionTo = (interactionId, stateId) => {
20
- setInteractionsStatesMap((map) => (Object.assign(Object.assign({}, map), { [interactionId]: stateId })));
21
- };
22
- const getItemTrigger = (itemId, triggerType) => {
23
- for (const interaction of interactions) {
24
- const activeStateId = interactionsStatesMap[interaction.id];
25
- const matchingTrigger = interaction.triggers.find((trigger) => trigger.itemId === itemId &&
26
- trigger.from === activeStateId &&
27
- trigger.type === triggerType);
28
- if (matchingTrigger) {
29
- return {
30
- id: interaction.id,
31
- from: matchingTrigger.from,
32
- to: matchingTrigger.to,
33
- };
34
- }
35
- }
36
- return null;
37
- };
38
- return ((0, jsx_runtime_1.jsx)(exports.InteractionsContext.Provider, { value: {
39
- transitionTo,
40
- interactionsStatesMap,
41
- interactions,
42
- getItemTrigger
43
- }, children: children }));
6
+ const InteractionsRegistry_1 = require("../interactions/InteractionsRegistry");
7
+ const useCurrentLayout_1 = require("../common/useCurrentLayout");
8
+ exports.InteractionsContext = (0, react_1.createContext)(undefined);
9
+ const InteractionsProvider = ({ article, children }) => {
10
+ const { layoutId } = (0, useCurrentLayout_1.useCurrentLayout)();
11
+ const registry = (0, react_1.useMemo)(() => {
12
+ if (!layoutId)
13
+ return;
14
+ return new InteractionsRegistry_1.InteractionsRegistry(article, layoutId);
15
+ }, [layoutId]);
16
+ return ((0, jsx_runtime_1.jsx)(exports.InteractionsContext.Provider, { value: registry, children: children }));
44
17
  };
45
18
  exports.InteractionsProvider = InteractionsProvider;
19
+ function useInteractionsRegistry() {
20
+ const registry = (0, react_1.useContext)(exports.InteractionsContext);
21
+ return registry;
22
+ }
23
+ exports.useInteractionsRegistry = useInteractionsRegistry;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getStyleFromItemStateAndParams = void 0;
4
+ function getStyleFromItemStateAndParams(stateValue, paramsValue) {
5
+ return stateValue !== undefined
6
+ ? stateValue
7
+ : paramsValue;
8
+ }
9
+ exports.getStyleFromItemStateAndParams = getStyleFromItemStateAndParams;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk-nextjs",
3
- "version": "1.0.19-alpha.2",
3
+ "version": "1.0.19-alpha.4",
4
4
  "description": "SDK for Next.js",
5
5
  "main": "lib/index.js",
6
6
  "types": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  import { FC, useEffect, useId, useRef, useState } from 'react';
2
2
  import JSXStyle from 'styled-jsx/style';
3
- import { Article as TArticle, ArticleItemType, ItemState, ItemStateParams } from '@cntrl-site/sdk';
3
+ import { Article as TArticle } from '@cntrl-site/sdk';
4
4
  import { Section } from './Section';
5
5
  import { Item } from './Item';
6
6
  import { useArticleRectObserver } from '../utils/ArticleRectManager/useArticleRectObserver';
@@ -28,7 +28,7 @@ export const Article: FC<Props> = ({ article, sectionData }) => {
28
28
 
29
29
  return (
30
30
  <ArticleRectContext.Provider value={articleRectObserver}>
31
- <InteractionsProvider interactions={article.interactions}>
31
+ <InteractionsProvider article={article}>
32
32
  <ArticleWrapper>
33
33
  <div className="article" ref={articleRef}>
34
34
  {article.sections.map((section, i) => {
@@ -31,16 +31,14 @@ import { GroupItem } from './items/GroupItem';
31
31
  import { CodeEmbedItem } from './items/CodeEmbedItem';
32
32
  import { AreaAnchor } from '@cntrl-site/sdk/src/types/article/ItemArea';
33
33
  import { KeyframesContext } from '../provider/KeyframesContext';
34
- import { InteractionsContext } from '../provider/InteractionsContext';
35
- import { getStatesCSS } from '../utils/getStatesCSS';
36
- import { useStatesClassNames } from './useStatesClassNames';
37
- import { useStatesTransitions } from './useStatesTransitions';
34
+ import { useItemInteractionCtrl } from '../interactions/useItemInteractionCtrl';
38
35
 
39
36
  export interface ItemProps<I extends ItemAny> {
40
37
  item: I;
41
38
  sectionId: string;
42
39
  onResize?: (height: number) => void;
43
40
  articleHeight: number;
41
+ interactionCtrl?: ReturnType<typeof useItemInteractionCtrl>;
44
42
  }
45
43
 
46
44
  export interface ItemWrapperProps extends ItemProps<ItemAny> {
@@ -92,14 +90,21 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
92
90
  const exemplary = useExemplary();
93
91
  const [wrapperHeight, setWrapperHeight] = useState<undefined | number>(undefined);
94
92
  const [itemHeight, setItemHeight] = useState<undefined | number>(undefined);
95
- const scale = useItemScale(item, sectionId);
96
- const position = useItemPosition(item, sectionId);
93
+ const itemScale = useItemScale(item, sectionId);
94
+ const interactionCtrl = useItemInteractionCtrl(item.id);
95
+ // @ts-ignore
96
+ console.log(interactionCtrl?.transitionsInProgress);
97
+ const wrapperStateProps = interactionCtrl?.getState(['top', 'left']);
98
+ const innerStateProps = interactionCtrl?.getState(['width', 'height', 'scale']);
99
+ const position = useItemPosition(item, sectionId, {
100
+ top: wrapperStateProps?.styles?.top as number,
101
+ left: wrapperStateProps?.styles?.left as number,
102
+ });
97
103
  const sectionHeight = useSectionHeightData(sectionId);
98
104
  const stickyTop = useStickyItemTop(item, sectionHeight, sectionId);
99
105
  const dimensions = useItemDimensions(item, sectionId);
100
- const layoutValues: Record<string, any>[] = [item.area, item.hidden, item.state];
106
+ const layoutValues: Record<string, any>[] = [item.area, item.hidden];
101
107
  const isInitialRef = useRef(true);
102
- const { transitionTo, getItemTrigger } = useContext(InteractionsContext);
103
108
  layoutValues.push(item.sticky);
104
109
  layoutValues.push(sectionHeight);
105
110
  if (item.layoutParams) {
@@ -134,19 +139,23 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
134
139
  }, []);
135
140
 
136
141
  const isRichText = isItemType(item, ArticleItemType.RichText);
137
- const innerStatesClassNames = useStatesClassNames(item.id, item.state, 'item-inner');
138
- const wrapperStatesClassNames = useStatesClassNames(item.id, item.state, 'item-wrapper');
139
- useStatesTransitions(itemWrapperRef.current, item.state, ['top', 'left']);
140
- useStatesTransitions(itemInnerRef.current, item.state, ['width', 'height', 'scale']);
142
+ const width = (innerStateProps?.styles?.width ?? dimensions?.width) as number | undefined;
143
+ const height = (innerStateProps?.styles?.height ?? dimensions?.height) as number | undefined;
144
+ const scale = innerStateProps?.styles?.scale ?? itemScale;
141
145
  return (
142
146
  <div
143
- className={`item-wrapper-${item.id} ${wrapperStatesClassNames}`}
147
+ className={`item-wrapper-${item.id}`}
144
148
  ref={itemWrapperRef}
149
+ onTransitionEnd={(e) => {
150
+ e.stopPropagation();
151
+ interactionCtrl?.handleTransitionEnd?.(e.propertyName);
152
+ }}
145
153
  style={{
146
154
  ...(position ? { top: position.top } : {}),
147
155
  ...(position ? { left: position.left } : {}),
148
156
  ...(position ? { bottom: position.bottom } : {}),
149
- ...(wrapperHeight !== undefined ? { height: `${wrapperHeight * 100}vw` } : {})
157
+ ...(wrapperHeight !== undefined ? { height: `${wrapperHeight * 100}vw` } : {}),
158
+ transition: wrapperStateProps?.transition ?? 'none'
150
159
  }}
151
160
  >
152
161
  <div
@@ -160,45 +169,44 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
160
169
  >
161
170
  <RichTextWrapper isRichText={isRichText}>
162
171
  <div
163
- className={`item-${item.id}-inner ${innerStatesClassNames}`}
172
+ className={`item-${item.id}-inner`}
164
173
  ref={itemInnerRef}
165
174
  onClick={() => {
166
- const trigger = getItemTrigger(item.id, 'click');
167
- if (!trigger) return;
168
- transitionTo(trigger.id, trigger.to);
175
+ interactionCtrl?.sendTrigger('click');
169
176
  }}
170
177
  onMouseEnter={() => {
171
- const trigger = getItemTrigger(item.id, 'hover-in');
172
- if (!trigger) return;
173
- transitionTo(trigger.id, trigger.to);
178
+ interactionCtrl?.sendTrigger('hover-in');
174
179
  }}
175
180
  onMouseLeave={() => {
176
- const trigger = getItemTrigger(item.id, 'hover-out');
177
- if (!trigger) return;
178
- transitionTo(trigger.id, trigger.to);
181
+ interactionCtrl?.sendTrigger('hover-out');
179
182
  }}
180
183
  style={{
181
- ...(dimensions ? {
184
+ ...((width && height) ? {
182
185
  width: `${sizingAxis.x === 'manual'
183
186
  ? isRichText
184
- ? `${dimensions.width * exemplary}px`
185
- : `${dimensions.width * 100}vw`
187
+ ? `${width * exemplary}px`
188
+ : `${width * 100}vw`
186
189
  : 'max-content'}`,
187
- height: `${sizingAxis.y === 'manual' ? `${dimensions.height * 100}vw` : 'unset'}` } : {}),
190
+ height: `${sizingAxis.y === 'manual' ? `${height * 100}vw` : 'unset'}` } : {}),
188
191
  ...(scale !== undefined ? { transform: `scale(${scale})`, 'WebkitTransform': `scale(${scale})` } : {}),
192
+ transition: innerStateProps?.transition ?? 'none'
189
193
  }}
190
194
  >
191
- <ItemComponent item={item} sectionId={sectionId} onResize={handleItemResize} articleHeight={articleHeight} />
195
+ <ItemComponent
196
+ item={item}
197
+ sectionId={sectionId}
198
+ onResize={handleItemResize}
199
+ articleHeight={articleHeight}
200
+ interactionCtrl={interactionCtrl}
201
+ />
192
202
  </div>
193
203
  </RichTextWrapper>
194
204
  </div>
195
205
  <JSXStyle id={id}>{`
196
- ${getLayoutStyles(layouts, layoutValues, ([area, hidden, states, sticky, sectionHeight, layoutParams], exemplary) => {
206
+ ${getLayoutStyles(layouts, layoutValues, ([area, hidden, sticky, sectionHeight, layoutParams], exemplary) => {
197
207
  const sizingAxis = parseSizing(layoutParams.sizing);
198
208
  const isScreenBasedBottom = area.positionType === PositionType.ScreenBased && area.anchorSide === AnchorSide.Bottom;
199
209
  const scaleAnchor = area.scaleAnchor as AreaAnchor;
200
- const statesInnerCSS = getStatesCSS(item.id, 'item-inner', ['width', 'height', 'scale'], states);
201
- const statesWrapperCSS = getStatesCSS(item.id, 'item-wrapper', ['top', 'left'], states, area.anchorSide);
202
210
  return (`
203
211
  .item-${item.id} {
204
212
  position: ${sticky ? 'sticky' : 'absolute'};
@@ -226,8 +234,6 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
226
234
  top: ${isScreenBasedBottom ? 'unset' : getItemTopStyle(area.top, area.anchorSide)};
227
235
  left: ${area.left * 100}vw;
228
236
  }
229
- ${statesWrapperCSS}
230
- ${statesInnerCSS}
231
237
  `);
232
238
  })}
233
239
  `}</JSXStyle>
@@ -7,9 +7,6 @@ import { useRegisterResize } from "../../common/useRegisterResize";
7
7
  import { useItemAngle } from '../useItemAngle';
8
8
  import { LinkWrapper } from '../LinkWrapper';
9
9
  import { useCodeEmbedItem } from './useCodeEmbedItem';
10
- import { getStatesCSS } from '../../utils/getStatesCSS';
11
- import { useStatesClassNames } from '../useStatesClassNames';
12
- import { useStatesTransitions } from '../useStatesTransitions';
13
10
 
14
11
  const stylesMap = {
15
12
  [AreaAnchor.TopLeft]: {},
@@ -23,18 +20,20 @@ const stylesMap = {
23
20
  [AreaAnchor.BottomRight]: { justifyContent: 'flex-end', alignItems: 'flex-end' }
24
21
  };
25
22
 
26
- export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId, onResize }) => {
23
+ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId, onResize, interactionCtrl }) => {
27
24
  const id = useId();
28
25
  const { layouts } = useCntrlContext();
29
- const { anchor, blur, opacity } = useCodeEmbedItem(item, sectionId);
30
- const angle = useItemAngle(item, sectionId);
26
+ const { anchor, blur: itemBlur, opacity: itemOpacity } = useCodeEmbedItem(item, sectionId);
27
+ const itemAngle = useItemAngle(item, sectionId);
31
28
  const { html } = item.commonParams;
32
29
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
33
30
  useRegisterResize(ref, onResize);
34
31
  const pos = stylesMap[anchor];
35
- const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state];
36
- const statesClassNames = useStatesClassNames(item.id, item.state, 'embed-wrapper');
37
- useStatesTransitions(ref!, item.state, ['angle', 'blur', 'opacity']);
32
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
33
+ const stateParams = interactionCtrl?.getState(['angle', 'blur', 'opacity']);
34
+ const blur = (stateParams?.styles?.blur ?? itemBlur) as number;
35
+ const opacity = stateParams?.styles?.opacity ?? itemOpacity;
36
+ const angle = stateParams?.styles?.angle ?? itemAngle;
38
37
 
39
38
  useEffect(() => {
40
39
  if (!ref) return;
@@ -60,11 +59,12 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
60
59
  return (
61
60
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
62
61
  <div
63
- className={`embed-wrapper-${item.id} ${statesClassNames}`}
62
+ className={`embed-wrapper-${item.id}`}
64
63
  style={{
65
64
  ...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
66
65
  ...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
67
66
  ...(opacity !== undefined ? { opacity } : {}),
67
+ transition: stateParams?.transition ?? 'none'
68
68
  }}
69
69
  ref={setRef}
70
70
  >
@@ -97,20 +97,17 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
97
97
  z-index: 1;
98
98
  border: none;
99
99
  }
100
- ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, stateParams], exemplary) => {
101
- const statesCSS = getStatesCSS(item.id, 'embed-wrapper', ['angle', 'blur', 'opacity'], stateParams);
100
+ ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams], exemplary) => {
102
101
  return (`
103
102
  .embed-wrapper-${item.id} {
104
103
  opacity: ${layoutParams.opacity};
105
104
  transform: rotate(${area.angle}deg);
106
105
  filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
107
- transition: all 0.2s ease;
108
106
  }
109
107
  .embed-${item.id} {
110
108
  width: ${item.commonParams.scale ? `${area.width * exemplary}px` : '100%'};
111
109
  height: ${item.commonParams.scale ? `${area.height * exemplary}px` : '100%'};
112
110
  }
113
- ${statesCSS}
114
111
  `);
115
112
  })}
116
113
  `}</JSXStyle>
@@ -5,46 +5,41 @@ import { ItemProps } from '../Item';
5
5
  import JSXStyle from 'styled-jsx/style';
6
6
  import { useRegisterResize } from "../../common/useRegisterResize";
7
7
  import { useItemAngle } from '../useItemAngle';
8
- import { useStatesClassNames } from '../useStatesClassNames';
9
- import { getStatesCSS } from '../../utils/getStatesCSS';
10
- import { useStatesTransitions } from '../useStatesTransitions';
11
8
 
12
- export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item, onResize, sectionId }) => {
9
+ export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item, onResize, sectionId, interactionCtrl }) => {
13
10
  const sdk = useCntrlContext();
14
11
  const { layouts } = useCntrlContext();
15
- const angle = useItemAngle(item, sectionId);
12
+ const itemAngle = useItemAngle(item, sectionId);
16
13
  const component = sdk.customItems.get(item.commonParams.name);
17
- const layoutValues: Record<string, any>[] = [item.area, item.state];
14
+ const layoutValues: Record<string, any>[] = [item.area];
18
15
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
19
16
  useRegisterResize(ref, onResize);
20
- const statesClassNames = useStatesClassNames(item.id, item.state, 'custom-component');
21
- useStatesTransitions(ref, item.state, ['angle'])
17
+ const stateParams = interactionCtrl?.getState(['angle']);
18
+ const angle = stateParams?.styles?.angle ?? itemAngle;
22
19
  if (!component) return null;
23
20
  return (
24
21
  <>
25
22
  <div
26
- className={`custom-component-${item.id} ${statesClassNames}`}
23
+ className={`custom-component-${item.id}`}
27
24
  ref={setRef}
28
25
  style={{
29
26
  ...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
27
+ transition: stateParams?.transition ?? 'none'
30
28
  }}
31
29
  >
32
30
  {component({})}
33
31
  </div>
34
32
  <JSXStyle id={item.id}>
35
- {`${getLayoutStyles(layouts, layoutValues, ([area, stateParams]) => {
36
- const statesCSS = getStatesCSS(item.id, 'custom-component', ['angle'], stateParams);
33
+ {`${getLayoutStyles(layouts, layoutValues, ([area]) => {
37
34
  return (`
38
35
  .custom-component-${item.id} {
39
36
  transform: rotate(${area.angle}deg);
40
- transition: all 0.2s ease;
41
37
  height: 100%;
42
38
  width: 100%;
43
39
  position: absolute;
44
40
  left: 0;
45
41
  top: 0;
46
42
  }
47
- ${statesCSS}
48
43
  `);
49
44
  })}`}
50
45
  </JSXStyle>
@@ -7,31 +7,31 @@ import { useRegisterResize } from '../../common/useRegisterResize';
7
7
  import { useCntrlContext } from '../../provider/useCntrlContext';
8
8
  import { useItemAngle } from '../useItemAngle';
9
9
  import { useGroupItem } from './useGroupItem';
10
- import { useStatesClassNames } from '../useStatesClassNames';
11
- import { getStatesCSS } from '../../utils/getStatesCSS';
12
- import { useStatesTransitions } from '../useStatesTransitions';
10
+ import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
13
11
 
14
- export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize, articleHeight }) => {
12
+ export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize, articleHeight, interactionCtrl }) => {
15
13
  const id = useId();
16
14
  const { items } = item;
17
- const angle = useItemAngle(item, sectionId);
15
+ const itemAngle = useItemAngle(item, sectionId);
18
16
  const { layouts } = useCntrlContext();
19
- const { opacity } = useGroupItem(item, sectionId);
20
- const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state];
21
- const statesClassNames = useStatesClassNames(item.id, item.state, 'group');
17
+ const { opacity: itemOpacity } = useGroupItem(item, sectionId);
18
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
22
19
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
23
20
  useRegisterResize(ref, onResize);
24
- useStatesTransitions(ref!, item.state, ['opacity', 'angle'])
21
+ const stateParams = interactionCtrl?.getState(['opacity', 'angle']);
22
+ const angle = getStyleFromItemStateAndParams(stateParams?.styles?.angle, itemAngle);
23
+ const opacity = getStyleFromItemStateAndParams(stateParams?.styles?.opacity, itemOpacity);
25
24
 
26
25
  return (
27
26
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
28
27
  <>
29
28
  <div
30
- className={`group-${item.id} ${statesClassNames}`}
29
+ className={`group-${item.id}`}
31
30
  ref={setRef}
32
31
  style={{
33
32
  ...(opacity !== undefined ? { opacity } : {}),
34
33
  ...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
34
+ transition: stateParams?.transition ?? 'none'
35
35
  }}
36
36
  >
37
37
  {items && items.map(item => (
@@ -40,6 +40,7 @@ export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize
40
40
  key={item.id}
41
41
  sectionId={sectionId}
42
42
  articleHeight={articleHeight}
43
+ interactionCtrl={interactionCtrl}
43
44
  isInGroup
44
45
  />
45
46
  ))}
@@ -51,15 +52,12 @@ export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize
51
52
  height: 100%;
52
53
  box-sizing: border-box;
53
54
  }
54
- ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, stateParams]) => {
55
- const statesCSS = getStatesCSS(item.id, 'group', ['opacity', 'angle'], stateParams);
55
+ ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
56
56
  return (`
57
57
  .group-${item.id} {
58
58
  opacity: ${layoutParams.opacity};
59
59
  transform: rotate(${area.angle}deg);
60
- transition: all 0.2s ease;
61
60
  }
62
- ${statesCSS}
63
61
  `);
64
62
  })}
65
63
  `}</JSXStyle>
@@ -11,9 +11,7 @@ import { useRegisterResize } from "../../common/useRegisterResize";
11
11
  import { useImageFx } from '../../utils/effects/useImageFx';
12
12
  import { useElementRect } from '../../utils/useElementRect';
13
13
  import { useLayoutContext } from '../useLayoutContext';
14
- import { useStatesClassNames } from '../useStatesClassNames';
15
- import { getStatesCSS } from '../../utils/getStatesCSS';
16
- import { useStatesTransitions } from '../useStatesTransitions';
14
+ import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
17
15
 
18
16
  const baseVariables = `precision mediump float;
19
17
  uniform sampler2D u_image;
@@ -24,15 +22,19 @@ uniform float u_time;
24
22
  uniform vec2 u_cursor;
25
23
  varying vec2 v_texCoord;`;
26
24
 
27
- export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize }) => {
25
+ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize, interactionCtrl }) => {
28
26
  const id = useId();
29
27
  const { layouts } = useCntrlContext();
30
28
  const layoutId = useLayoutContext();
31
- const { radius, strokeWidth, opacity, strokeColor, blur } = useFileItem(item, sectionId);
32
- const angle = useItemAngle(item, sectionId);
33
- const borderColor = useMemo(() => strokeColor ? CntrlColor.parse(strokeColor) : undefined, [strokeColor]);
29
+ const {
30
+ radius: itemRadius,
31
+ strokeWidth: itemStrokeWidth,
32
+ opacity: itemOpacity,
33
+ strokeColor: itemStrokeColor,
34
+ blur: itemBlur
35
+ } = useFileItem(item, sectionId);
36
+ const itemAngle = useItemAngle(item, sectionId);
34
37
  const [wrapperRef, setWrapperRef] = useState<HTMLDivElement | null>(null);
35
- const [imgRef, setImgRef] = useState<HTMLImageElement | HTMLCanvasElement | null>(null);
36
38
  useRegisterResize(wrapperRef, onResize);
37
39
  const { url, hasGLEffect, fragmentShader, FXControls, FXCursor } = item.commonParams;
38
40
  const fxCanvas = useRef<HTMLCanvasElement | null>(null);
@@ -44,17 +46,15 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
44
46
  acc[control.shaderParam] = control.value;
45
47
  return acc;
46
48
  }, {});
47
- const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state];
49
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
48
50
  const fullShaderCode = `${baseVariables}\n${controlsVariables}\n${fragmentShader}`;
49
51
  const area = layoutId ? item.area[layoutId] : null;
50
52
  const exemplary = layouts?.find(l => l.id === layoutId)?.exemplary;
51
53
  const width = area && exemplary ? area.width * exemplary : 0;
52
54
  const height = area && exemplary ? area.height * exemplary : 0;
53
- const statesWrapperClassNames = useStatesClassNames(item.id, item.state, 'image-wrapper');
54
- const statesImgClassNames = useStatesClassNames(item.id, item.state, 'image');
55
- useStatesTransitions(wrapperRef, item.state, ['angle', 'opacity', 'blur']);
56
- useStatesTransitions(imgRef, item.state, ['strokeWidth', 'radius', 'strokeColor']);
57
- useStatesTransitions(fxCanvas.current, item.state, ['strokeWidth', 'radius', 'strokeColor']);
55
+ const wrapperStateParams = interactionCtrl?.getState(['angle', 'opacity', 'blur']);
56
+ const imgStateParams = interactionCtrl?.getState(['strokeWidth', 'radius', 'strokeColor']);
57
+
58
58
  useEffect(() => {
59
59
  isInitialRef.current = false;
60
60
  }, []);
@@ -73,37 +73,47 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
73
73
  const rect = useElementRect(wrapperRef);
74
74
  const rectWidth = Math.floor(rect?.width ?? 0);
75
75
  const rectHeight = Math.floor(rect?.height ?? 0);
76
+ const borderColor = useMemo(() => {
77
+ const borderColor = getStyleFromItemStateAndParams(imgStateParams?.styles?.strokeColor, itemStrokeColor)
78
+ return borderColor ? CntrlColor.parse(borderColor) : undefined;
79
+ }, [itemStrokeColor, imgStateParams?.styles?.strokeColor]);
80
+ const radius = getStyleFromItemStateAndParams(imgStateParams?.styles?.radius, itemRadius);
81
+ const strokeWidth = getStyleFromItemStateAndParams(imgStateParams?.styles?.strokeWidth, itemStrokeWidth);
82
+ const angle = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.angle, itemAngle);
83
+ const opacity = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.opacity, itemOpacity);
84
+ const blur = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.blur, itemBlur);
76
85
  const inlineStyles = {
77
86
  ...(borderColor ? { borderColor: `${borderColor.fmt('rgba')}` } : {}),
78
87
  ...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {}),
79
88
  ...(strokeWidth !== undefined ? { borderWidth: `${strokeWidth * 100}vw` } : {}),
89
+ transition: imgStateParams?.transition ?? 'none'
80
90
  };
81
91
  return (
82
92
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
83
93
  <>
84
94
  <div
85
- className={`image-wrapper-${item.id} ${statesWrapperClassNames}`}
95
+ className={`image-wrapper-${item.id}`}
86
96
  ref={setWrapperRef}
87
97
  style={{
88
98
  ...(opacity !== undefined ? { opacity } : {}),
89
99
  ...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
90
100
  ...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
101
+ transition: wrapperStateParams?.transition ?? 'none'
91
102
  }}
92
103
  >
93
104
  {hasGLEffect && isFXAllowed ? (
94
105
  <canvas
95
106
  style={inlineStyles}
96
107
  ref={fxCanvas}
97
- className={`img-canvas image-${item.id} ${statesImgClassNames}`}
108
+ className={`img-canvas image-${item.id}`}
98
109
  width={rectWidth}
99
110
  height={rectHeight}
100
111
  />
101
112
  ) : (
102
113
  <img
103
114
  alt=""
104
- className={`image image-${item.id} ${statesImgClassNames}`}
115
+ className={`image image-${item.id}`}
105
116
  style={inlineStyles}
106
- ref={setImgRef}
107
117
  src={item.commonParams.url}
108
118
  />
109
119
  )}
@@ -134,24 +144,18 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
134
144
  border-width: 0;
135
145
  box-sizing: border-box;
136
146
  }
137
- ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, stateParams]) => {
138
- const wrapperStatesCSS = getStatesCSS(item.id, 'image-wrapper', ['angle', 'opacity', 'blur'], stateParams);
139
- const imgStatesCSS = getStatesCSS(item.id, 'image', ['strokeWidth', 'radius', 'strokeColor'], stateParams);
147
+ ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
140
148
  return (`
141
149
  .image-wrapper-${item.id} {
142
150
  opacity: ${layoutParams.opacity};
143
151
  transform: rotate(${area.angle}deg);
144
152
  filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
145
- transition: all 0.2s ease;
146
153
  }
147
154
  .image-${item.id} {
148
155
  border-color: ${CntrlColor.parse(layoutParams.strokeColor).fmt('rgba')};
149
156
  border-radius: ${layoutParams.radius * 100}vw;
150
157
  border-width: ${layoutParams.strokeWidth * 100}vw;
151
- transition: all 0.2s ease;
152
158
  }
153
- ${wrapperStatesCSS}
154
- ${imgStatesCSS}
155
159
  `);
156
160
  })}
157
161
  `}</JSXStyle>