@cntrl-site/sdk-nextjs 1.0.18 → 1.0.19-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  2. package/.idea/inspectionProfiles/Project_Default.xml +15 -0
  3. package/jest.config.js +2 -2
  4. package/lib/components/Article.js +9 -8
  5. package/lib/components/Item.js +33 -13
  6. package/lib/components/items/CodeEmbedItem.js +11 -8
  7. package/lib/components/items/CustomItem.js +10 -7
  8. package/lib/components/items/GroupItem.js +11 -8
  9. package/lib/components/items/ImageItem.js +21 -15
  10. package/lib/components/items/RectangleItem.js +11 -8
  11. package/lib/components/items/RichTextItem.js +10 -7
  12. package/lib/components/items/VideoItem.js +16 -12
  13. package/lib/components/items/VimeoEmbed.js +19 -15
  14. package/lib/components/items/YoutubeEmbed.js +18 -13
  15. package/lib/components/useStatesClassNames.js +18 -0
  16. package/lib/components/useStatesTransitions.js +89 -0
  17. package/lib/provider/InteractionsContext.js +45 -0
  18. package/lib/utils/{HoverStyles/HoverStyles.js → StateStyles/StateStyles.js} +36 -24
  19. package/lib/utils/getStatesCSS.js +16 -0
  20. package/package.json +7 -4
  21. package/src/components/Article.tsx +31 -28
  22. package/src/components/ArticleWrapper.tsx +1 -2
  23. package/src/components/Item.tsx +38 -18
  24. package/src/components/items/CodeEmbedItem.tsx +12 -9
  25. package/src/components/items/CustomItem.tsx +12 -9
  26. package/src/components/items/GroupItem.tsx +12 -9
  27. package/src/components/items/ImageItem.tsx +26 -19
  28. package/src/components/items/RectangleItem.tsx +13 -10
  29. package/src/components/items/RichTextItem.tsx +17 -9
  30. package/src/components/items/VideoItem.tsx +19 -15
  31. package/src/components/items/VimeoEmbed.tsx +20 -16
  32. package/src/components/items/YoutubeEmbed.tsx +20 -16
  33. package/src/components/useStatesClassNames.ts +23 -0
  34. package/src/components/useStatesTransitions.ts +95 -0
  35. package/src/provider/InteractionsContext.test.tsx +97 -0
  36. package/src/provider/InteractionsContext.tsx +65 -0
  37. package/src/utils/{HoverStyles/HoverStyles.ts → StateStyles/StateStyles.ts} +41 -27
  38. package/src/utils/getStatesCSS.ts +24 -0
  39. package/cntrl-site-sdk-nextjs-1.0.17.tgz +0 -0
  40. package/cntrl-site-sdk-nextjs-1.0.18.tgz +0 -0
@@ -13,10 +13,12 @@ const LinkWrapper_1 = require("../LinkWrapper");
13
13
  const useFileItem_1 = require("./useFileItem");
14
14
  const useItemAngle_1 = require("../useItemAngle");
15
15
  const useCntrlContext_1 = require("../../provider/useCntrlContext");
16
- const HoverStyles_1 = require("../../utils/HoverStyles/HoverStyles");
17
16
  const useRegisterResize_1 = require("../../common/useRegisterResize");
18
17
  const useLayoutContext_1 = require("../useLayoutContext");
19
18
  const ScrollPlaybackVideo_1 = require("../ScrollPlaybackVideo");
19
+ const useStatesClassNames_1 = require("../useStatesClassNames");
20
+ const getStatesCSS_1 = require("../../utils/getStatesCSS");
21
+ const useStatesTransitions_1 = require("../useStatesTransitions");
20
22
  const VideoItem = ({ item, sectionId, onResize }) => {
21
23
  var _a, _b, _c;
22
24
  const id = (0, react_1.useId)();
@@ -28,11 +30,15 @@ const VideoItem = ({ item, sectionId, onResize }) => {
28
30
  const videoRef = (0, react_1.useRef)(null);
29
31
  const layoutId = (0, useLayoutContext_1.useLayoutContext)();
30
32
  const scrollPlayback = layoutId ? item.layoutParams[layoutId].scrollPlayback : null;
31
- const layoutValues = [item.area, item.layoutParams, item.state.hover];
33
+ const layoutValues = [item.area, item.layoutParams, item.state];
34
+ const statesWrapperClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'video-wrapper');
35
+ const statesVideoClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'video');
32
36
  const hasScrollPlayback = scrollPlayback !== null;
37
+ (0, useStatesTransitions_1.useStatesTransitions)(ref, item.state, ['angle', 'opacity', 'blur']);
38
+ (0, useStatesTransitions_1.useStatesTransitions)(videoRef.current, item.state, ['strokeWidth', 'radius', 'strokeColor']);
33
39
  (0, useRegisterResize_1.useRegisterResize)(ref, onResize);
34
40
  const inlineStyles = Object.assign(Object.assign(Object.assign({}, (radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})), (strokeWidth !== undefined ? { borderWidth: `${strokeWidth * 100}vw` } : {})), (borderColor ? { borderColor: `${borderColor.toCss()}` } : {}));
35
- return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: (_a = item.link) === null || _a === void 0 ? void 0 : _a.url, target: (_b = item.link) === null || _b === void 0 ? void 0 : _b.target, children: [(0, jsx_runtime_1.jsx)("div", { className: `video-wrapper-${item.id}`, ref: setRef, style: Object.assign(Object.assign(Object.assign({}, (opacity !== undefined ? { opacity } : {})), (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), children: hasScrollPlayback ? ((0, jsx_runtime_1.jsx)(ScrollPlaybackVideo_1.ScrollPlaybackVideo, { sectionId: sectionId, src: item.commonParams.url, playbackParams: scrollPlayback, style: inlineStyles, className: `video video-playback-wrapper video-${item.id}` })) : ((0, jsx_runtime_1.jsx)("video", { poster: (_c = item.commonParams.coverUrl) !== null && _c !== void 0 ? _c : '', ref: videoRef, autoPlay: true, muted: true, loop: true, playsInline: true, className: `video video-${item.id}`, style: inlineStyles, children: (0, jsx_runtime_1.jsx)("source", { src: item.commonParams.url }) })) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
41
+ return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: (_a = item.link) === null || _a === void 0 ? void 0 : _a.url, target: (_b = item.link) === null || _b === void 0 ? void 0 : _b.target, children: [(0, jsx_runtime_1.jsx)("div", { className: `video-wrapper-${item.id} ${statesWrapperClassNames}`, ref: setRef, style: Object.assign(Object.assign(Object.assign({}, (opacity !== undefined ? { opacity } : {})), (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), children: hasScrollPlayback ? ((0, jsx_runtime_1.jsx)(ScrollPlaybackVideo_1.ScrollPlaybackVideo, { sectionId: sectionId, src: item.commonParams.url, playbackParams: scrollPlayback, style: inlineStyles, className: `video video-playback-wrapper video-${item.id} ${statesWrapperClassNames}` })) : ((0, jsx_runtime_1.jsx)("video", { poster: (_c = item.commonParams.coverUrl) !== null && _c !== void 0 ? _c : '', ref: videoRef, autoPlay: true, muted: true, loop: true, playsInline: true, className: `video video-${item.id} ${statesVideoClassNames}`, style: inlineStyles, children: (0, jsx_runtime_1.jsx)("source", { src: item.commonParams.url }) })) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
36
42
  .video-wrapper-${item.id} {
37
43
  position: absolute;
38
44
  overflow: hidden;
@@ -59,26 +65,24 @@ const VideoItem = ({ item, sectionId, onResize }) => {
59
65
  display: flex;
60
66
  justify-content: center;
61
67
  }
62
- ${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams, hoverParams]) => {
68
+ ${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams, stateParams]) => {
69
+ const wrapperStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'video-wrapper', ['angle', 'opacity', 'blur'], stateParams);
70
+ const videoStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'video', ['strokeWidth', 'radius', 'strokeColor'], stateParams);
63
71
  return (`
64
72
  .video-wrapper-${item.id} {
65
73
  opacity: ${layoutParams.opacity};
66
74
  transform: rotate(${area.angle}deg);
67
75
  filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
68
- transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'opacity', 'blur'], hoverParams)};
69
- }
70
- .video-wrapper-${item.id}:hover {
71
- ${(0, HoverStyles_1.getHoverStyles)(['angle', 'opacity', 'blur'], hoverParams)};
76
+ transition: all 0.2s ease;
72
77
  }
73
78
  .video-${item.id} {
74
79
  border-color: ${color_1.CntrlColor.parse(layoutParams.strokeColor).fmt('rgba')};
75
80
  border-radius: ${layoutParams.radius * 100}vw;
76
81
  border-width: ${layoutParams.strokeWidth * 100}vw;
77
- transition: ${(0, HoverStyles_1.getTransitions)(['strokeWidth', 'radius', 'strokeColor'], hoverParams)};
78
- }
79
- .video-wrapper-${item.id}:hover .video {
80
- ${(0, HoverStyles_1.getHoverStyles)(['strokeWidth', 'radius', 'strokeColor'], hoverParams)};
82
+ transition: all 0.2s ease;
81
83
  }
84
+ ${wrapperStatesCSS}
85
+ ${videoStatesCSS}
82
86
  `);
83
87
  })}
84
88
  ` })] }));
@@ -13,8 +13,10 @@ const useEmbedVideoItem_1 = require("./useEmbedVideoItem");
13
13
  const useItemAngle_1 = require("../useItemAngle");
14
14
  const sdk_1 = require("@cntrl-site/sdk");
15
15
  const useCntrlContext_1 = require("../../provider/useCntrlContext");
16
- const HoverStyles_1 = require("../../utils/HoverStyles/HoverStyles");
17
16
  const useRegisterResize_1 = require("../../common/useRegisterResize");
17
+ const useStatesClassNames_1 = require("../useStatesClassNames");
18
+ const getStatesCSS_1 = require("../../utils/getStatesCSS");
19
+ const useStatesTransitions_1 = require("../useStatesTransitions");
18
20
  const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
19
21
  var _a, _b, _c;
20
22
  const id = (0, react_1.useId)();
@@ -27,8 +29,12 @@ const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
27
29
  const [ref, setRef] = (0, react_1.useState)(null);
28
30
  const [imgRef, setImgRef] = (0, react_1.useState)(null);
29
31
  const [isCoverVisible, setIsCoverVisible] = (0, react_1.useState)(false);
30
- const layoutValues = [item.area, item.layoutParams, item.state.hover];
32
+ const layoutValues = [item.area, item.layoutParams, item.state];
33
+ const wrapperClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-video-wrapper');
34
+ const videoClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-video');
31
35
  (0, useRegisterResize_1.useRegisterResize)(ref, onResize);
36
+ (0, useStatesTransitions_1.useStatesTransitions)(ref, item.state, ['angle', 'blur', 'opacity']);
37
+ (0, useStatesTransitions_1.useStatesTransitions)(iframeRef, item.state, ['radius']);
32
38
  const getValidVimeoUrl = (url) => {
33
39
  const validURL = new URL(url);
34
40
  validURL.searchParams.append('controls', String(controls));
@@ -61,7 +67,7 @@ const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
61
67
  vimeoPlayer.play();
62
68
  setIsCoverVisible(false);
63
69
  };
64
- return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: (_a = item.link) === null || _a === void 0 ? void 0 : _a.url, target: (_b = item.link) === null || _b === void 0 ? void 0 : _b.target, children: [(0, jsx_runtime_1.jsxs)("div", { className: `embed-video-wrapper-${item.id}`, ref: setRef, style: Object.assign(Object.assign(Object.assign({}, (opacity !== undefined ? { opacity } : {})), (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), onMouseEnter: () => {
70
+ return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: (_a = item.link) === null || _a === void 0 ? void 0 : _a.url, target: (_b = item.link) === null || _b === void 0 ? void 0 : _b.target, children: [(0, jsx_runtime_1.jsxs)("div", { className: `embed-video-wrapper-${item.id} ${wrapperClassNames}`, ref: setRef, style: Object.assign(Object.assign(Object.assign({}, (opacity !== undefined ? { opacity } : {})), (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), onMouseEnter: () => {
65
71
  if (!vimeoPlayer || play !== 'on-hover')
66
72
  return;
67
73
  vimeoPlayer.play();
@@ -78,37 +84,35 @@ const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
78
84
  width: '100%',
79
85
  top: '0',
80
86
  left: '0'
81
- }, alt: "Cover img" })), (0, jsx_runtime_1.jsx)("iframe", { ref: setIframeRef, className: "embedVideo", src: validUrl || '', allow: "autoplay; fullscreen; picture-in-picture;", allowFullScreen: true, style: Object.assign({}, (radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})) })] }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
87
+ }, alt: "Cover img" })), (0, jsx_runtime_1.jsx)("iframe", { ref: setIframeRef, className: `embed-video ${videoClassNames}`, src: validUrl || '', allow: "autoplay; fullscreen; picture-in-picture;", allowFullScreen: true, style: Object.assign({}, (radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})) })] }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
82
88
  .embed-video-wrapper-${item.id} {
83
89
  position: absolute;
84
90
  width: 100%;
85
91
  height: 100%;
86
92
  }
87
- .embedVideo {
93
+ .embed-video {
88
94
  width: 100%;
89
95
  height: 100%;
90
96
  z-index: 1;
91
97
  border: none;
92
98
  overflow: hidden;
93
99
  }
94
- ${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams, hoverParams]) => {
100
+ ${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams, stateParams]) => {
101
+ const wrapperStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'embed-video-wrapper', ['angle', 'blur', 'opacity'], stateParams);
102
+ const videoStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'embed-video', ['radius'], stateParams);
95
103
  return (`
96
104
  .embed-video-wrapper-${item.id} {
97
105
  opacity: ${layoutParams.opacity};
98
106
  transform: rotate(${area.angle}deg);
99
107
  filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
100
- transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'blur', 'opacity'], hoverParams)};
108
+ transition: all 0.2s ease;
101
109
  }
102
- .embed-video-wrapper-${item.id}:hover {
103
- ${(0, HoverStyles_1.getHoverStyles)(['angle', 'blur', 'opacity'], hoverParams)}
104
- }
105
- .embed-video-wrapper-${item.id} .embedVideo {
110
+ .embed-video-wrapper-${item.id} .embed-video {
106
111
  border-radius: ${layoutParams.radius * 100}vw;
107
- transition: ${(0, HoverStyles_1.getTransitions)(['radius'], hoverParams)};
108
- }
109
- .embed-video-wrapper-${item.id}:hover .embedVideo {
110
- ${(0, HoverStyles_1.getHoverStyles)(['radius'], hoverParams)};
112
+ transition: all 0.2s ease;
111
113
  }
114
+ ${wrapperStatesCSS}
115
+ ${videoStatesCSS}
112
116
  `);
113
117
  })}
114
118
  ` })] }));
@@ -12,10 +12,12 @@ const getValidYoutubeUrl_1 = require("../../utils/getValidYoutubeUrl");
12
12
  const useEmbedVideoItem_1 = require("./useEmbedVideoItem");
13
13
  const useItemAngle_1 = require("../useItemAngle");
14
14
  const sdk_1 = require("@cntrl-site/sdk");
15
- const HoverStyles_1 = require("../../utils/HoverStyles/HoverStyles");
16
15
  const useCntrlContext_1 = require("../../provider/useCntrlContext");
17
16
  const useYouTubeIframeApi_1 = require("../../utils/Youtube/useYouTubeIframeApi");
18
17
  const useRegisterResize_1 = require("../../common/useRegisterResize");
18
+ const useStatesClassNames_1 = require("../useStatesClassNames");
19
+ const getStatesCSS_1 = require("../../utils/getStatesCSS");
20
+ const useStatesTransitions_1 = require("../useStatesTransitions");
19
21
  const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
20
22
  var _a, _b, _c;
21
23
  const id = (0, react_1.useId)();
@@ -25,11 +27,16 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
25
27
  const angle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
26
28
  const YT = (0, useYouTubeIframeApi_1.useYouTubeIframeApi)();
27
29
  const [div, setDiv] = (0, react_1.useState)(null);
30
+ const [wrapperRef, setWrapperRef] = (0, react_1.useState)(null);
28
31
  const [player, setPlayer] = (0, react_1.useState)(undefined);
29
32
  const [isCoverVisible, setIsCoverVisible] = (0, react_1.useState)(false);
30
33
  const [imgRef, setImgRef] = (0, react_1.useState)(null);
31
- const layoutValues = [item.area, item.layoutParams, item.state.hover];
34
+ const layoutValues = [item.area, item.layoutParams, item.state];
35
+ const wrapperClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-youtube-video-wrapper');
36
+ const embedClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed');
32
37
  (0, useRegisterResize_1.useRegisterResize)(div, onResize);
38
+ (0, useStatesTransitions_1.useStatesTransitions)(wrapperRef, item.state, ['angle', 'blur', 'opacity']);
39
+ (0, useStatesTransitions_1.useStatesTransitions)(div, item.state, ['radius']);
33
40
  (0, react_1.useEffect)(() => {
34
41
  const newUrl = new URL(url);
35
42
  const videoId = (0, getValidYoutubeUrl_1.getYoutubeId)(newUrl);
@@ -80,7 +87,7 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
80
87
  setIsCoverVisible(true);
81
88
  }
82
89
  }, []);
83
- return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: (_a = item.link) === null || _a === void 0 ? void 0 : _a.url, target: (_b = item.link) === null || _b === void 0 ? void 0 : _b.target, children: [(0, jsx_runtime_1.jsxs)("div", { className: `embed-youtube-video-wrapper-${item.id}`, onMouseEnter: () => {
90
+ return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: (_a = item.link) === null || _a === void 0 ? void 0 : _a.url, target: (_b = item.link) === null || _b === void 0 ? void 0 : _b.target, children: [(0, jsx_runtime_1.jsxs)("div", { ref: setWrapperRef, className: `embed-youtube-video-wrapper-${item.id} ${wrapperClassNames}`, onMouseEnter: () => {
84
91
  if (!player || play !== 'on-hover')
85
92
  return;
86
93
  player.playVideo();
@@ -98,7 +105,7 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
98
105
  top: '0',
99
106
  left: '0',
100
107
  zIndex: 1
101
- }, alt: "Cover img" })), (0, jsx_runtime_1.jsx)("div", { className: `embed-${item.id}`, ref: setDiv, style: Object.assign({}, (radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})) })] }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
108
+ }, alt: "Cover img" })), (0, jsx_runtime_1.jsx)("div", { className: `embed-${item.id} ${embedClassNames}`, ref: setDiv, style: Object.assign({}, (radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})) })] }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
102
109
  .embed-youtube-video-wrapper-${item.id},
103
110
  .embed-${item.id} {
104
111
  position: absolute;
@@ -114,24 +121,22 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
114
121
  z-index: 1;
115
122
  border: none;
116
123
  }
117
- ${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams, hoverParams]) => {
124
+ ${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams, stateParams]) => {
125
+ const wrapperStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'embed-youtube-video-wrapper', ['angle', 'blur', 'opacity'], stateParams);
126
+ const embedStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'embed', ['radius'], stateParams);
118
127
  return (`
119
128
  .embed-youtube-video-wrapper-${item.id} {
120
129
  opacity: ${layoutParams.opacity};
121
130
  transform: rotate(${area.angle}deg);
122
131
  filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
123
- transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'blur', 'opacity'], hoverParams)};
132
+ transition: all 0.2s ease;
124
133
  }
125
134
  .embed-youtube-video-wrapper-${item.id} .embed-${item.id} {
126
135
  border-radius: ${layoutParams.radius * 100}vw;
127
- transition: ${(0, HoverStyles_1.getTransitions)(['radius'], hoverParams)};
128
- }
129
- .embed-youtube-video-wrapper-${item.id}:hover {
130
- ${(0, HoverStyles_1.getHoverStyles)(['angle', 'blur', 'opacity'], hoverParams)};
131
- }
132
- .embed-youtube-video-wrapper-${item.id}:hover .embed-${item.id} {
133
- ${(0, HoverStyles_1.getHoverStyles)(['radius'], hoverParams)};
136
+ transition: all 0.2s ease;
134
137
  }
138
+ ${wrapperStatesCSS}
139
+ ${embedStatesCSS}
135
140
  `);
136
141
  })}
137
142
  ` })] }));
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useStatesClassNames = void 0;
4
+ const react_1 = require("react");
5
+ const InteractionsContext_1 = require("../provider/InteractionsContext");
6
+ const useCurrentLayout_1 = require("../common/useCurrentLayout");
7
+ function useStatesClassNames(itemId, itemStates, uniquePrefix) {
8
+ const { interactionsStatesMap } = (0, react_1.useContext)(InteractionsContext_1.InteractionsContext);
9
+ const { layoutId } = (0, useCurrentLayout_1.useCurrentLayout)();
10
+ const activeStates = Object.values(interactionsStatesMap);
11
+ const statesForLayout = layoutId ? itemStates[layoutId] : {};
12
+ const stateClassNames = Object.keys(statesForLayout !== null && statesForLayout !== void 0 ? statesForLayout : {})
13
+ .filter((stateId) => activeStates.includes(stateId))
14
+ .map((stateId) => `${uniquePrefix}-${itemId}-state-${stateId}`)
15
+ .join(' ');
16
+ return stateClassNames;
17
+ }
18
+ exports.useStatesClassNames = useStatesClassNames;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useStatesTransitions = void 0;
4
+ const react_1 = require("react");
5
+ const InteractionsContext_1 = require("../provider/InteractionsContext");
6
+ const useCurrentLayout_1 = require("../common/useCurrentLayout");
7
+ function useStatesTransitions(el, state, values) {
8
+ const { interactionsStatesMap } = (0, react_1.useContext)(InteractionsContext_1.InteractionsContext);
9
+ const { layoutId } = (0, useCurrentLayout_1.useCurrentLayout)();
10
+ const activeStates = Object.values(interactionsStatesMap);
11
+ const statesForLayout = (0, react_1.useMemo)(() => layoutId ? state[layoutId] : {}, [layoutId]);
12
+ const itemStatesIds = Object.keys(statesForLayout);
13
+ const itemActiveStateId = activeStates.find((stateId) => itemStatesIds.includes(stateId));
14
+ (0, react_1.useEffect)(() => {
15
+ if (!itemActiveStateId || !el)
16
+ return;
17
+ const state = statesForLayout[itemActiveStateId];
18
+ const transitionStr = getTransition(state, 'in', values);
19
+ const slowestProp = getSlowestProperty(state, 'in', values);
20
+ if (!transitionStr)
21
+ return;
22
+ el.style.transition = transitionStr;
23
+ el.ontransitionend = (e) => {
24
+ e.stopPropagation();
25
+ if (e.target !== el || e.propertyName !== slowestProp)
26
+ return;
27
+ el.style.transition = 'none';
28
+ };
29
+ return () => {
30
+ const transitionStr = getTransition(state, 'out', values);
31
+ const slowestProp = getSlowestProperty(state, 'out', values);
32
+ if (!transitionStr)
33
+ return;
34
+ el.style.transition = transitionStr;
35
+ el.ontransitionend = (e) => {
36
+ e.stopPropagation();
37
+ if (e.target !== el || e.propertyName !== slowestProp)
38
+ return;
39
+ el.style.transition = 'none';
40
+ };
41
+ };
42
+ }, [itemActiveStateId, statesForLayout, el]);
43
+ }
44
+ exports.useStatesTransitions = useStatesTransitions;
45
+ const CSSPropertyNameMap = {
46
+ 'width': 'width',
47
+ 'height': 'height',
48
+ 'top': 'top',
49
+ 'left': 'left',
50
+ 'scale': 'transform',
51
+ 'angle': 'transform',
52
+ 'opacity': 'opacity',
53
+ 'radius': 'border-radius',
54
+ 'strokeWidth': 'border-width',
55
+ 'strokeColor': 'border-color',
56
+ 'fillColor': 'background-color',
57
+ 'blur': 'filter',
58
+ 'backdropBlur': 'backdrop-filter',
59
+ 'letterSpacing': 'letter-spacing',
60
+ 'wordSpacing': 'word-spacing',
61
+ 'color': 'color'
62
+ };
63
+ function getTransition(state, direction, values) {
64
+ return Object.entries(state)
65
+ .filter(([key]) => values.includes(key))
66
+ .map(([key, params]) => {
67
+ const cssKey = CSSPropertyNameMap[key];
68
+ if (!cssKey) {
69
+ throw new Error(`Cannot translate "${key}" to a CSS property.`);
70
+ }
71
+ return `${cssKey} ${params[direction].duration}ms ${params[direction].timing} ${params[direction].delay}ms`;
72
+ }, [])
73
+ .join(', ');
74
+ }
75
+ function getSlowestProperty(state, direction, values) {
76
+ const mappedEntries = Object.entries(state)
77
+ .filter(([key]) => values.includes(key))
78
+ .map(([key, params]) => {
79
+ const transitionParams = params[direction];
80
+ return {
81
+ key,
82
+ time: transitionParams.duration + transitionParams.delay
83
+ };
84
+ });
85
+ if (mappedEntries.length === 0)
86
+ return '';
87
+ const { key } = mappedEntries.reduce((slowest, current) => current.time > slowest.time ? current : slowest);
88
+ return CSSPropertyNameMap[key];
89
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InteractionsProvider = exports.InteractionsContext = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
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 }));
44
+ };
45
+ exports.InteractionsProvider = InteractionsProvider;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getHoverStyles = exports.getTransitions = void 0;
3
+ exports.getTransitions = exports.getStateStyles = void 0;
4
4
  const color_1 = require("@cntrl-site/color");
5
5
  const getItemTopStyle_1 = require("../getItemTopStyle");
6
- const hoverTransformationMap = {
6
+ const stateTransformationMap = {
7
7
  'width': (width) => `width: ${width * 100}vw !important;`,
8
8
  'height': (height) => `height: ${height * 100}vw !important;`,
9
9
  'top': (top, anchorSide) => `top: ${(0, getItemTopStyle_1.getItemTopStyle)(top, anchorSide)} !important;`,
@@ -39,39 +39,51 @@ const CSSPropertyNameMap = {
39
39
  'wordSpacing': 'word-spacing',
40
40
  'color': 'color'
41
41
  };
42
- function getTransitions(values, hover) {
43
- if (!hover)
44
- return 'unset';
45
- const transitionValues = values.reduce((acc, valueName) => {
46
- if (valueName in hover && hover[valueName] !== undefined) {
47
- const hoverProperties = hover[valueName];
42
+ function getStateStyles(values, state, anchorSide) {
43
+ if (!state)
44
+ return '';
45
+ const stateValues = values.reduce((acc, valueName) => {
46
+ // @ts-ignore
47
+ if (valueName in state && state[valueName] !== undefined) {
48
+ // @ts-ignore
49
+ const stateProperties = state[valueName];
50
+ const getter = stateTransformationMap[valueName];
48
51
  return [
49
52
  ...acc,
50
- `${CSSPropertyNameMap[valueName]} ${hoverProperties.duration}ms ${hoverProperties.timing} ${hoverProperties.delay}ms`
53
+ getter(stateProperties.value, anchorSide)
51
54
  ];
52
55
  }
53
56
  return acc;
54
57
  }, []);
55
- if (!transitionValues.length)
56
- return 'unset';
57
- return transitionValues.join(', ');
58
- }
59
- exports.getTransitions = getTransitions;
60
- function getHoverStyles(values, hover, anchorSide) {
61
- if (!hover)
58
+ if (!stateValues.length)
62
59
  return '';
63
- const hoverValues = values.reduce((acc, valueName) => {
64
- if (valueName in hover && hover[valueName] !== undefined) {
65
- const hoverProperties = hover[valueName];
60
+ // @ts-ignore
61
+ const transitionStr = getTransitions(values, state);
62
+ stateValues.push(`transition: ${transitionStr};`);
63
+ return stateValues.join('\n');
64
+ }
65
+ exports.getStateStyles = getStateStyles;
66
+ function getTransitions(values, state) {
67
+ if (!state)
68
+ return 'unset';
69
+ const transitionValues = values.reduce((acc, valueName) => {
70
+ // @ts-ignore TODO
71
+ if (valueName in state && state[valueName] !== undefined) {
72
+ // @ts-ignore TODO
73
+ const stateProperties = state[valueName];
66
74
  return [
67
75
  ...acc,
68
- hoverTransformationMap[valueName](hoverProperties.value, anchorSide)
76
+ // @ts-ignore
77
+ `${CSSPropertyNameMap[valueName]} ${stateProperties.duration}ms ${stateProperties.timing} ${stateProperties.delay}ms`
69
78
  ];
70
79
  }
71
80
  return acc;
72
81
  }, []);
73
- if (!hoverValues.length)
74
- return '';
75
- return hoverValues.join('\n');
82
+ if (!transitionValues.length)
83
+ return 'unset';
84
+ return transitionValues.join(', ');
76
85
  }
77
- exports.getHoverStyles = getHoverStyles;
86
+ exports.getTransitions = getTransitions;
87
+ // export function getStateTransitions<T extends ArticleItemType>(
88
+ //
89
+ // )
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getStatesCSS = void 0;
4
+ const StateStyles_1 = require("./StateStyles/StateStyles");
5
+ function getStatesCSS(itemId, classNamePrefix, keys, states, anchorSide) {
6
+ return states
7
+ ? Object.entries(states).map(([stateId, params]) => {
8
+ return `
9
+ .${classNamePrefix}-${itemId}-state-${stateId} {
10
+ ${(0, StateStyles_1.getStateStyles)(keys, params, anchorSide)};
11
+ }
12
+ `;
13
+ }).join('\n')
14
+ : '';
15
+ }
16
+ exports.getStatesCSS = getStatesCSS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk-nextjs",
3
- "version": "1.0.18",
3
+ "version": "1.0.19-alpha.1",
4
4
  "description": "SDK for Next.js",
5
5
  "main": "lib/index.js",
6
6
  "types": "src/index.ts",
@@ -23,7 +23,7 @@
23
23
  "dependencies": {
24
24
  "@cntrl-site/color": "^1.0.0",
25
25
  "@cntrl-site/effects": "^1.2.0",
26
- "@cntrl-site/sdk": "^1.12.3",
26
+ "@cntrl-site/sdk": "^1.12.4-alpha.8",
27
27
  "@types/vimeo__player": "^2.18.0",
28
28
  "@vimeo/player": "^2.20.1",
29
29
  "html-react-parser": "^3.0.1",
@@ -38,12 +38,15 @@
38
38
  "react-dom": "^18.3.1"
39
39
  },
40
40
  "devDependencies": {
41
+ "@testing-library/jest-dom": "^6.4.8",
42
+ "@testing-library/react": "^16.0.0",
41
43
  "@tsconfig/node16": "^1.0.3",
42
44
  "@tsconfig/recommended": "^1.0.1",
43
- "@types/jest": "^29.2.5",
45
+ "@types/jest": "^29.5.12",
44
46
  "@types/node": "^18.11.7",
45
47
  "@types/react": "^18.0.15",
46
- "jest": "^29.3.1",
48
+ "jest": "^29.7.0",
49
+ "jest-environment-jsdom": "^29.7.0",
47
50
  "ts-jest": "^29.0.3",
48
51
  "typescript": "^5.2.2"
49
52
  },
@@ -1,11 +1,12 @@
1
1
  import { FC, useEffect, useId, useRef, useState } from 'react';
2
2
  import JSXStyle from 'styled-jsx/style';
3
- import { Article as TArticle } from '@cntrl-site/sdk';
3
+ import { Article as TArticle, ArticleItemType, ItemState, ItemStateParams } from '@cntrl-site/sdk';
4
4
  import { Section } from './Section';
5
5
  import { Item } from './Item';
6
6
  import { useArticleRectObserver } from '../utils/ArticleRectManager/useArticleRectObserver';
7
7
  import { ArticleRectContext } from '../provider/ArticleRectContext';
8
8
  import { ArticleWrapper } from './ArticleWrapper';
9
+ import { InteractionsProvider } from '../provider/InteractionsContext';
9
10
 
10
11
  interface Props {
11
12
  article: TArticle;
@@ -27,35 +28,37 @@ export const Article: FC<Props> = ({ article, sectionData }) => {
27
28
 
28
29
  return (
29
30
  <ArticleRectContext.Provider value={articleRectObserver}>
30
- <ArticleWrapper>
31
- <div className="article" ref={articleRef}>
32
- {article.sections.map((section, i) => {
33
- const data = section.name ? sectionData[section.name] : {};
34
- return (
35
- <Section
36
- section={section}
37
- key={section.id}
38
- data={data}
39
- >
40
- {article.sections[i].items.map(item => (
41
- <Item
42
- item={item}
43
- key={item.id}
44
- sectionId={section.id}
45
- articleHeight={articleHeight}
46
- />
47
- ))}
48
- </Section>
49
- );
50
- })}
51
- </div>
52
- </ArticleWrapper>
53
- <JSXStyle id={id}>{`
31
+ <InteractionsProvider interactions={article.interactions}>
32
+ <ArticleWrapper>
33
+ <div className="article" ref={articleRef}>
34
+ {article.sections.map((section, i) => {
35
+ const data = section.name ? sectionData[section.name] : {};
36
+ return (
37
+ <Section
38
+ section={section}
39
+ key={section.id}
40
+ data={data}
41
+ >
42
+ {article.sections[i].items.map(item => (
43
+ <Item
44
+ item={item}
45
+ key={item.id}
46
+ sectionId={section.id}
47
+ articleHeight={articleHeight}
48
+ />
49
+ ))}
50
+ </Section>
51
+ );
52
+ })}
53
+ </div>
54
+ </ArticleWrapper>
55
+ <JSXStyle id={id}>{`
54
56
  .article {
55
- position: relative;
56
- overflow: clip;
57
- }
57
+ position: relative;
58
+ overflow: clip;
59
+ }
58
60
  `}</JSXStyle>
61
+ </InteractionsProvider>
59
62
  </ArticleRectContext.Provider>
60
63
  );
61
64
  };
@@ -1,5 +1,4 @@
1
- import React, { CSSProperties, FC, PropsWithChildren, useEffect, useId, useState } from 'react';
2
- import JSXStyle from 'styled-jsx/style';
1
+ import React, { CSSProperties, FC, PropsWithChildren } from 'react';
3
2
  import { useCurrentLayout } from '../common/useCurrentLayout';
4
3
  import { LayoutContext } from '../provider/LayoutContext';
5
4