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

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.
@@ -33,6 +33,7 @@ const KeyframesContext_1 = require("../provider/KeyframesContext");
33
33
  const InteractionsContext_1 = require("../provider/InteractionsContext");
34
34
  const getStatesCSS_1 = require("../utils/getStatesCSS");
35
35
  const useStatesClassNames_1 = require("./useStatesClassNames");
36
+ const useStatesTransitions_1 = require("./useStatesTransitions");
36
37
  const itemsMap = {
37
38
  [sdk_1.ArticleItemType.Rectangle]: RectangleItem_1.RectangleItem,
38
39
  [sdk_1.ArticleItemType.Image]: ImageItem_1.ImageItem,
@@ -56,6 +57,7 @@ const RichTextWrapper = ({ isRichText, children }) => {
56
57
  const noop = () => null;
57
58
  const Item = ({ item, sectionId, articleHeight, isInGroup = false }) => {
58
59
  const itemWrapperRef = (0, react_1.useRef)(null);
60
+ const itemInnerRef = (0, react_1.useRef)(null);
59
61
  const rectObserver = (0, react_1.useContext)(ArticleRectContext_1.ArticleRectContext);
60
62
  const keyframesRepo = (0, react_1.useContext)(KeyframesContext_1.KeyframesContext);
61
63
  const id = (0, react_1.useId)();
@@ -107,11 +109,13 @@ const Item = ({ item, sectionId, articleHeight, isInGroup = false }) => {
107
109
  const isRichText = isItemType(item, sdk_1.ArticleItemType.RichText);
108
110
  const innerStatesClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'item-inner');
109
111
  const wrapperStatesClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'item-wrapper');
112
+ (0, useStatesTransitions_1.useStatesTransitions)(itemWrapperRef.current, item.state, ['top', 'left']);
113
+ (0, useStatesTransitions_1.useStatesTransitions)(itemInnerRef.current, item.state, ['width', 'height', 'scale']);
110
114
  return ((0, jsx_runtime_1.jsxs)("div", { className: `item-wrapper-${item.id} ${wrapperStatesClassNames}`, ref: itemWrapperRef, style: Object.assign(Object.assign(Object.assign(Object.assign({}, (position ? { top: position.top } : {})), (position ? { left: position.left } : {})), (position ? { bottom: position.bottom } : {})), (wrapperHeight !== undefined ? { height: `${wrapperHeight * 100}vw` } : {})), children: [(0, jsx_runtime_1.jsx)("div", { suppressHydrationWarning: true, className: `item-${item.id}`, style: {
111
115
  opacity: (keyframes.length !== 0 && !layout) ? 0 : 1,
112
116
  top: `${stickyTop * 100}vw`,
113
117
  height: isRichText && itemHeight ? `${itemHeight * 100}vw` : 'unset',
114
- }, children: (0, jsx_runtime_1.jsx)(RichTextWrapper, { isRichText: isRichText, children: (0, jsx_runtime_1.jsx)("div", { className: `item-${item.id}-inner ${innerStatesClassNames}`, onClick: () => {
118
+ }, children: (0, jsx_runtime_1.jsx)(RichTextWrapper, { isRichText: isRichText, children: (0, jsx_runtime_1.jsx)("div", { className: `item-${item.id}-inner ${innerStatesClassNames}`, ref: itemInnerRef, onClick: () => {
115
119
  const trigger = getItemTrigger(item.id, 'click');
116
120
  if (!trigger)
117
121
  return;
@@ -157,7 +161,6 @@ const Item = ({ item, sectionId, articleHeight, isInGroup = false }) => {
157
161
  height: ${sizingAxis.y === 'manual' ? `${area.height * 100}vw` : 'unset'};
158
162
  transform-origin: ${ScaleAnchorMap_1.ScaleAnchorMap[scaleAnchor]};
159
163
  transform: scale(${area.scale});
160
- transition: all 0.2s ease;
161
164
  }
162
165
  .item-wrapper-${item.id} {
163
166
  position: ${area.positionType === sdk_1.PositionType.ScreenBased ? 'fixed' : 'absolute'};
@@ -167,7 +170,6 @@ const Item = ({ item, sectionId, articleHeight, isInGroup = false }) => {
167
170
  bottom: ${isScreenBasedBottom ? `${-area.top * 100}vw` : 'unset'};
168
171
  top: ${isScreenBasedBottom ? 'unset' : (0, getItemTopStyle_1.getItemTopStyle)(area.top, area.anchorSide)};
169
172
  left: ${area.left * 100}vw;
170
- transition: all 0.2s ease;
171
173
  }
172
174
  ${statesWrapperCSS}
173
175
  ${statesInnerCSS}
@@ -15,6 +15,7 @@ const LinkWrapper_1 = require("../LinkWrapper");
15
15
  const useCodeEmbedItem_1 = require("./useCodeEmbedItem");
16
16
  const getStatesCSS_1 = require("../../utils/getStatesCSS");
17
17
  const useStatesClassNames_1 = require("../useStatesClassNames");
18
+ const useStatesTransitions_1 = require("../useStatesTransitions");
18
19
  const stylesMap = {
19
20
  [sdk_1.AreaAnchor.TopLeft]: {},
20
21
  [sdk_1.AreaAnchor.TopCenter]: { justifyContent: 'center' },
@@ -38,6 +39,7 @@ const CodeEmbedItem = ({ item, sectionId, onResize }) => {
38
39
  const pos = stylesMap[anchor];
39
40
  const layoutValues = [item.area, item.layoutParams, item.state];
40
41
  const statesClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-wrapper');
42
+ (0, useStatesTransitions_1.useStatesTransitions)(ref, item.state, ['angle', 'blur', 'opacity']);
41
43
  (0, react_1.useEffect)(() => {
42
44
  if (!ref)
43
45
  return;
@@ -13,6 +13,7 @@ const useRegisterResize_1 = require("../../common/useRegisterResize");
13
13
  const useItemAngle_1 = require("../useItemAngle");
14
14
  const useStatesClassNames_1 = require("../useStatesClassNames");
15
15
  const getStatesCSS_1 = require("../../utils/getStatesCSS");
16
+ const useStatesTransitions_1 = require("../useStatesTransitions");
16
17
  const CustomItem = ({ item, onResize, sectionId }) => {
17
18
  const sdk = (0, useCntrlContext_1.useCntrlContext)();
18
19
  const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
@@ -22,6 +23,7 @@ const CustomItem = ({ item, onResize, sectionId }) => {
22
23
  const [ref, setRef] = (0, react_1.useState)(null);
23
24
  (0, useRegisterResize_1.useRegisterResize)(ref, onResize);
24
25
  const statesClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'custom-component');
26
+ (0, useStatesTransitions_1.useStatesTransitions)(ref, item.state, ['angle']);
25
27
  if (!component)
26
28
  return null;
27
29
  return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `custom-component-${item.id} ${statesClassNames}`, ref: setRef, style: Object.assign({}, (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), children: component({}) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: item.id, children: `${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, stateParams]) => {
@@ -16,6 +16,7 @@ const useItemAngle_1 = require("../useItemAngle");
16
16
  const useGroupItem_1 = require("./useGroupItem");
17
17
  const useStatesClassNames_1 = require("../useStatesClassNames");
18
18
  const getStatesCSS_1 = require("../../utils/getStatesCSS");
19
+ const useStatesTransitions_1 = require("../useStatesTransitions");
19
20
  const GroupItem = ({ item, sectionId, onResize, articleHeight }) => {
20
21
  var _a, _b;
21
22
  const id = (0, react_1.useId)();
@@ -27,6 +28,7 @@ const GroupItem = ({ item, sectionId, onResize, articleHeight }) => {
27
28
  const statesClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'group');
28
29
  const [ref, setRef] = (0, react_1.useState)(null);
29
30
  (0, useRegisterResize_1.useRegisterResize)(ref, onResize);
31
+ (0, useStatesTransitions_1.useStatesTransitions)(ref, item.state, ['opacity', 'angle']);
30
32
  return ((0, jsx_runtime_1.jsx)(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)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `group-${item.id} ${statesClassNames}`, ref: setRef, style: Object.assign(Object.assign({}, (opacity !== undefined ? { opacity } : {})), (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), children: items && items.map(item => ((0, jsx_runtime_1.jsx)(Item_1.Item, { item: item, sectionId: sectionId, articleHeight: articleHeight, isInGroup: true }, item.id))) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
31
33
  .group-${item.id} {
32
34
  position: absolute;
@@ -19,6 +19,7 @@ const useElementRect_1 = require("../../utils/useElementRect");
19
19
  const useLayoutContext_1 = require("../useLayoutContext");
20
20
  const useStatesClassNames_1 = require("../useStatesClassNames");
21
21
  const getStatesCSS_1 = require("../../utils/getStatesCSS");
22
+ const useStatesTransitions_1 = require("../useStatesTransitions");
22
23
  const baseVariables = `precision mediump float;
23
24
  uniform sampler2D u_image;
24
25
  uniform sampler2D u_pattern;
@@ -35,8 +36,9 @@ const ImageItem = ({ item, sectionId, onResize }) => {
35
36
  const { radius, strokeWidth, opacity, strokeColor, blur } = (0, useFileItem_1.useFileItem)(item, sectionId);
36
37
  const angle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
37
38
  const borderColor = (0, react_1.useMemo)(() => strokeColor ? color_1.CntrlColor.parse(strokeColor) : undefined, [strokeColor]);
38
- const [ref, setRef] = (0, react_1.useState)(null);
39
- (0, useRegisterResize_1.useRegisterResize)(ref, onResize);
39
+ const [wrapperRef, setWrapperRef] = (0, react_1.useState)(null);
40
+ const [imgRef, setImgRef] = (0, react_1.useState)(null);
41
+ (0, useRegisterResize_1.useRegisterResize)(wrapperRef, onResize);
40
42
  const { url, hasGLEffect, fragmentShader, FXControls, FXCursor } = item.commonParams;
41
43
  const fxCanvas = (0, react_1.useRef)(null);
42
44
  const isInitialRef = (0, react_1.useRef)(true);
@@ -55,6 +57,9 @@ const ImageItem = ({ item, sectionId, onResize }) => {
55
57
  const height = area && exemplary ? area.height * exemplary : 0;
56
58
  const statesWrapperClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'image-wrapper');
57
59
  const statesImgClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'image');
60
+ (0, useStatesTransitions_1.useStatesTransitions)(wrapperRef, item.state, ['angle', 'opacity', 'blur']);
61
+ (0, useStatesTransitions_1.useStatesTransitions)(imgRef, item.state, ['strokeWidth', 'radius', 'strokeColor']);
62
+ (0, useStatesTransitions_1.useStatesTransitions)(fxCanvas.current, item.state, ['strokeWidth', 'radius', 'strokeColor']);
58
63
  (0, react_1.useEffect)(() => {
59
64
  isInitialRef.current = false;
60
65
  }, []);
@@ -64,11 +69,11 @@ const ImageItem = ({ item, sectionId, onResize }) => {
64
69
  cursor: FXCursor,
65
70
  controls: controlValues
66
71
  }, width, height);
67
- const rect = (0, useElementRect_1.useElementRect)(ref);
72
+ const rect = (0, useElementRect_1.useElementRect)(wrapperRef);
68
73
  const rectWidth = Math.floor((_b = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _b !== void 0 ? _b : 0);
69
74
  const rectHeight = Math.floor((_c = rect === null || rect === void 0 ? void 0 : rect.height) !== null && _c !== void 0 ? _c : 0);
70
75
  const inlineStyles = Object.assign(Object.assign(Object.assign({}, (borderColor ? { borderColor: `${borderColor.fmt('rgba')}` } : {})), (radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})), (strokeWidth !== undefined ? { borderWidth: `${strokeWidth * 100}vw` } : {}));
71
- return ((0, jsx_runtime_1.jsx)(LinkWrapper_1.LinkWrapper, { url: (_d = item.link) === null || _d === void 0 ? void 0 : _d.url, target: (_e = item.link) === null || _e === void 0 ? void 0 : _e.target, children: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `image-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: hasGLEffect && isFXAllowed ? ((0, jsx_runtime_1.jsx)("canvas", { style: inlineStyles, ref: fxCanvas, className: `img-canvas image-${item.id} ${statesImgClassNames}`, width: rectWidth, height: rectHeight })) : ((0, jsx_runtime_1.jsx)("img", { alt: "", className: `image image-${item.id} ${statesImgClassNames}`, style: inlineStyles, src: item.commonParams.url })) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
76
+ return ((0, jsx_runtime_1.jsx)(LinkWrapper_1.LinkWrapper, { url: (_d = item.link) === null || _d === void 0 ? void 0 : _d.url, target: (_e = item.link) === null || _e === void 0 ? void 0 : _e.target, children: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `image-wrapper-${item.id} ${statesWrapperClassNames}`, ref: setWrapperRef, style: Object.assign(Object.assign(Object.assign({}, (opacity !== undefined ? { opacity } : {})), (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), children: hasGLEffect && isFXAllowed ? ((0, jsx_runtime_1.jsx)("canvas", { style: inlineStyles, ref: fxCanvas, className: `img-canvas image-${item.id} ${statesImgClassNames}`, width: rectWidth, height: rectHeight })) : ((0, jsx_runtime_1.jsx)("img", { alt: "", className: `image image-${item.id} ${statesImgClassNames}`, style: inlineStyles, ref: setImgRef, src: item.commonParams.url })) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
72
77
  .image-wrapper-${item.id} {
73
78
  position: absolute;
74
79
  width: 100%;
@@ -16,6 +16,7 @@ const useCntrlContext_1 = require("../../provider/useCntrlContext");
16
16
  const useRegisterResize_1 = require("../../common/useRegisterResize");
17
17
  const getStatesCSS_1 = require("../../utils/getStatesCSS");
18
18
  const useStatesClassNames_1 = require("../useStatesClassNames");
19
+ const useStatesTransitions_1 = require("../useStatesTransitions");
19
20
  const RectangleItem = ({ item, sectionId, onResize }) => {
20
21
  var _a, _b;
21
22
  const id = (0, react_1.useId)();
@@ -29,6 +30,7 @@ const RectangleItem = ({ item, sectionId, onResize }) => {
29
30
  const statesClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'rectangle');
30
31
  (0, useRegisterResize_1.useRegisterResize)(ref, onResize);
31
32
  const backdropFilterValue = backdropBlur ? `blur(${backdropBlur * 100}vw)` : undefined;
33
+ (0, useStatesTransitions_1.useStatesTransitions)(ref, item.state, ['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur']);
32
34
  return ((0, jsx_runtime_1.jsx)(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)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `rectangle-${item.id} ${statesClassNames}`, ref: setRef, style: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (backgroundColor ? { backgroundColor: `${backgroundColor.fmt('rgba')}` } : {})), (borderColor ? { borderColor: `${borderColor.fmt('rgba')}` } : {})), (radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})), (strokeWidth !== undefined ? { borderWidth: `${strokeWidth * 100}vw` } : {})), (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), (backdropFilterValue !== undefined
33
35
  ? { backdropFilter: backdropFilterValue, WebkitBackdropFilter: backdropFilterValue }
34
36
  : {})) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
@@ -18,6 +18,7 @@ const useExemplary_1 = require("../../common/useExemplary");
18
18
  const useItemAngle_1 = require("../useItemAngle");
19
19
  const useStatesClassNames_1 = require("../useStatesClassNames");
20
20
  const getStatesCSS_1 = require("../../utils/getStatesCSS");
21
+ const useStatesTransitions_1 = require("../useStatesTransitions");
21
22
  const RichTextItem = ({ item, sectionId, onResize }) => {
22
23
  const [content, styles] = (0, useRichTextItem_1.useRichTextItem)(item);
23
24
  const id = (0, react_1.useId)();
@@ -30,6 +31,7 @@ const RichTextItem = ({ item, sectionId, onResize }) => {
30
31
  const exemplary = (0, useExemplary_1.useExemplary)();
31
32
  const stateClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'rich-text-wrapper');
32
33
  (0, useRegisterResize_1.useRegisterResize)(ref, onResize);
34
+ (0, useStatesTransitions_1.useStatesTransitions)(ref, item.state, ['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color']);
33
35
  return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { ref: setRef, className: `rich-text-wrapper-${item.id} ${stateClassNames}`, style: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), (textColor ? { color: `${textColor.fmt('rgba')}` } : {})), (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (letterSpacing !== undefined ? { letterSpacing: `${letterSpacing * exemplary}px` } : {})), (wordSpacing !== undefined ? { wordSpacing: `${wordSpacing * exemplary}px` } : {})), (fontSize !== undefined ? { fontSize: `${Math.round(fontSize * exemplary)}px` } : {})), (lineHeight !== undefined ? { lineHeight: `${lineHeight * exemplary}px` } : {})), children: content }), (0, jsx_runtime_1.jsxs)(style_1.default, { id: id, children: [styles, `${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams, states]) => {
34
36
  const color = color_1.CntrlColor.parse(layoutParams.color);
35
37
  const statesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'rich-text-wrapper', ['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color'], states);
@@ -18,6 +18,7 @@ const useLayoutContext_1 = require("../useLayoutContext");
18
18
  const ScrollPlaybackVideo_1 = require("../ScrollPlaybackVideo");
19
19
  const useStatesClassNames_1 = require("../useStatesClassNames");
20
20
  const getStatesCSS_1 = require("../../utils/getStatesCSS");
21
+ const useStatesTransitions_1 = require("../useStatesTransitions");
21
22
  const VideoItem = ({ item, sectionId, onResize }) => {
22
23
  var _a, _b, _c;
23
24
  const id = (0, react_1.useId)();
@@ -33,6 +34,8 @@ const VideoItem = ({ item, sectionId, onResize }) => {
33
34
  const statesWrapperClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'video-wrapper');
34
35
  const statesVideoClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'video');
35
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']);
36
39
  (0, useRegisterResize_1.useRegisterResize)(ref, onResize);
37
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()}` } : {}));
38
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: `
@@ -16,6 +16,7 @@ const useCntrlContext_1 = require("../../provider/useCntrlContext");
16
16
  const useRegisterResize_1 = require("../../common/useRegisterResize");
17
17
  const useStatesClassNames_1 = require("../useStatesClassNames");
18
18
  const getStatesCSS_1 = require("../../utils/getStatesCSS");
19
+ const useStatesTransitions_1 = require("../useStatesTransitions");
19
20
  const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
20
21
  var _a, _b, _c;
21
22
  const id = (0, react_1.useId)();
@@ -32,6 +33,8 @@ const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
32
33
  const wrapperClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-video-wrapper');
33
34
  const videoClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-video');
34
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']);
35
38
  const getValidVimeoUrl = (url) => {
36
39
  const validURL = new URL(url);
37
40
  validURL.searchParams.append('controls', String(controls));
@@ -17,6 +17,7 @@ const useYouTubeIframeApi_1 = require("../../utils/Youtube/useYouTubeIframeApi")
17
17
  const useRegisterResize_1 = require("../../common/useRegisterResize");
18
18
  const useStatesClassNames_1 = require("../useStatesClassNames");
19
19
  const getStatesCSS_1 = require("../../utils/getStatesCSS");
20
+ const useStatesTransitions_1 = require("../useStatesTransitions");
20
21
  const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
21
22
  var _a, _b, _c;
22
23
  const id = (0, react_1.useId)();
@@ -26,6 +27,7 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
26
27
  const angle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
27
28
  const YT = (0, useYouTubeIframeApi_1.useYouTubeIframeApi)();
28
29
  const [div, setDiv] = (0, react_1.useState)(null);
30
+ const [wrapperRef, setWrapperRef] = (0, react_1.useState)(null);
29
31
  const [player, setPlayer] = (0, react_1.useState)(undefined);
30
32
  const [isCoverVisible, setIsCoverVisible] = (0, react_1.useState)(false);
31
33
  const [imgRef, setImgRef] = (0, react_1.useState)(null);
@@ -33,6 +35,8 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
33
35
  const wrapperClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-youtube-video-wrapper');
34
36
  const embedClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed');
35
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']);
36
40
  (0, react_1.useEffect)(() => {
37
41
  const newUrl = new URL(url);
38
42
  const videoId = (0, getValidYoutubeUrl_1.getYoutubeId)(newUrl);
@@ -83,7 +87,7 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
83
87
  setIsCoverVisible(true);
84
88
  }
85
89
  }, []);
86
- 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} ${wrapperClassNames}`, 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: () => {
87
91
  if (!player || play !== 'on-hover')
88
92
  return;
89
93
  player.playVideo();
@@ -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] : {}, [state, layoutId]);
12
+ const itemStatesIds = statesForLayout ? 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
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getStateStyles = 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
6
  const stateTransformationMap = {
@@ -39,41 +39,51 @@ const CSSPropertyNameMap = {
39
39
  'wordSpacing': 'word-spacing',
40
40
  'color': 'color'
41
41
  };
42
- function getTransitions(values, state) {
42
+ function getStateStyles(values, state, anchorSide) {
43
43
  if (!state)
44
- return 'unset';
45
- const transitionValues = values.reduce((acc, valueName) => {
44
+ return '';
45
+ const stateValues = values.reduce((acc, valueName) => {
46
+ // @ts-ignore
46
47
  if (valueName in state && state[valueName] !== undefined) {
47
- // @ts-ignore TODO
48
+ // @ts-ignore
48
49
  const stateProperties = state[valueName];
50
+ const getter = stateTransformationMap[valueName];
49
51
  return [
50
52
  ...acc,
51
- `${CSSPropertyNameMap[valueName]} ${stateProperties.duration}ms ${stateProperties.timing} ${stateProperties.delay}ms`
53
+ getter(stateProperties.value, anchorSide)
52
54
  ];
53
55
  }
54
56
  return acc;
55
57
  }, []);
56
- if (!transitionValues.length)
57
- return 'unset';
58
- return transitionValues.join(', ');
58
+ if (!stateValues.length)
59
+ return '';
60
+ // @ts-ignore
61
+ const transitionStr = getTransitions(values, state);
62
+ stateValues.push(`transition: ${transitionStr};`);
63
+ return stateValues.join('\n');
59
64
  }
60
- exports.getTransitions = getTransitions;
61
- function getStateStyles(values, state, anchorSide) {
65
+ exports.getStateStyles = getStateStyles;
66
+ function getTransitions(values, state) {
62
67
  if (!state)
63
- return '';
64
- const stateValues = values.reduce((acc, valueName) => {
68
+ return 'unset';
69
+ const transitionValues = values.reduce((acc, valueName) => {
70
+ // @ts-ignore TODO
65
71
  if (valueName in state && state[valueName] !== undefined) {
66
72
  // @ts-ignore TODO
67
73
  const stateProperties = state[valueName];
68
74
  return [
69
75
  ...acc,
70
- stateTransformationMap[valueName](stateProperties.value, anchorSide)
76
+ // @ts-ignore
77
+ `${CSSPropertyNameMap[valueName]} ${stateProperties.duration}ms ${stateProperties.timing} ${stateProperties.delay}ms`
71
78
  ];
72
79
  }
73
80
  return acc;
74
81
  }, []);
75
- if (!stateValues.length)
76
- return '';
77
- return stateValues.join('\n');
82
+ if (!transitionValues.length)
83
+ return 'unset';
84
+ return transitionValues.join(', ');
78
85
  }
79
- exports.getStateStyles = getStateStyles;
86
+ exports.getTransitions = getTransitions;
87
+ // export function getStateTransitions<T extends ArticleItemType>(
88
+ //
89
+ // )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk-nextjs",
3
- "version": "1.0.19-alpha.0",
3
+ "version": "1.0.19-alpha.2",
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.4-alpha.7",
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",
@@ -34,6 +34,7 @@ import { KeyframesContext } from '../provider/KeyframesContext';
34
34
  import { InteractionsContext } from '../provider/InteractionsContext';
35
35
  import { getStatesCSS } from '../utils/getStatesCSS';
36
36
  import { useStatesClassNames } from './useStatesClassNames';
37
+ import { useStatesTransitions } from './useStatesTransitions';
37
38
 
38
39
  export interface ItemProps<I extends ItemAny> {
39
40
  item: I;
@@ -81,6 +82,7 @@ const noop = () => null;
81
82
 
82
83
  export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isInGroup = false }) => {
83
84
  const itemWrapperRef = useRef<HTMLDivElement | null>(null);
85
+ const itemInnerRef = useRef<HTMLDivElement | null>(null);
84
86
  const rectObserver = useContext(ArticleRectContext);
85
87
  const keyframesRepo = useContext(KeyframesContext);
86
88
  const id = useId();
@@ -134,6 +136,8 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
134
136
  const isRichText = isItemType(item, ArticleItemType.RichText);
135
137
  const innerStatesClassNames = useStatesClassNames(item.id, item.state, 'item-inner');
136
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']);
137
141
  return (
138
142
  <div
139
143
  className={`item-wrapper-${item.id} ${wrapperStatesClassNames}`}
@@ -157,6 +161,7 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
157
161
  <RichTextWrapper isRichText={isRichText}>
158
162
  <div
159
163
  className={`item-${item.id}-inner ${innerStatesClassNames}`}
164
+ ref={itemInnerRef}
160
165
  onClick={() => {
161
166
  const trigger = getItemTrigger(item.id, 'click');
162
167
  if (!trigger) return;
@@ -211,7 +216,6 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
211
216
  height: ${sizingAxis.y === 'manual' ? `${area.height * 100}vw` : 'unset'};
212
217
  transform-origin: ${ScaleAnchorMap[scaleAnchor]};
213
218
  transform: scale(${area.scale});
214
- transition: all 0.2s ease;
215
219
  }
216
220
  .item-wrapper-${item.id} {
217
221
  position: ${area.positionType === PositionType.ScreenBased ? 'fixed': 'absolute'};
@@ -221,7 +225,6 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
221
225
  bottom: ${isScreenBasedBottom ? `${-area.top * 100}vw` : 'unset'};
222
226
  top: ${isScreenBasedBottom ? 'unset' : getItemTopStyle(area.top, area.anchorSide)};
223
227
  left: ${area.left * 100}vw;
224
- transition: all 0.2s ease;
225
228
  }
226
229
  ${statesWrapperCSS}
227
230
  ${statesInnerCSS}
@@ -9,6 +9,7 @@ import { LinkWrapper } from '../LinkWrapper';
9
9
  import { useCodeEmbedItem } from './useCodeEmbedItem';
10
10
  import { getStatesCSS } from '../../utils/getStatesCSS';
11
11
  import { useStatesClassNames } from '../useStatesClassNames';
12
+ import { useStatesTransitions } from '../useStatesTransitions';
12
13
 
13
14
  const stylesMap = {
14
15
  [AreaAnchor.TopLeft]: {},
@@ -33,6 +34,7 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
33
34
  const pos = stylesMap[anchor];
34
35
  const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state];
35
36
  const statesClassNames = useStatesClassNames(item.id, item.state, 'embed-wrapper');
37
+ useStatesTransitions(ref!, item.state, ['angle', 'blur', 'opacity']);
36
38
 
37
39
  useEffect(() => {
38
40
  if (!ref) return;
@@ -7,6 +7,7 @@ import { useRegisterResize } from "../../common/useRegisterResize";
7
7
  import { useItemAngle } from '../useItemAngle';
8
8
  import { useStatesClassNames } from '../useStatesClassNames';
9
9
  import { getStatesCSS } from '../../utils/getStatesCSS';
10
+ import { useStatesTransitions } from '../useStatesTransitions';
10
11
 
11
12
  export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item, onResize, sectionId }) => {
12
13
  const sdk = useCntrlContext();
@@ -17,6 +18,7 @@ export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item, onResize, section
17
18
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
18
19
  useRegisterResize(ref, onResize);
19
20
  const statesClassNames = useStatesClassNames(item.id, item.state, 'custom-component');
21
+ useStatesTransitions(ref, item.state, ['angle'])
20
22
  if (!component) return null;
21
23
  return (
22
24
  <>
@@ -9,6 +9,7 @@ import { useItemAngle } from '../useItemAngle';
9
9
  import { useGroupItem } from './useGroupItem';
10
10
  import { useStatesClassNames } from '../useStatesClassNames';
11
11
  import { getStatesCSS } from '../../utils/getStatesCSS';
12
+ import { useStatesTransitions } from '../useStatesTransitions';
12
13
 
13
14
  export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize, articleHeight }) => {
14
15
  const id = useId();
@@ -20,6 +21,7 @@ export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize
20
21
  const statesClassNames = useStatesClassNames(item.id, item.state, 'group');
21
22
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
22
23
  useRegisterResize(ref, onResize);
24
+ useStatesTransitions(ref!, item.state, ['opacity', 'angle'])
23
25
 
24
26
  return (
25
27
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
@@ -13,6 +13,7 @@ import { useElementRect } from '../../utils/useElementRect';
13
13
  import { useLayoutContext } from '../useLayoutContext';
14
14
  import { useStatesClassNames } from '../useStatesClassNames';
15
15
  import { getStatesCSS } from '../../utils/getStatesCSS';
16
+ import { useStatesTransitions } from '../useStatesTransitions';
16
17
 
17
18
  const baseVariables = `precision mediump float;
18
19
  uniform sampler2D u_image;
@@ -30,8 +31,9 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
30
31
  const { radius, strokeWidth, opacity, strokeColor, blur } = useFileItem(item, sectionId);
31
32
  const angle = useItemAngle(item, sectionId);
32
33
  const borderColor = useMemo(() => strokeColor ? CntrlColor.parse(strokeColor) : undefined, [strokeColor]);
33
- const [ref, setRef] = useState<HTMLDivElement | null>(null);
34
- useRegisterResize(ref, onResize);
34
+ const [wrapperRef, setWrapperRef] = useState<HTMLDivElement | null>(null);
35
+ const [imgRef, setImgRef] = useState<HTMLImageElement | HTMLCanvasElement | null>(null);
36
+ useRegisterResize(wrapperRef, onResize);
35
37
  const { url, hasGLEffect, fragmentShader, FXControls, FXCursor } = item.commonParams;
36
38
  const fxCanvas = useRef<HTMLCanvasElement | null>(null);
37
39
  const isInitialRef = useRef(true);
@@ -50,6 +52,9 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
50
52
  const height = area && exemplary ? area.height * exemplary : 0;
51
53
  const statesWrapperClassNames = useStatesClassNames(item.id, item.state, 'image-wrapper');
52
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']);
53
58
  useEffect(() => {
54
59
  isInitialRef.current = false;
55
60
  }, []);
@@ -65,7 +70,7 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
65
70
  width,
66
71
  height
67
72
  );
68
- const rect = useElementRect(ref);
73
+ const rect = useElementRect(wrapperRef);
69
74
  const rectWidth = Math.floor(rect?.width ?? 0);
70
75
  const rectHeight = Math.floor(rect?.height ?? 0);
71
76
  const inlineStyles = {
@@ -78,7 +83,7 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
78
83
  <>
79
84
  <div
80
85
  className={`image-wrapper-${item.id} ${statesWrapperClassNames}`}
81
- ref={setRef}
86
+ ref={setWrapperRef}
82
87
  style={{
83
88
  ...(opacity !== undefined ? { opacity } : {}),
84
89
  ...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
@@ -98,6 +103,7 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
98
103
  alt=""
99
104
  className={`image image-${item.id} ${statesImgClassNames}`}
100
105
  style={inlineStyles}
106
+ ref={setImgRef}
101
107
  src={item.commonParams.url}
102
108
  />
103
109
  )}
@@ -10,6 +10,7 @@ import { useCntrlContext } from '../../provider/useCntrlContext';
10
10
  import { useRegisterResize } from "../../common/useRegisterResize";
11
11
  import { getStatesCSS } from '../../utils/getStatesCSS';
12
12
  import { useStatesClassNames } from '../useStatesClassNames';
13
+ import { useStatesTransitions } from '../useStatesTransitions';
13
14
 
14
15
  export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId, onResize }) => {
15
16
  const id = useId();
@@ -23,6 +24,7 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
23
24
  const statesClassNames = useStatesClassNames(item.id, item.state, 'rectangle');
24
25
  useRegisterResize(ref, onResize);
25
26
  const backdropFilterValue = backdropBlur ? `blur(${backdropBlur * 100}vw)`: undefined;
27
+ useStatesTransitions(ref, item.state, ['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur']);
26
28
 
27
29
  return (
28
30
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
@@ -12,6 +12,7 @@ import { useExemplary } from '../../common/useExemplary';
12
12
  import { useItemAngle } from '../useItemAngle';
13
13
  import { useStatesClassNames } from '../useStatesClassNames';
14
14
  import { getStatesCSS } from '../../utils/getStatesCSS';
15
+ import { useStatesTransitions } from '../useStatesTransitions';
15
16
 
16
17
  export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, onResize }) => {
17
18
  const [content, styles] = useRichTextItem(item);
@@ -25,6 +26,7 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
25
26
  const exemplary = useExemplary();
26
27
  const stateClassNames = useStatesClassNames(item.id, item.state, 'rich-text-wrapper');
27
28
  useRegisterResize(ref, onResize);
29
+ useStatesTransitions(ref, item.state, ['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color']);
28
30
 
29
31
  return (
30
32
  <>
@@ -12,6 +12,7 @@ import { useLayoutContext } from '../useLayoutContext';
12
12
  import { ScrollPlaybackVideo } from '../ScrollPlaybackVideo';
13
13
  import { useStatesClassNames } from '../useStatesClassNames';
14
14
  import { getStatesCSS } from '../../utils/getStatesCSS';
15
+ import { useStatesTransitions } from '../useStatesTransitions';
15
16
 
16
17
  export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize }) => {
17
18
  const id = useId();
@@ -27,6 +28,8 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
27
28
  const statesWrapperClassNames = useStatesClassNames(item.id, item.state, 'video-wrapper');
28
29
  const statesVideoClassNames = useStatesClassNames(item.id, item.state, 'video');
29
30
  const hasScrollPlayback = scrollPlayback !== null;
31
+ useStatesTransitions(ref, item.state, ['angle', 'opacity', 'blur']);
32
+ useStatesTransitions(videoRef.current, item.state, ['strokeWidth', 'radius', 'strokeColor']);
30
33
  useRegisterResize(ref, onResize);
31
34
  const inlineStyles = {
32
35
  ...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {}),
@@ -10,6 +10,7 @@ import { useCntrlContext } from '../../provider/useCntrlContext';
10
10
  import { useRegisterResize } from "../../common/useRegisterResize";
11
11
  import { useStatesClassNames } from '../useStatesClassNames';
12
12
  import { getStatesCSS } from '../../utils/getStatesCSS';
13
+ import { useStatesTransitions } from '../useStatesTransitions';
13
14
 
14
15
  export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId, onResize }) => {
15
16
  const id = useId();
@@ -26,6 +27,8 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId
26
27
  const wrapperClassNames = useStatesClassNames(item.id, item.state, 'embed-video-wrapper');
27
28
  const videoClassNames = useStatesClassNames(item.id, item.state, 'embed-video');
28
29
  useRegisterResize(ref, onResize);
30
+ useStatesTransitions(ref, item.state, ['angle', 'blur', 'opacity']);
31
+ useStatesTransitions(iframeRef, item.state, ['radius']);
29
32
  const getValidVimeoUrl = (url: string): string => {
30
33
  const validURL = new URL(url);
31
34
  validURL.searchParams.append('controls', String(controls));
@@ -12,6 +12,7 @@ import { YTPlayer } from '../../utils/Youtube/YoutubeIframeApi';
12
12
  import { useRegisterResize } from "../../common/useRegisterResize";
13
13
  import { useStatesClassNames } from '../useStatesClassNames';
14
14
  import { getStatesCSS } from '../../utils/getStatesCSS';
15
+ import { useStatesTransitions } from '../useStatesTransitions';
15
16
 
16
17
  export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, sectionId, onResize }) => {
17
18
  const id = useId();
@@ -21,6 +22,7 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
21
22
  const angle = useItemAngle(item, sectionId);
22
23
  const YT = useYouTubeIframeApi();
23
24
  const [div, setDiv] = useState<HTMLDivElement | null>(null);
25
+ const [wrapperRef, setWrapperRef] = useState<HTMLDivElement | null>(null);
24
26
  const [player, setPlayer] = useState<YTPlayer | undefined>(undefined);
25
27
  const [isCoverVisible, setIsCoverVisible] = useState(false);
26
28
  const [imgRef, setImgRef] = useState<HTMLImageElement | null>(null);
@@ -28,7 +30,8 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
28
30
  const wrapperClassNames = useStatesClassNames(item.id, item.state, 'embed-youtube-video-wrapper');
29
31
  const embedClassNames = useStatesClassNames(item.id, item.state, 'embed');
30
32
  useRegisterResize(div, onResize);
31
-
33
+ useStatesTransitions(wrapperRef, item.state, ['angle', 'blur', 'opacity']);
34
+ useStatesTransitions(div, item.state, ['radius']);
32
35
  useEffect(() => {
33
36
  const newUrl = new URL(url);
34
37
  const videoId = getYoutubeId(newUrl);
@@ -81,6 +84,7 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
81
84
  return (
82
85
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
83
86
  <div
87
+ ref={setWrapperRef}
84
88
  className={`embed-youtube-video-wrapper-${item.id} ${wrapperClassNames}`}
85
89
  onMouseEnter={() => {
86
90
  if (!player || play !== 'on-hover') return;
@@ -0,0 +1,95 @@
1
+ import { ArticleItemType, ItemState, ItemStateParams, ItemStatesMap } from '@cntrl-site/sdk';
2
+ import { useContext, useEffect, useMemo } from 'react';
3
+ import { InteractionsContext } from '../provider/InteractionsContext';
4
+ import { useCurrentLayout } from '../common/useCurrentLayout';
5
+
6
+ type AllKeys<T> = T extends any ? keyof T : never;
7
+ type StatePropertyKey = AllKeys<ItemStatesMap[keyof ItemStatesMap]>;
8
+
9
+ export function useStatesTransitions(
10
+ el: HTMLElement | null,
11
+ state: ItemState<ArticleItemType>,
12
+ values: StatePropertyKey[]
13
+ ) {
14
+ const { interactionsStatesMap } = useContext(InteractionsContext);
15
+ const { layoutId } = useCurrentLayout();
16
+ const activeStates = Object.values(interactionsStatesMap);
17
+ const statesForLayout = useMemo(() => layoutId ? state[layoutId] : {}, [state, layoutId]);
18
+ const itemStatesIds = statesForLayout ? Object.keys(statesForLayout) : [];
19
+ const itemActiveStateId = activeStates.find((stateId) => itemStatesIds.includes(stateId));
20
+ useEffect(() => {
21
+ if (!itemActiveStateId || !el) return;
22
+ const state = statesForLayout[itemActiveStateId];
23
+ const transitionStr = getTransition(state, 'in', values);
24
+ const slowestProp = getSlowestProperty(state, 'in', values);
25
+ if (!transitionStr) return;
26
+ el.style.transition = transitionStr;
27
+ el.ontransitionend = (e) => {
28
+ e.stopPropagation();
29
+ if (e.target !== el || e.propertyName !== slowestProp) return;
30
+ el.style.transition = 'none';
31
+ };
32
+ return () => {
33
+ const transitionStr = getTransition(state, 'out', values);
34
+ const slowestProp = getSlowestProperty(state, 'out', values);
35
+ if (!transitionStr) return;
36
+ el.style.transition = transitionStr;
37
+ el.ontransitionend = (e) => {
38
+ e.stopPropagation();
39
+ if (e.target !== el || e.propertyName !== slowestProp) return;
40
+ el.style.transition = 'none';
41
+ };
42
+ };
43
+ }, [itemActiveStateId, statesForLayout, el]);
44
+ }
45
+
46
+ const CSSPropertyNameMap: Record<keyof ItemState<ArticleItemType>, string> = {
47
+ 'width': 'width',
48
+ 'height': 'height',
49
+ 'top': 'top',
50
+ 'left': 'left',
51
+ 'scale': 'transform',
52
+ 'angle': 'transform',
53
+ 'opacity': 'opacity',
54
+ 'radius': 'border-radius',
55
+ 'strokeWidth': 'border-width',
56
+ 'strokeColor': 'border-color',
57
+ 'fillColor': 'background-color',
58
+ 'blur': 'filter',
59
+ 'backdropBlur': 'backdrop-filter',
60
+ 'letterSpacing': 'letter-spacing',
61
+ 'wordSpacing': 'word-spacing',
62
+ 'color': 'color'
63
+ };
64
+
65
+ function getTransition(
66
+ state: ItemStateParams,
67
+ direction: 'in' | 'out',
68
+ values: string[]
69
+ ) {
70
+ return Object.entries(state)
71
+ .filter(([key]) => values.includes(key))
72
+ .map(([key, params]) => {
73
+ const cssKey = CSSPropertyNameMap[key];
74
+ if (!cssKey) {
75
+ throw new Error(`Cannot translate "${key}" to a CSS property.`);
76
+ }
77
+ return `${cssKey} ${params[direction].duration}ms ${params[direction].timing} ${params[direction].delay}ms`;
78
+ }, [])
79
+ .join(', ');
80
+ }
81
+
82
+ function getSlowestProperty(state: ItemStateParams, direction: 'in' | 'out', values: string[]): string {
83
+ const mappedEntries = Object.entries(state)
84
+ .filter(([key]) => values.includes(key))
85
+ .map(([key, params]) => {
86
+ const transitionParams = params[direction];
87
+ return {
88
+ key,
89
+ time: transitionParams.duration + transitionParams.delay
90
+ }
91
+ });
92
+ if (mappedEntries.length === 0) return '';
93
+ const { key } = mappedEntries.reduce((slowest, current) => current.time > slowest.time ? current : slowest);
94
+ return CSSPropertyNameMap[key];
95
+ }
@@ -1,11 +1,12 @@
1
- import { StateParams, ArticleItemType, AnchorSide, ItemState } from '@cntrl-site/sdk';
1
+ import { StateParams, ArticleItemType, AnchorSide, ItemState, ItemStatesMap } from '@cntrl-site/sdk';
2
2
  import { CntrlColor } from '@cntrl-site/color';
3
3
  import { getItemTopStyle } from '../getItemTopStyle';
4
4
 
5
- type UnionToIntersection<U> = (U extends any ? (arg: U) => void : never) extends (arg: infer I) => void ? I : never;
6
5
  type StateParamsGetter = (value: any, anchorSide?: AnchorSide) => string;
6
+ type AllKeys<T> = T extends any ? keyof T : never;
7
+ type StatePropertyKey = AllKeys<ItemStatesMap[keyof ItemStatesMap]>;
7
8
 
8
- const stateTransformationMap: Record<keyof ItemState<ArticleItemType>, StateParamsGetter> = {
9
+ const stateTransformationMap: Record<StatePropertyKey, StateParamsGetter> = {
9
10
  'width': (width: number) => `width: ${width * 100}vw !important;`,
10
11
  'height': (height: number) => `height: ${height * 100}vw !important;`,
11
12
  'top': (top: number, anchorSide?: AnchorSide) => `top: ${getItemTopStyle(top, anchorSide)} !important;`,
@@ -43,44 +44,56 @@ const CSSPropertyNameMap: Record<keyof ItemState<ArticleItemType>, string> = {
43
44
  'color': 'color'
44
45
  };
45
46
 
46
- export function getTransitions<T extends ArticleItemType>(
47
- values: Array<keyof ItemState<ArticleItemType>>,
48
- state?: ItemState<ArticleItemType>
47
+ export function getStateStyles<T extends ArticleItemType>(
48
+ values: Array<StatePropertyKey>,
49
+ state?: ItemStatesMap[T],
50
+ anchorSide?: AnchorSide
49
51
  ): string {
50
- if (!state) return 'unset';
51
- const transitionValues = values.reduce<string[]>((acc, valueName) => {
52
+ if (!state) return '';
53
+
54
+ const stateValues = values.reduce<string[]>((acc, valueName) => {
55
+ // @ts-ignore
52
56
  if (valueName in state && state[valueName] !== undefined) {
53
- // @ts-ignore TODO
57
+ // @ts-ignore
54
58
  const stateProperties = state[valueName] as StateParams<string | number>;
59
+ const getter = stateTransformationMap[valueName] as StateParamsGetter;
55
60
  return [
56
61
  ...acc,
57
- `${CSSPropertyNameMap[valueName]} ${stateProperties!.duration}ms ${stateProperties!.timing} ${stateProperties!.delay}ms`
62
+ getter(stateProperties.value, anchorSide)
58
63
  ];
59
64
  }
60
65
  return acc;
61
66
  }, []);
62
- if (!transitionValues.length) return 'unset';
63
- return transitionValues.join(', ');
67
+
68
+ if (!stateValues.length) return '';
69
+ // @ts-ignore
70
+ const transitionStr = getTransitions(values, state);
71
+ stateValues.push(`transition: ${transitionStr};`);
72
+ return stateValues.join('\n');
64
73
  }
65
74
 
66
- export function getStateStyles<T extends ArticleItemType>(
67
- values: Array<keyof ItemState<ArticleItemType>>,
68
- state?: ItemState<ArticleItemType>,
69
- anchorSide?: AnchorSide
75
+ export function getTransitions<T extends ArticleItemType>(
76
+ values: Array<StatePropertyKey>,
77
+ state?: ItemStatesMap[T]
70
78
  ): string {
71
- if (!state) return '';
72
- const stateValues = values.reduce<string[]>((acc, valueName) => {
79
+ if (!state) return 'unset';
80
+ const transitionValues = values.reduce<string[]>((acc, valueName) => {
81
+ // @ts-ignore TODO
73
82
  if (valueName in state && state[valueName] !== undefined) {
74
83
  // @ts-ignore TODO
75
84
  const stateProperties = state[valueName] as StateParams<string | number>;
76
85
  return [
77
86
  ...acc,
78
- stateTransformationMap[valueName](stateProperties.value, anchorSide)
87
+ // @ts-ignore
88
+ `${CSSPropertyNameMap[valueName]} ${stateProperties!.duration}ms ${stateProperties!.timing} ${stateProperties!.delay}ms`
79
89
  ];
80
90
  }
81
91
  return acc;
82
92
  }, []);
83
- if (!stateValues.length) return '';
84
- return stateValues.join('\n');
93
+ if (!transitionValues.length) return 'unset';
94
+ return transitionValues.join(', ');
85
95
  }
86
96
 
97
+ // export function getStateTransitions<T extends ArticleItemType>(
98
+ //
99
+ // )
@@ -1,11 +1,14 @@
1
- import { AnchorSide, ArticleItemType, ItemState } from '@cntrl-site/sdk';
1
+ import { AnchorSide, ArticleItemType, ItemStatesMap } from '@cntrl-site/sdk';
2
2
  import { getStateStyles } from './StateStyles/StateStyles';
3
3
 
4
- export function getStatesCSS(
4
+ type AllKeys<T> = T extends any ? keyof T : never;
5
+ type StatePropertyKey = AllKeys<ItemStatesMap[keyof ItemStatesMap]>;
6
+
7
+ export function getStatesCSS<T extends ArticleItemType>(
5
8
  itemId: string,
6
9
  classNamePrefix: string,
7
- keys: Array<keyof ItemState<ArticleItemType>>,
8
- states: Record<string, ItemState<ArticleItemType>> | undefined,
10
+ keys: Array<StatePropertyKey>,
11
+ states: Record<string, ItemStatesMap[T]> | undefined,
9
12
  anchorSide?: AnchorSide
10
13
  ) {
11
14
  return states