@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
@@ -8,29 +8,45 @@ import { useRectangleItem } from './useRectangleItem';
8
8
  import { useItemAngle } from '../useItemAngle';
9
9
  import { useCntrlContext } from '../../provider/useCntrlContext';
10
10
  import { useRegisterResize } from "../../common/useRegisterResize";
11
- import { getStatesCSS } from '../../utils/getStatesCSS';
12
- import { useStatesClassNames } from '../useStatesClassNames';
13
- import { useStatesTransitions } from '../useStatesTransitions';
11
+ import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
14
12
 
15
- export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId, onResize }) => {
13
+ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId, onResize, interactionCtrl }) => {
16
14
  const id = useId();
17
15
  const { layouts } = useCntrlContext();
18
- const { fillColor, radius, strokeWidth, strokeColor, blur, backdropBlur } = useRectangleItem(item, sectionId);
19
- const angle = useItemAngle(item, sectionId);
20
- const backgroundColor = useMemo(() => fillColor ? CntrlColor.parse(fillColor) : undefined, [fillColor]);
21
- const borderColor = useMemo(() => strokeColor ? CntrlColor.parse(strokeColor) : undefined, [strokeColor]);
22
- const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state];
16
+ const {
17
+ fillColor: itemFillColor,
18
+ radius: itemRadius,
19
+ strokeWidth: itemStrokeWidth,
20
+ strokeColor: itemStrokeColor,
21
+ blur: itemBlur,
22
+ backdropBlur: itemBackdropBlur
23
+ } = useRectangleItem(item, sectionId);
24
+ const itemAngle = useItemAngle(item, sectionId);
25
+ const stateParams = interactionCtrl?.getState(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur']);
26
+ const styles = stateParams?.styles ?? {};
27
+ const backgroundColor = useMemo(() => {
28
+ const fillColor = getStyleFromItemStateAndParams(styles?.fillColor, itemFillColor);
29
+ return fillColor ? CntrlColor.parse(fillColor) : undefined;
30
+ }, [itemFillColor, styles?.fillColor]);
31
+ const borderColor = useMemo(() => {
32
+ const strokeColor = getStyleFromItemStateAndParams(styles?.strokeColor, itemStrokeColor);
33
+ return strokeColor ? CntrlColor.parse(strokeColor) : undefined;
34
+ }, [itemStrokeColor, styles?.strokeColor]);
35
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
23
36
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
24
- const statesClassNames = useStatesClassNames(item.id, item.state, 'rectangle');
25
37
  useRegisterResize(ref, onResize);
38
+ const backdropBlur = getStyleFromItemStateAndParams(styles?.backdropBlur, itemBackdropBlur);
39
+ const radius = getStyleFromItemStateAndParams(styles?.radius, itemRadius);
40
+ const strokeWidth = getStyleFromItemStateAndParams(styles?.strokeWidth, itemStrokeWidth);
41
+ const angle = getStyleFromItemStateAndParams(styles?.angle, itemAngle);
42
+ const blur = getStyleFromItemStateAndParams(styles?.blur, itemBlur);
26
43
  const backdropFilterValue = backdropBlur ? `blur(${backdropBlur * 100}vw)`: undefined;
27
- useStatesTransitions(ref, item.state, ['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur']);
28
44
 
29
45
  return (
30
46
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
31
47
  <>
32
48
  <div
33
- className={`rectangle-${item.id} ${statesClassNames}`}
49
+ className={`rectangle-${item.id}`}
34
50
  ref={setRef}
35
51
  style={{
36
52
  ...(backgroundColor ? { backgroundColor : `${backgroundColor.fmt('rgba')}` } : {}),
@@ -43,6 +59,7 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
43
59
  ? { backdropFilter: backdropFilterValue, WebkitBackdropFilter: backdropFilterValue }
44
60
  : {}
45
61
  ),
62
+ transition: stateParams?.transition ?? 'none'
46
63
  }}
47
64
  />
48
65
  <JSXStyle id={id}>{`
@@ -53,8 +70,7 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
53
70
  border-style: solid;
54
71
  box-sizing: border-box;
55
72
  }
56
- ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, states]) => {
57
- const statesCSS = getStatesCSS(item.id, 'rectangle', ['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur'], states);
73
+ ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
58
74
  return (`
59
75
  .rectangle-${item.id} {
60
76
  background-color: ${CntrlColor.parse(layoutParams.fillColor).fmt('rgba')};
@@ -65,9 +81,7 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
65
81
  filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
66
82
  backdrop-filter: ${layoutParams.backdropFilter !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
67
83
  -webkit-backdrop-filter: ${layoutParams.backdropFilter !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
68
- transition: all 0.2s ease;
69
84
  }
70
- ${statesCSS}
71
85
  `);
72
86
  })}
73
87
  `}</JSXStyle>
@@ -10,29 +10,42 @@ import { useRegisterResize } from "../../common/useRegisterResize";
10
10
  import { getFontFamilyValue } from '../../utils/getFontFamilyValue';
11
11
  import { useExemplary } from '../../common/useExemplary';
12
12
  import { useItemAngle } from '../useItemAngle';
13
- import { useStatesClassNames } from '../useStatesClassNames';
14
- import { getStatesCSS } from '../../utils/getStatesCSS';
15
- import { useStatesTransitions } from '../useStatesTransitions';
13
+ import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
16
14
 
17
- export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, onResize }) => {
15
+ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, onResize, interactionCtrl }) => {
18
16
  const [content, styles] = useRichTextItem(item);
19
17
  const id = useId();
20
18
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
21
19
  const { layouts } = useCntrlContext();
22
- const angle = useItemAngle(item, sectionId);
23
- const { blur, wordSpacing, letterSpacing, color, fontSize, lineHeight } = useRichTextItemValues(item, sectionId);
24
- const textColor = useMemo(() => color ? CntrlColor.parse(color) : undefined, [color]);
25
- const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state];
20
+ const itemAngle = useItemAngle(item, sectionId);
21
+ const {
22
+ blur: itemBlur,
23
+ wordSpacing: itemWordSpacing,
24
+ letterSpacing: itemLetterSpacing,
25
+ color: itemColor,
26
+ fontSize,
27
+ lineHeight
28
+ } = useRichTextItemValues(item, sectionId);
29
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
26
30
  const exemplary = useExemplary();
27
- const stateClassNames = useStatesClassNames(item.id, item.state, 'rich-text-wrapper');
28
31
  useRegisterResize(ref, onResize);
29
- useStatesTransitions(ref, item.state, ['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color']);
32
+ const stateParams = interactionCtrl?.getState(['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color']);
33
+ const stateStyles = stateParams?.styles ?? {};
34
+ const transition = stateParams?.transition ?? 'none';
35
+ const textColor = useMemo(() => {
36
+ const color = getStyleFromItemStateAndParams(stateParams?.styles?.color, itemColor);
37
+ return color ? CntrlColor.parse(color) : undefined;
38
+ }, [itemColor, stateStyles.color]);
39
+ const angle = getStyleFromItemStateAndParams(stateStyles.angle, itemAngle);
40
+ const blur = getStyleFromItemStateAndParams(stateStyles.blur, itemBlur);
41
+ const letterSpacing = getStyleFromItemStateAndParams(stateStyles.letterSpacing, itemLetterSpacing);
42
+ const wordSpacing = getStyleFromItemStateAndParams(stateStyles.wordSpacing, itemWordSpacing);
30
43
 
31
44
  return (
32
45
  <>
33
46
  <div
34
47
  ref={setRef}
35
- className={`rich-text-wrapper-${item.id} ${stateClassNames}`}
48
+ className={`rich-text-wrapper-${item.id}`}
36
49
  style={{
37
50
  ...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
38
51
  ...(textColor ? { color: `${textColor.fmt('rgba')}` } : {}),
@@ -41,20 +54,15 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
41
54
  ...(wordSpacing !== undefined ? { wordSpacing: `${wordSpacing * exemplary}px` } : {}),
42
55
  ...(fontSize !== undefined ? { fontSize: `${Math.round(fontSize * exemplary)}px` } : {}),
43
56
  ...(lineHeight !== undefined ? { lineHeight: `${lineHeight * exemplary}px` } : {}),
57
+ transition
44
58
  }}
45
59
  >
46
60
  {content}
47
61
  </div>
48
62
  <JSXStyle id={id}>
49
63
  {styles}
50
- {`${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, states]) => {
64
+ {`${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
51
65
  const color = CntrlColor.parse(layoutParams.color);
52
- const statesCSS = getStatesCSS(
53
- item.id,
54
- 'rich-text-wrapper',
55
- ['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color'],
56
- states
57
- );
58
66
  return (`
59
67
  .rich-text-wrapper-${item.id} {
60
68
  font-size: ${layoutParams.fontSize * 100}vw;
@@ -70,14 +78,12 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
70
78
  transform: rotate(${area.angle}deg);
71
79
  filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
72
80
  text-transform: ${layoutParams.textTransform};
73
- transition: all 0.2s ease;
74
81
  }
75
82
  @supports not (color: oklch(42% 0.3 90 / 1)) {
76
83
  .rich-text-wrapper-${item.id} {
77
84
  color: ${color.fmt('rgba')};
78
85
  }
79
86
  }
80
- ${statesCSS}
81
87
  `);
82
88
  })}`}
83
89
  </JSXStyle>
@@ -10,42 +10,54 @@ import { useCntrlContext } from '../../provider/useCntrlContext';
10
10
  import { useRegisterResize } from "../../common/useRegisterResize";
11
11
  import { useLayoutContext } from '../useLayoutContext';
12
12
  import { ScrollPlaybackVideo } from '../ScrollPlaybackVideo';
13
- import { useStatesClassNames } from '../useStatesClassNames';
14
- import { getStatesCSS } from '../../utils/getStatesCSS';
15
- import { useStatesTransitions } from '../useStatesTransitions';
13
+ import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
16
14
 
17
- export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize }) => {
15
+ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize, interactionCtrl }) => {
18
16
  const id = useId();
19
17
  const { layouts } = useCntrlContext();
20
- const { radius, strokeWidth, strokeColor, opacity, blur } = useFileItem(item, sectionId);
21
- const angle = useItemAngle(item, sectionId);
22
- const borderColor = useMemo(() => strokeColor ? CntrlColor.parse(strokeColor) : undefined, [strokeColor]);
18
+ const {
19
+ radius: itemRadius,
20
+ strokeWidth: itemStrokeWidth,
21
+ strokeColor: itemStrokeColor,
22
+ opacity: itemOpacity,
23
+ blur: itemBlur
24
+ } = useFileItem(item, sectionId);
25
+ const itemAngle = useItemAngle(item, sectionId);
23
26
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
24
27
  const videoRef = useRef<HTMLVideoElement | null>(null);
25
28
  const layoutId = useLayoutContext();
26
29
  const scrollPlayback = layoutId ? item.layoutParams[layoutId].scrollPlayback : null;
27
- const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state];
28
- const statesWrapperClassNames = useStatesClassNames(item.id, item.state, 'video-wrapper');
29
- const statesVideoClassNames = useStatesClassNames(item.id, item.state, 'video');
30
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
30
31
  const hasScrollPlayback = scrollPlayback !== null;
31
- useStatesTransitions(ref, item.state, ['angle', 'opacity', 'blur']);
32
- useStatesTransitions(videoRef.current, item.state, ['strokeWidth', 'radius', 'strokeColor']);
32
+ const wrapperStateParams = interactionCtrl?.getState(['angle', 'opacity', 'blur']);
33
+ const videoStateParams = interactionCtrl?.getState(['strokeWidth', 'radius', 'strokeColor']);
34
+ const borderColor = useMemo(() => {
35
+ const strokeColor = getStyleFromItemStateAndParams(videoStateParams?.styles?.color, itemStrokeColor);
36
+ return strokeColor ? CntrlColor.parse(strokeColor) : undefined;
37
+ }, [videoStateParams?.styles?.color, itemStrokeColor]);
38
+ const angle = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.angle, itemAngle);
39
+ const opacity = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.opacity, itemOpacity);
40
+ const blur = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.blur, itemBlur);
41
+ const strokeWidth = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.strokeWidth, itemStrokeWidth);
42
+ const radius = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.radius, itemRadius);
33
43
  useRegisterResize(ref, onResize);
34
44
  const inlineStyles = {
35
45
  ...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {}),
36
46
  ...(strokeWidth !== undefined ? { borderWidth: `${strokeWidth * 100}vw` } : {}),
37
- ...(borderColor ? { borderColor: `${borderColor.toCss()}` } : {})
47
+ ...(borderColor ? { borderColor: `${borderColor.toCss()}` } : {}),
48
+ transition: videoStateParams?.transition ?? 'none'
38
49
  };
39
50
 
40
51
  return (
41
52
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
42
53
  <div
43
- className={`video-wrapper-${item.id} ${statesWrapperClassNames}`}
54
+ className={`video-wrapper-${item.id}`}
44
55
  ref={setRef}
45
56
  style={{
46
57
  ...(opacity !== undefined ? { opacity } : {}),
47
58
  ...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
48
- ...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})
59
+ ...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
60
+ transition: wrapperStateParams?.transition ?? 'none'
49
61
  }}
50
62
  >
51
63
  {hasScrollPlayback ? (
@@ -54,7 +66,7 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
54
66
  src={item.commonParams.url}
55
67
  playbackParams={scrollPlayback}
56
68
  style={inlineStyles}
57
- className={`video video-playback-wrapper video-${item.id} ${statesWrapperClassNames}`}
69
+ className={`video video-playback-wrapper video-${item.id}`}
58
70
  />
59
71
  ) : (
60
72
  <video
@@ -64,7 +76,7 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
64
76
  muted
65
77
  loop
66
78
  playsInline
67
- className={`video video-${item.id} ${statesVideoClassNames}`}
79
+ className={`video video-${item.id}`}
68
80
  style={inlineStyles}
69
81
  >
70
82
  <source src={item.commonParams.url} />
@@ -92,30 +104,24 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
92
104
  pointer-events: auto;
93
105
  }
94
106
  .video-${item.id} {
95
- border-color: ${strokeColor};
107
+ border-color: ${itemStrokeColor};
96
108
  }
97
109
  .video-playback-wrapper {
98
110
  display: flex;
99
111
  justify-content: center;
100
112
  }
101
- ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, stateParams]) => {
102
- const wrapperStatesCSS = getStatesCSS(item.id, 'video-wrapper', ['angle', 'opacity', 'blur'], stateParams);
103
- const videoStatesCSS = getStatesCSS(item.id, 'video', ['strokeWidth', 'radius', 'strokeColor'], stateParams);
113
+ ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
104
114
  return (`
105
115
  .video-wrapper-${item.id} {
106
116
  opacity: ${layoutParams.opacity};
107
117
  transform: rotate(${area.angle}deg);
108
118
  filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
109
- transition: all 0.2s ease;
110
119
  }
111
120
  .video-${item.id} {
112
121
  border-color: ${CntrlColor.parse(layoutParams.strokeColor).fmt('rgba')};
113
122
  border-radius: ${layoutParams.radius * 100}vw;
114
123
  border-width: ${layoutParams.strokeWidth * 100}vw;
115
- transition: all 0.2s ease;
116
124
  }
117
- ${wrapperStatesCSS}
118
- ${videoStatesCSS}
119
125
  `);
120
126
  })}
121
127
  `}</JSXStyle>
@@ -8,27 +8,31 @@ import { useItemAngle } from '../useItemAngle';
8
8
  import { getLayoutStyles, VimeoEmbedItem as TVimeoEmbedItem } from '@cntrl-site/sdk';
9
9
  import { useCntrlContext } from '../../provider/useCntrlContext';
10
10
  import { useRegisterResize } from "../../common/useRegisterResize";
11
- import { useStatesClassNames } from '../useStatesClassNames';
12
- import { getStatesCSS } from '../../utils/getStatesCSS';
13
- import { useStatesTransitions } from '../useStatesTransitions';
11
+ import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
14
12
 
15
- export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId, onResize }) => {
13
+ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId, onResize, interactionCtrl }) => {
16
14
  const id = useId();
17
15
  const { layouts } = useCntrlContext();
18
- const { radius, blur, opacity } = useEmbedVideoItem(item, sectionId);
16
+ const {
17
+ radius: itemRadius,
18
+ blur: itemBlur,
19
+ opacity: itemOpacity
20
+ } = useEmbedVideoItem(item, sectionId);
19
21
  const [iframeRef, setIframeRef] = useState<HTMLIFrameElement | null>(null);
20
22
  const vimeoPlayer = useMemo(() => iframeRef ? new Player(iframeRef) : undefined, [iframeRef]);
21
- const angle = useItemAngle(item, sectionId);
23
+ const itemAngle = useItemAngle(item, sectionId);
22
24
  const { play, controls, loop, muted, pictureInPicture, url } = item.commonParams;
23
25
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
24
26
  const [imgRef, setImgRef] = useState<HTMLImageElement | null>(null);
25
27
  const [isCoverVisible, setIsCoverVisible] = useState(false);
26
- const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state];
27
- const wrapperClassNames = useStatesClassNames(item.id, item.state, 'embed-video-wrapper');
28
- const videoClassNames = useStatesClassNames(item.id, item.state, 'embed-video');
28
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
29
+ const wrapperStateParams = interactionCtrl?.getState(['angle', 'blur', 'opacity']);
30
+ const frameStateParams = interactionCtrl?.getState(['radius']);
31
+ const angle = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.angle, itemAngle);
32
+ const blur = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.blur, itemBlur);
33
+ const opacity = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.opacity, itemOpacity);
34
+ const radius = getStyleFromItemStateAndParams(frameStateParams?.styles?.radius, itemRadius);
29
35
  useRegisterResize(ref, onResize);
30
- useStatesTransitions(ref, item.state, ['angle', 'blur', 'opacity']);
31
- useStatesTransitions(iframeRef, item.state, ['radius']);
32
36
  const getValidVimeoUrl = (url: string): string => {
33
37
  const validURL = new URL(url);
34
38
  validURL.searchParams.append('controls', String(controls));
@@ -66,12 +70,13 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId
66
70
  return (
67
71
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
68
72
  <div
69
- className={`embed-video-wrapper-${item.id} ${wrapperClassNames}`}
73
+ className={`embed-video-wrapper-${item.id}`}
70
74
  ref={setRef}
71
75
  style={{
72
76
  ...(opacity !== undefined ? { opacity } : {}),
73
77
  ...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
74
78
  ...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
79
+ transition: wrapperStateParams?.transition ?? 'none'
75
80
  }}
76
81
  onMouseEnter={() => {
77
82
  if (!vimeoPlayer || play !== 'on-hover') return;
@@ -102,12 +107,13 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId
102
107
  )}
103
108
  <iframe
104
109
  ref={setIframeRef}
105
- className={`embed-video ${videoClassNames}`}
110
+ className={`embed-video`}
106
111
  src={validUrl || ''}
107
112
  allow="autoplay; fullscreen; picture-in-picture;"
108
113
  allowFullScreen
109
114
  style={{
110
- ...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})
115
+ ...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {}),
116
+ transition: frameStateParams?.transition ?? 'none'
111
117
  }}
112
118
  />
113
119
  </div>
@@ -124,22 +130,16 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId
124
130
  border: none;
125
131
  overflow: hidden;
126
132
  }
127
- ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, stateParams]) => {
128
- const wrapperStatesCSS = getStatesCSS(item.id, 'embed-video-wrapper', ['angle', 'blur', 'opacity'], stateParams);
129
- const videoStatesCSS = getStatesCSS(item.id, 'embed-video', ['radius'], stateParams);
133
+ ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
130
134
  return (`
131
135
  .embed-video-wrapper-${item.id} {
132
136
  opacity: ${layoutParams.opacity};
133
137
  transform: rotate(${area.angle}deg);
134
138
  filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
135
- transition: all 0.2s ease;
136
139
  }
137
140
  .embed-video-wrapper-${item.id} .embed-video {
138
141
  border-radius: ${layoutParams.radius * 100}vw;
139
- transition: all 0.2s ease;
140
142
  }
141
- ${wrapperStatesCSS}
142
- ${videoStatesCSS}
143
143
  `);
144
144
  })}
145
145
  `}</JSXStyle>
@@ -10,28 +10,27 @@ import { useCntrlContext } from '../../provider/useCntrlContext';
10
10
  import { useYouTubeIframeApi } from '../../utils/Youtube/useYouTubeIframeApi';
11
11
  import { YTPlayer } from '../../utils/Youtube/YoutubeIframeApi';
12
12
  import { useRegisterResize } from "../../common/useRegisterResize";
13
- import { useStatesClassNames } from '../useStatesClassNames';
14
- import { getStatesCSS } from '../../utils/getStatesCSS';
15
- import { useStatesTransitions } from '../useStatesTransitions';
13
+ import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
16
14
 
17
- export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, sectionId, onResize }) => {
15
+ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, sectionId, onResize, interactionCtrl }) => {
18
16
  const id = useId();
19
17
  const { layouts } = useCntrlContext();
20
18
  const { play, controls, url } = item.commonParams;
21
- const { radius, blur, opacity } = useEmbedVideoItem(item, sectionId);
22
- const angle = useItemAngle(item, sectionId);
19
+ const { radius: itemRadius, blur: itemBlur, opacity: itemOpacity } = useEmbedVideoItem(item, sectionId);
20
+ const itemAngle = useItemAngle(item, sectionId);
23
21
  const YT = useYouTubeIframeApi();
24
22
  const [div, setDiv] = useState<HTMLDivElement | null>(null);
25
- const [wrapperRef, setWrapperRef] = useState<HTMLDivElement | null>(null);
26
23
  const [player, setPlayer] = useState<YTPlayer | undefined>(undefined);
27
24
  const [isCoverVisible, setIsCoverVisible] = useState(false);
28
25
  const [imgRef, setImgRef] = useState<HTMLImageElement | null>(null);
29
- const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state];
30
- const wrapperClassNames = useStatesClassNames(item.id, item.state, 'embed-youtube-video-wrapper');
31
- const embedClassNames = useStatesClassNames(item.id, item.state, 'embed');
26
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
27
+ const wrapperStateParams = interactionCtrl?.getState(['angle', 'blur', 'opacity']);
28
+ const frameStateParams = interactionCtrl?.getState(['radius']);
29
+ const angle = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.angle, itemAngle);
30
+ const blur = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.blur, itemBlur);
31
+ const opacity = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.opacity, itemOpacity);
32
+ const radius = getStyleFromItemStateAndParams(frameStateParams?.styles?.radius, itemRadius);
32
33
  useRegisterResize(div, onResize);
33
- useStatesTransitions(wrapperRef, item.state, ['angle', 'blur', 'opacity']);
34
- useStatesTransitions(div, item.state, ['radius']);
35
34
  useEffect(() => {
36
35
  const newUrl = new URL(url);
37
36
  const videoId = getYoutubeId(newUrl);
@@ -84,8 +83,7 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
84
83
  return (
85
84
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
86
85
  <div
87
- ref={setWrapperRef}
88
- className={`embed-youtube-video-wrapper-${item.id} ${wrapperClassNames}`}
86
+ className={`embed-youtube-video-wrapper-${item.id}`}
89
87
  onMouseEnter={() => {
90
88
  if (!player || play !== 'on-hover') return;
91
89
  player.playVideo();
@@ -98,6 +96,7 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
98
96
  ...(opacity !== undefined ? { opacity } : {}),
99
97
  ...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
100
98
  ...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
99
+ transition: wrapperStateParams?.transition ?? 'none'
101
100
  }}
102
101
  >
103
102
  {item.commonParams.coverUrl && (
@@ -120,10 +119,11 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
120
119
  />
121
120
  )}
122
121
  <div
123
- className={`embed-${item.id} ${embedClassNames}`}
122
+ className={`embed-${item.id}`}
124
123
  ref={setDiv}
125
124
  style={{
126
- ...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {}),
125
+ ...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {}),
126
+ transition: frameStateParams?.transition ?? 'none'
127
127
  }}
128
128
  />
129
129
  </div>
@@ -143,22 +143,16 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
143
143
  z-index: 1;
144
144
  border: none;
145
145
  }
146
- ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, stateParams]) => {
147
- const wrapperStatesCSS = getStatesCSS(item.id, 'embed-youtube-video-wrapper', ['angle', 'blur', 'opacity'], stateParams);
148
- const embedStatesCSS = getStatesCSS(item.id, 'embed', ['radius'], stateParams);
146
+ ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
149
147
  return (`
150
148
  .embed-youtube-video-wrapper-${item.id} {
151
149
  opacity: ${layoutParams.opacity};
152
150
  transform: rotate(${area.angle}deg);
153
151
  filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
154
- transition: all 0.2s ease;
155
152
  }
156
153
  .embed-youtube-video-wrapper-${item.id} .embed-${item.id} {
157
154
  border-radius: ${layoutParams.radius * 100}vw;
158
- transition: all 0.2s ease;
159
155
  }
160
- ${wrapperStatesCSS}
161
- ${embedStatesCSS}
162
156
  `);
163
157
  })}
164
158
  `}</JSXStyle>
@@ -3,7 +3,11 @@ import { useKeyframeValue } from '../common/useKeyframeValue';
3
3
  import { getItemTopStyle } from '../utils/getItemTopStyle';
4
4
  import { useLayoutContext } from './useLayoutContext';
5
5
 
6
- export const useItemPosition = (item: ItemAny, sectionId: string) => {
6
+ export const useItemPosition = (
7
+ item: ItemAny,
8
+ sectionId: string,
9
+ stateValues: { top?: number; left?: number; }
10
+ ) => {
7
11
  const layoutId = useLayoutContext();
8
12
  const position = useKeyframeValue<{ top: number; left: number } | undefined>(
9
13
  item,
@@ -18,12 +22,15 @@ export const useItemPosition = (item: ItemAny, sectionId: string) => {
18
22
  );
19
23
  const anchorSide = layoutId ? item.area[layoutId].anchorSide : AnchorSide.Top;
20
24
  const positionType = layoutId ? item.area[layoutId].positionType : PositionType.ScreenBased;
21
- // tp prevent fixed item (with anchor point bottom) to jump when scroll in safari on mobile
25
+ // to prevent fixed item (with anchor point bottom) to jump when scroll in safari on mobile
22
26
  const isScreenBasedBottom = positionType === PositionType.ScreenBased && anchorSide === AnchorSide.Bottom;
27
+ if (!position) return undefined;
28
+ const top = stateValues?.top ?? position.top;
29
+ const left = stateValues?.left ?? position.left;
23
30
  return position ? {
24
- bottom: isScreenBasedBottom ? `${-position.top * 100}vw` : 'unset',
25
- top: isScreenBasedBottom ? 'unset' : getItemTopStyle(position.top, anchorSide),
26
- left: `${position.left * 100}vw`
31
+ bottom: isScreenBasedBottom ? `${-top * 100}vw` : 'unset',
32
+ top: isScreenBasedBottom ? 'unset' : getItemTopStyle(top, anchorSide),
33
+ left: `${left * 100}vw`
27
34
  } : undefined;
28
35
  };
29
36
 
@@ -0,0 +1,38 @@
1
+ import { ArticleItemType, ItemState } from '@cntrl-site/sdk';
2
+
3
+ export const CSSPropertyNameMap: Record<keyof ItemState<ArticleItemType>, string> = {
4
+ 'width': 'width',
5
+ 'height': 'height',
6
+ 'top': 'top',
7
+ 'left': 'left',
8
+ 'scale': 'transform',
9
+ 'angle': 'transform',
10
+ 'opacity': 'opacity',
11
+ 'radius': 'border-radius',
12
+ 'strokeWidth': 'border-width',
13
+ 'strokeColor': 'border-color',
14
+ 'fillColor': 'background-color',
15
+ 'blur': 'filter',
16
+ 'backdropBlur': 'backdrop-filter',
17
+ 'letterSpacing': 'letter-spacing',
18
+ 'wordSpacing': 'word-spacing',
19
+ 'color': 'color'
20
+ };
21
+
22
+ const PropertyNameCSSMap: Record<string, string[]> = {
23
+ 'transform': ['angle', 'scale'],
24
+ 'border-radius': ['radius'],
25
+ 'border-width': ['strokeWidth'],
26
+ 'border-color': ['strokeColor'],
27
+ 'background-color': ['fillColor'],
28
+ 'filter': ['blur'],
29
+ 'backdrop-filter': ['backdrop-blur'],
30
+ 'letter-spacing': ['letterSpacing'],
31
+ 'word-spacing': ['wordSpacing'],
32
+ };
33
+
34
+
35
+ export function getStyleKeysFromCSSProperty(cssProp: string): string[] {
36
+ const key = PropertyNameCSSMap[cssProp] ?? [cssProp];
37
+ return key;
38
+ }