@cntrl-site/sdk-nextjs 0.14.1 → 0.14.3

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 (33) hide show
  1. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  2. package/lib/components/Item.js +5 -11
  3. package/lib/components/items/ImageItem.js +5 -4
  4. package/lib/components/items/RectangleItem.js +6 -4
  5. package/lib/components/items/RichTextItem.js +6 -5
  6. package/lib/components/items/VideoItem.js +5 -4
  7. package/lib/components/items/VimeoEmbed.js +5 -4
  8. package/lib/components/items/YoutubeEmbed.js +5 -4
  9. package/lib/components/items/useEmbedVideoItem.js +7 -1
  10. package/lib/components/items/useFileItem.js +7 -1
  11. package/lib/components/items/useRectangleItem.js +13 -1
  12. package/lib/components/items/useRichTextItemValues.js +17 -0
  13. package/lib/components/items/useStickyItemTop.js +14 -0
  14. package/lib/utils/Animator/Animator.js +33 -1
  15. package/lib/utils/HoverStyles/HoverStyles.js +5 -1
  16. package/lib/utils/getAnchoredItemTop.js +15 -0
  17. package/package.json +2 -2
  18. package/src/components/Item.tsx +4 -11
  19. package/src/components/items/ImageItem.tsx +5 -4
  20. package/src/components/items/RectangleItem.tsx +6 -4
  21. package/src/components/items/RichTextItem.tsx +6 -5
  22. package/src/components/items/VideoItem.tsx +5 -4
  23. package/src/components/items/VimeoEmbed.tsx +6 -6
  24. package/src/components/items/YoutubeEmbed.tsx +5 -4
  25. package/src/components/items/useEmbedVideoItem.ts +13 -2
  26. package/src/components/items/useFileItem.ts +12 -1
  27. package/src/components/items/useRectangleItem.ts +23 -1
  28. package/src/components/items/useRichTextItemValues.ts +28 -0
  29. package/src/components/items/useStickyItemTop.ts +18 -0
  30. package/src/utils/Animator/Animator.ts +39 -1
  31. package/src/utils/HoverStyles/HoverStyles.ts +5 -1
  32. package/src/utils/getAnchoredItemTop.ts +12 -0
  33. package/cntrl-site-sdk-nextjs-0.12.9.tgz +0 -0
@@ -0,0 +1,5 @@
1
+ <component name="ProjectCodeStyleConfiguration">
2
+ <state>
3
+ <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
4
+ </state>
5
+ </component>
@@ -24,6 +24,8 @@ const ScaleAnchorMap_1 = require("../utils/ScaleAnchorMap");
24
24
  const useSectionHeightMap_1 = require("./useSectionHeightMap");
25
25
  const HoverStyles_1 = require("../utils/HoverStyles/HoverStyles");
26
26
  const getItemTopStyle_1 = require("../utils/getItemTopStyle");
27
+ const useStickyItemTop_1 = require("./items/useStickyItemTop");
28
+ const getAnchoredItemTop_1 = require("../utils/getAnchoredItemTop");
27
29
  const itemsMap = {
28
30
  [sdk_1.ArticleItemType.Rectangle]: RectangleItem_1.RectangleItem,
29
31
  [sdk_1.ArticleItemType.Image]: ImageItem_1.ImageItem,
@@ -41,6 +43,7 @@ const Item = ({ item, sectionId }) => {
41
43
  const { scale, scaleAnchor } = (0, useItemScale_1.useItemScale)(item, sectionId);
42
44
  const { top, left } = (0, useItemPosition_1.useItemPosition)(item, sectionId);
43
45
  const sectionHeight = (0, useSectionHeightMap_1.useSectionHeightData)(sectionId);
46
+ const stickyTop = (0, useStickyItemTop_1.useStickyItemTop)(item, sectionHeight, sectionId);
44
47
  const layout = (0, useCurrentLayout_1.useCurrentLayout)();
45
48
  const { width, height } = (0, useItemDimensions_1.useItemDimensions)(item, sectionId);
46
49
  const layoutValues = [item.area, item.hidden, item.state.hover];
@@ -72,6 +75,7 @@ const Item = ({ item, sectionId }) => {
72
75
  transformOrigin: ScaleAnchorMap_1.ScaleAnchorMap[scaleAnchor],
73
76
  width: `${sizingAxis.x === sdk_1.ArticleItemSizingType.Manual ? `${width * 100}vw` : 'max-content'}`,
74
77
  height: `${sizingAxis.y === sdk_1.ArticleItemSizingType.Manual ? `${height * 100}vw` : 'unset'}`,
78
+ top: stickyTop
75
79
  };
76
80
  return ((0, jsx_runtime_1.jsxs)("div", { className: `item-wrapper-${item.id}`, style: { top, left, ...(wrapperHeight ? { height: `${wrapperHeight * 100}vw` } : {}) }, children: [(0, jsx_runtime_1.jsx)("div", { suppressHydrationWarning: true, className: `item-${item.id}`, style: isInitialRef.current ? {} : styles, children: (0, jsx_runtime_1.jsx)(ItemComponent, { item: item, sectionId: sectionId, onResize: handleItemResize }) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
77
81
  ${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, hidden, hoverParams, sticky, sectionHeight, layoutParams]) => {
@@ -82,7 +86,7 @@ const Item = ({ item, sectionId }) => {
82
86
  width: ${sizingAxis.x === sdk_1.ArticleItemSizingType.Manual ? `${area.width * 100}vw` : 'max-content'};
83
87
  height: ${sizingAxis.y === sdk_1.ArticleItemSizingType.Manual ? `w${area.height * 100}vw` : 'unset'};
84
88
  transform: scale(${scale});
85
- top: ${sticky ? `${getAnchoredItemTop(area.top - sticky.from, sectionHeight, area.anchorSide)}` : 0};
89
+ top: ${sticky ? `${(0, getAnchoredItemTop_1.getAnchoredItemTop)(area.top - sticky.from, sectionHeight, area.anchorSide)}` : 0};
86
90
  transform-origin: ${ScaleAnchorMap_1.ScaleAnchorMap[scaleAnchor]};
87
91
  pointer-events: auto;
88
92
  --webkit-backface-visibility: hidden;
@@ -112,16 +116,6 @@ const Item = ({ item, sectionId }) => {
112
116
  ` })] }));
113
117
  };
114
118
  exports.Item = Item;
115
- function getAnchoredItemTop(top, sectionHeight, anchorSide) {
116
- const styleTop = `${top * 100}vw`;
117
- switch (anchorSide) {
118
- case sdk_1.AnchorSide.Center: return `calc(${styleTop} + ${sectionHeight} / 2)`;
119
- case sdk_1.AnchorSide.Bottom: return `calc(${styleTop} + ${sectionHeight})`;
120
- case sdk_1.AnchorSide.Top:
121
- default:
122
- return styleTop;
123
- }
124
- }
125
119
  function getStickyItemWrapperHeight(sticky, itemHeight) {
126
120
  const end = sticky.to ?? 100;
127
121
  return end - sticky.from + itemHeight;
@@ -16,7 +16,7 @@ const HoverStyles_1 = require("../../utils/HoverStyles/HoverStyles");
16
16
  const ImageItem = ({ item, sectionId }) => {
17
17
  const id = (0, react_1.useId)();
18
18
  const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
19
- const { radius, strokeWidth, opacity, strokeColor } = (0, useFileItem_1.useFileItem)(item, sectionId);
19
+ const { radius, strokeWidth, opacity, strokeColor, blur } = (0, useFileItem_1.useFileItem)(item, sectionId);
20
20
  const angle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
21
21
  const borderColor = (0, react_1.useMemo)(() => sdk_1.CntrlColor.parse(strokeColor), [strokeColor]);
22
22
  return ((0, jsx_runtime_1.jsx)(LinkWrapper_1.LinkWrapper, { url: item.link?.url, children: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `image-wrapper-${item.id}`, style: {
@@ -24,7 +24,8 @@ const ImageItem = ({ item, sectionId }) => {
24
24
  borderWidth: `${strokeWidth * 100}vw`,
25
25
  opacity: `${opacity}`,
26
26
  borderColor: `${borderColor.toCss()}`,
27
- transform: `rotate(${angle}deg)`
27
+ transform: `rotate(${angle}deg)`,
28
+ filter: `blur(${blur * 100}vw)`
28
29
  }, children: (0, jsx_runtime_1.jsx)("img", { className: "image", src: item.commonParams.url }) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
29
30
  @supports not (color: oklch(42% 0.3 90 / 1)) {
30
31
  .image-wrapper-${item.id} {
@@ -50,10 +51,10 @@ const ImageItem = ({ item, sectionId }) => {
50
51
  ${(0, sdk_1.getLayoutStyles)(layouts, [item.state.hover], ([hoverParams]) => {
51
52
  return (`
52
53
  .image-wrapper-${item.id} {
53
- transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity'], hoverParams)};
54
+ transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity', 'blur'], hoverParams)};
54
55
  }
55
56
  .image-wrapper-${item.id}:hover {
56
- ${(0, HoverStyles_1.getHoverStyles)(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity'], hoverParams)}
57
+ ${(0, HoverStyles_1.getHoverStyles)(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity', 'blur'], hoverParams)}
57
58
  }
58
59
  `);
59
60
  })}
@@ -16,7 +16,7 @@ const HoverStyles_1 = require("../../utils/HoverStyles/HoverStyles");
16
16
  const RectangleItem = ({ item, sectionId }) => {
17
17
  const id = (0, react_1.useId)();
18
18
  const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
19
- const { fillColor, radius, strokeWidth, strokeColor } = (0, useRectangleItem_1.useRectangleItem)(item, sectionId);
19
+ const { fillColor, radius, strokeWidth, strokeColor, blur, backdropBlur } = (0, useRectangleItem_1.useRectangleItem)(item, sectionId);
20
20
  const angle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
21
21
  const backgroundColor = (0, react_1.useMemo)(() => sdk_1.CntrlColor.parse(fillColor), [fillColor]);
22
22
  const borderColor = (0, react_1.useMemo)(() => sdk_1.CntrlColor.parse(strokeColor), [strokeColor]);
@@ -25,7 +25,9 @@ const RectangleItem = ({ item, sectionId }) => {
25
25
  borderRadius: `${radius * 100}vw`,
26
26
  borderWidth: `${strokeWidth * 100}vw`,
27
27
  borderColor: `${borderColor.toCss()}`,
28
- transform: `rotate(${angle}deg)`
28
+ transform: `rotate(${angle}deg)`,
29
+ filter: blur !== 0 ? `blur(${blur * 100}vw)` : '',
30
+ backdropFilter: backdropBlur !== 0 ? `blur(${backdropBlur * 100}vw)` : ''
29
31
  } }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
30
32
  @supports not (color: oklch(42% 0.3 90 / 1)) {
31
33
  .rectangle-${item.id} {
@@ -43,10 +45,10 @@ const RectangleItem = ({ item, sectionId }) => {
43
45
  ${(0, sdk_1.getLayoutStyles)(layouts, [item.state.hover], ([hoverParams]) => {
44
46
  return (`
45
47
  .rectangle-${item.id} {
46
- transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor'], hoverParams)};
48
+ transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur'], hoverParams)};
47
49
  }
48
50
  .rectangle-${item.id}:hover {
49
- ${(0, HoverStyles_1.getHoverStyles)(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor'], hoverParams)}
51
+ ${(0, HoverStyles_1.getHoverStyles)(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur'], hoverParams)}
50
52
  }
51
53
  `);
52
54
  })}
@@ -9,16 +9,16 @@ const react_1 = require("react");
9
9
  const sdk_1 = require("@cntrl-site/sdk");
10
10
  const style_1 = __importDefault(require("styled-jsx/style"));
11
11
  const useRichTextItem_1 = require("./useRichTextItem");
12
- const useItemAngle_1 = require("../useItemAngle");
13
12
  const useCntrlContext_1 = require("../../provider/useCntrlContext");
14
13
  const HoverStyles_1 = require("../../utils/HoverStyles/HoverStyles");
14
+ const useRichTextItemValues_1 = require("./useRichTextItemValues");
15
15
  const RichTextItem = ({ item, sectionId, onResize }) => {
16
16
  const [content, styles, preset] = (0, useRichTextItem_1.useRichTextItem)(item);
17
17
  const id = (0, react_1.useId)();
18
18
  const [ref, setRef] = (0, react_1.useState)(null);
19
- const angle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
20
19
  const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
21
20
  const className = preset ? `cntrl-preset-${preset.id}` : undefined;
21
+ const { angle, blur } = (0, useRichTextItemValues_1.useRichTextItemValues)(item, sectionId);
22
22
  (0, react_1.useEffect)(() => {
23
23
  if (!ref || !onResize)
24
24
  return;
@@ -32,14 +32,15 @@ const RichTextItem = ({ item, sectionId, onResize }) => {
32
32
  };
33
33
  }, [ref, onResize]);
34
34
  return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { ref: setRef, className: `${className} rich-text-wrapper-${item.id}`, style: {
35
- transform: `rotate(${angle}deg)`
35
+ transform: `rotate(${angle}deg)`,
36
+ filter: `blur(${blur * 100}vw)`
36
37
  }, children: content }), (0, jsx_runtime_1.jsxs)(style_1.default, { id: id, children: [styles, `${(0, sdk_1.getLayoutStyles)(layouts, [item.state.hover], ([hoverParams]) => {
37
38
  return (`
38
39
  .rich-text-wrapper-${item.id} {
39
- transition: ${(0, HoverStyles_1.getTransitions)(['angle'], hoverParams)};
40
+ transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'blur'], hoverParams)};
40
41
  }
41
42
  .rich-text-wrapper-${item.id}:hover {
42
- ${(0, HoverStyles_1.getHoverStyles)(['angle'], hoverParams)}
43
+ ${(0, HoverStyles_1.getHoverStyles)(['angle', 'blur'], hoverParams)}
43
44
  }
44
45
  `);
45
46
  })}`] })] }));
@@ -16,7 +16,7 @@ const HoverStyles_1 = require("../../utils/HoverStyles/HoverStyles");
16
16
  const VideoItem = ({ item, sectionId }) => {
17
17
  const id = (0, react_1.useId)();
18
18
  const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
19
- const { radius, strokeWidth, strokeColor, opacity } = (0, useFileItem_1.useFileItem)(item, sectionId);
19
+ const { radius, strokeWidth, strokeColor, opacity, blur } = (0, useFileItem_1.useFileItem)(item, sectionId);
20
20
  const angle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
21
21
  const borderColor = (0, react_1.useMemo)(() => sdk_1.CntrlColor.parse(strokeColor), [strokeColor]);
22
22
  return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: item.link?.url, children: [(0, jsx_runtime_1.jsx)("div", { className: `video-wrapper-${item.id}`, style: {
@@ -24,7 +24,8 @@ const VideoItem = ({ item, sectionId }) => {
24
24
  borderWidth: `${strokeWidth * 100}vw`,
25
25
  opacity: `${opacity}`,
26
26
  borderColor: `${borderColor.toCss()}`,
27
- transform: `rotate(${angle}deg)`
27
+ transform: `rotate(${angle}deg)`,
28
+ filter: `blur(${blur * 100}vw)`
28
29
  }, children: (0, jsx_runtime_1.jsx)("video", { autoPlay: true, muted: true, loop: true, playsInline: true, className: "video", children: (0, jsx_runtime_1.jsx)("source", { src: item.commonParams.url }) }) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
29
30
  @supports not (color: oklch(42% 0.3 90 / 1)) {
30
31
  .video-wrapper-${item.id} {
@@ -51,10 +52,10 @@ const VideoItem = ({ item, sectionId }) => {
51
52
  ${(0, sdk_1.getLayoutStyles)(layouts, [item.state.hover], ([hoverParams]) => {
52
53
  return (`
53
54
  .video-wrapper-${item.id} {
54
- transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity'], hoverParams)};
55
+ transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity', 'blur'], hoverParams)};
55
56
  }
56
57
  .video-wrapper-${item.id}:hover {
57
- ${(0, HoverStyles_1.getHoverStyles)(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity'], hoverParams)}
58
+ ${(0, HoverStyles_1.getHoverStyles)(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity', 'blur'], hoverParams)}
58
59
  }
59
60
  `);
60
61
  })}
@@ -17,7 +17,7 @@ const HoverStyles_1 = require("../../utils/HoverStyles/HoverStyles");
17
17
  const VimeoEmbedItem = ({ item, sectionId }) => {
18
18
  const id = (0, react_1.useId)();
19
19
  const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
20
- const { radius } = (0, useEmbedVideoItem_1.useEmbedVideoItem)(item, sectionId);
20
+ const { radius, blur } = (0, useEmbedVideoItem_1.useEmbedVideoItem)(item, sectionId);
21
21
  const [iframeRef, setIframeRef] = (0, react_1.useState)(null);
22
22
  const vimeoPlayer = (0, react_1.useMemo)(() => iframeRef ? new player_1.default(iframeRef) : undefined, [iframeRef]);
23
23
  const angle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
@@ -38,7 +38,8 @@ const VimeoEmbedItem = ({ item, sectionId }) => {
38
38
  const validUrl = getValidVimeoUrl(url);
39
39
  return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: item.link?.url, children: [(0, jsx_runtime_1.jsx)("div", { className: `embed-video-wrapper-${item.id}`, style: {
40
40
  borderRadius: `${radius * 100}vw`,
41
- transform: `rotate(${angle}deg)`
41
+ transform: `rotate(${angle}deg)`,
42
+ filter: `blur(${blur * 100}vw)`
42
43
  }, onMouseEnter: () => {
43
44
  if (!vimeoPlayer || play !== 'on-hover')
44
45
  return;
@@ -63,10 +64,10 @@ const VimeoEmbedItem = ({ item, sectionId }) => {
63
64
  ${(0, sdk_1.getLayoutStyles)(layouts, [item.state.hover], ([hoverParams]) => {
64
65
  return (`
65
66
  .embed-video-wrapper-${item.id} {
66
- transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'radius'], hoverParams)};
67
+ transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'radius', 'blur'], hoverParams)};
67
68
  }
68
69
  .embed-video-wrapper-${item.id}:hover {
69
- ${(0, HoverStyles_1.getHoverStyles)(['angle', 'radius'], hoverParams)}
70
+ ${(0, HoverStyles_1.getHoverStyles)(['angle', 'radius', 'blur'], hoverParams)}
70
71
  }
71
72
  `);
72
73
  })}
@@ -19,7 +19,7 @@ const YoutubeEmbedItem = ({ item, sectionId }) => {
19
19
  const id = (0, react_1.useId)();
20
20
  const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
21
21
  const { play, controls, url } = item.commonParams;
22
- const { radius } = (0, useEmbedVideoItem_1.useEmbedVideoItem)(item, sectionId);
22
+ const { radius, blur } = (0, useEmbedVideoItem_1.useEmbedVideoItem)(item, sectionId);
23
23
  const angle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
24
24
  const YT = (0, useYouTubeIframeApi_1.useYouTubeIframeApi)();
25
25
  const [div, setDiv] = (0, react_1.useState)(null);
@@ -63,7 +63,8 @@ const YoutubeEmbedItem = ({ item, sectionId }) => {
63
63
  player.pauseVideo();
64
64
  }, ref: setDiv, style: {
65
65
  borderRadius: `${radius * 100}vw`,
66
- transform: `rotate(${angle}deg)`
66
+ transform: `rotate(${angle}deg)`,
67
+ filter: `blur(${blur * 100}vw)`
67
68
  } }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
68
69
  .embed-youtube-video-wrapper-${item.id} {
69
70
  position: absolute;
@@ -80,10 +81,10 @@ const YoutubeEmbedItem = ({ item, sectionId }) => {
80
81
  ${(0, sdk_1.getLayoutStyles)(layouts, [item.state.hover], ([hoverParams]) => {
81
82
  return (`
82
83
  .embed-youtube-video-wrapper-${item.id} {
83
- transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'radius'], hoverParams)};
84
+ transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'radius', 'blur'], hoverParams)};
84
85
  }
85
86
  .embed-youtube-video-wrapper-${item.id}:hover {
86
- ${(0, HoverStyles_1.getHoverStyles)(['angle', 'radius'], hoverParams)}
87
+ ${(0, HoverStyles_1.getHoverStyles)(['angle', 'radius', 'blur'], hoverParams)}
87
88
  }
88
89
  `);
89
90
  })}
@@ -11,6 +11,12 @@ const useEmbedVideoItem = (item, sectionId) => {
11
11
  const layoutParams = item.layoutParams[layoutId];
12
12
  return 'radius' in layoutParams ? layoutParams.radius : 0;
13
13
  }, (animator, scroll, value) => animator.getRadius({ radius: value }, scroll).radius, sectionId, [layoutId]);
14
- return { radius };
14
+ const blur = (0, useKeyframeValue_1.useKeyframeValue)(item, (item, layoutId) => {
15
+ if (!layoutId)
16
+ return 0;
17
+ const layoutParams = item.layoutParams[layoutId];
18
+ return 'blur' in layoutParams ? layoutParams.blur : 0;
19
+ }, (animator, scroll, value) => animator.getBlur({ blur: value }, scroll).blur, sectionId, [layoutId]);
20
+ return { radius, blur };
15
21
  };
16
22
  exports.useEmbedVideoItem = useEmbedVideoItem;
@@ -30,6 +30,12 @@ const useFileItem = (item, sectionId) => {
30
30
  const layoutParams = item.layoutParams[layoutId];
31
31
  return 'strokeColor' in layoutParams ? layoutParams.strokeColor : defaultColor;
32
32
  }, (animator, scroll, value) => animator.getBorderColor({ color: value }, scroll).color, sectionId);
33
- return { radius, strokeWidth, opacity, strokeColor };
33
+ const blur = (0, useKeyframeValue_1.useKeyframeValue)(item, (item, layoutId) => {
34
+ if (!layoutId)
35
+ return 0;
36
+ const layoutParams = item.layoutParams[layoutId];
37
+ return 'blur' in layoutParams ? layoutParams.blur : 0;
38
+ }, (animator, scroll, value) => animator.getBlur({ blur: value }, scroll).blur, sectionId);
39
+ return { radius, strokeWidth, opacity, strokeColor, blur };
34
40
  };
35
41
  exports.useFileItem = useFileItem;
@@ -30,6 +30,18 @@ const useRectangleItem = (item, sectionId) => {
30
30
  const layoutParams = item.layoutParams[layoutId];
31
31
  return 'strokeColor' in layoutParams ? layoutParams.strokeColor : defaultColor;
32
32
  }, (animator, scroll, value) => animator.getBorderColor({ color: value }, scroll).color, sectionId, [layoutId]);
33
- return { fillColor, strokeWidth, radius, strokeColor };
33
+ const blur = (0, useKeyframeValue_1.useKeyframeValue)(item, (item, layoutId) => {
34
+ if (!layoutId)
35
+ return 0;
36
+ const layoutParams = item.layoutParams[layoutId];
37
+ return 'blur' in layoutParams ? layoutParams.blur : 0;
38
+ }, (animator, scroll, value) => animator.getBlur({ blur: value }, scroll).blur, sectionId, [layoutId]);
39
+ const backdropBlur = (0, useKeyframeValue_1.useKeyframeValue)(item, (item, layoutId) => {
40
+ if (!layoutId)
41
+ return 0;
42
+ const layoutParams = item.layoutParams[layoutId];
43
+ return 'backdropBlur' in layoutParams ? layoutParams.backdropBlur : 0;
44
+ }, (animator, scroll, value) => animator.getBackdropBlur({ backdropBlur: value }, scroll).backdropBlur, sectionId, [layoutId]);
45
+ return { fillColor, strokeWidth, radius, strokeColor, blur, backdropBlur };
34
46
  };
35
47
  exports.useRectangleItem = useRectangleItem;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useRichTextItemValues = void 0;
4
+ const useCurrentLayout_1 = require("../../common/useCurrentLayout");
5
+ const useKeyframeValue_1 = require("../../common/useKeyframeValue");
6
+ const useRichTextItemValues = (item, sectionId) => {
7
+ const layoutId = (0, useCurrentLayout_1.useCurrentLayout)();
8
+ const angle = (0, useKeyframeValue_1.useKeyframeValue)(item, (item, layoutId) => ({ angle: layoutId ? item.area[layoutId].angle : 0 }), (animator, scroll, value) => animator.getRotation(value, scroll), sectionId);
9
+ const blur = (0, useKeyframeValue_1.useKeyframeValue)(item, (item, layoutId) => {
10
+ if (!layoutId)
11
+ return 0;
12
+ const layoutParams = item.layoutParams[layoutId];
13
+ return 'blur' in layoutParams ? layoutParams.blur : 0;
14
+ }, (animator, scroll, value) => animator.getBlur({ blur: value }, scroll).blur, sectionId, [layoutId]);
15
+ return { angle, blur };
16
+ };
17
+ exports.useRichTextItemValues = useRichTextItemValues;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useStickyItemTop = void 0;
4
+ const useCurrentLayout_1 = require("../../common/useCurrentLayout");
5
+ const useKeyframeValue_1 = require("../../common/useKeyframeValue");
6
+ const getAnchoredItemTop_1 = require("../../utils/getAnchoredItemTop");
7
+ function useStickyItemTop(item, sectionHeightMap, sectionId) {
8
+ const layoutId = (0, useCurrentLayout_1.useCurrentLayout)();
9
+ const { top } = (0, useKeyframeValue_1.useKeyframeValue)(item, (item, layoutId) => item.area[layoutId], (animator, scroll, value) => animator.getPositions(value, scroll), sectionId, [layoutId]);
10
+ const sticky = item.sticky[layoutId];
11
+ const sectionHeight = sectionHeightMap[layoutId];
12
+ return sticky ? `${(0, getAnchoredItemTop_1.getAnchoredItemTop)(top - sticky.from, sectionHeight, item.area[layoutId].anchorSide)}` : 0;
13
+ }
14
+ exports.useStickyItemTop = useStickyItemTop;
@@ -153,6 +153,36 @@ class Animator {
153
153
  scale: rangeMap(pos, start.position, end.position, start.value.scale, end.value.scale, true)
154
154
  };
155
155
  }
156
+ getBlur(values, pos) {
157
+ const keyframes = this.keyframesMap[sdk_1.KeyframeType.Blur];
158
+ if (!keyframes || !keyframes.length)
159
+ return values;
160
+ if (keyframes.length === 1) {
161
+ const [keyframe] = keyframes;
162
+ return {
163
+ blur: keyframe.value.blur
164
+ };
165
+ }
166
+ const { start, end } = this.getStartEnd(pos, keyframes);
167
+ return {
168
+ blur: rangeMap(pos, start.position, end.position, start.value.blur, end.value.blur, true)
169
+ };
170
+ }
171
+ getBackdropBlur(values, pos) {
172
+ const keyframes = this.keyframesMap[sdk_1.KeyframeType.BackdropBlur];
173
+ if (!keyframes || !keyframes.length)
174
+ return values;
175
+ if (keyframes.length === 1) {
176
+ const [keyframe] = keyframes;
177
+ return {
178
+ backdropBlur: keyframe.value.backdropBlur
179
+ };
180
+ }
181
+ const { start, end } = this.getStartEnd(pos, keyframes);
182
+ return {
183
+ backdropBlur: rangeMap(pos, start.position, end.position, start.value.backdropBlur, end.value.backdropBlur, true)
184
+ };
185
+ }
156
186
  getStartEnd(position, keyframes) {
157
187
  const index = (0, binSearchInsertAt_1.binSearchInsertAt)(keyframes, { position }, compare);
158
188
  const end = index === keyframes.length ? index - 1 : index;
@@ -184,7 +214,9 @@ function createKeyframesMap() {
184
214
  [sdk_1.KeyframeType.Rotation]: [],
185
215
  [sdk_1.KeyframeType.BorderColor]: [],
186
216
  [sdk_1.KeyframeType.Opacity]: [],
187
- [sdk_1.KeyframeType.Scale]: []
217
+ [sdk_1.KeyframeType.Scale]: [],
218
+ [sdk_1.KeyframeType.Blur]: [],
219
+ [sdk_1.KeyframeType.BackdropBlur]: []
188
220
  };
189
221
  }
190
222
  function rangeMap(n, start1, stop1, start2, stop2, withinBounds = false) {
@@ -15,6 +15,8 @@ const hoverTransformationMap = {
15
15
  'strokeWidth': (strokeWidth) => `border-width: ${strokeWidth * 100}vw !important;`,
16
16
  'strokeColor': (strokeColor) => `border-color: ${sdk_1.CntrlColor.parse(strokeColor).toCss()} !important;`,
17
17
  'fillColor': (fillColor) => `background-color: ${sdk_1.CntrlColor.parse(fillColor).toCss()} !important;`,
18
+ 'blur': (blur) => `filter: blur(${blur * 100}vw) !important;`,
19
+ 'backdropBlur': (backdropBlur) => `backdrop-filter: blur(${backdropBlur * 100}vw) !important;`
18
20
  };
19
21
  const CSSPropertyNameMap = {
20
22
  'width': 'width',
@@ -27,7 +29,9 @@ const CSSPropertyNameMap = {
27
29
  'radius': 'border-radius',
28
30
  'strokeWidth': 'border-width',
29
31
  'strokeColor': 'border-color',
30
- 'fillColor': 'background-color'
32
+ 'fillColor': 'background-color',
33
+ 'blur': 'filter',
34
+ 'backdropBlur': 'backdrop-filter'
31
35
  };
32
36
  function getTransitions(values, hover) {
33
37
  if (!hover)
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAnchoredItemTop = void 0;
4
+ const sdk_1 = require("@cntrl-site/sdk");
5
+ function getAnchoredItemTop(top, sectionHeight, anchorSide) {
6
+ const styleTop = `${top * 100}vw`;
7
+ switch (anchorSide) {
8
+ case sdk_1.AnchorSide.Center: return `calc(${styleTop} + ${sectionHeight} / 2)`;
9
+ case sdk_1.AnchorSide.Bottom: return `calc(${styleTop} + ${sectionHeight})`;
10
+ case sdk_1.AnchorSide.Top:
11
+ default:
12
+ return styleTop;
13
+ }
14
+ }
15
+ exports.getAnchoredItemTop = getAnchoredItemTop;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk-nextjs",
3
- "version": "0.14.1",
3
+ "version": "0.14.3",
4
4
  "description": "SDK for Next.js",
5
5
  "main": "lib/index.js",
6
6
  "types": "src/index.ts",
@@ -21,8 +21,8 @@
21
21
  },
22
22
  "homepage": "https://github.com/cntrl-site/sdk-nextjs#readme",
23
23
  "dependencies": {
24
+ "@cntrl-site/sdk": "^1.1.9",
24
25
  "@types/vimeo__player": "^2.18.0",
25
- "@cntrl-site/sdk": "^1.1.8",
26
26
  "@vimeo/player": "^2.20.1",
27
27
  "html-react-parser": "^3.0.1",
28
28
  "styled-jsx": "^5.0.2"
@@ -26,6 +26,8 @@ import { ScaleAnchorMap } from '../utils/ScaleAnchorMap';
26
26
  import { useSectionHeightData } from './useSectionHeightMap';
27
27
  import { getHoverStyles, getTransitions } from '../utils/HoverStyles/HoverStyles';
28
28
  import { getItemTopStyle } from '../utils/getItemTopStyle';
29
+ import { useStickyItemTop } from './items/useStickyItemTop';
30
+ import { getAnchoredItemTop } from '../utils/getAnchoredItemTop';
29
31
 
30
32
  export interface ItemProps<I extends TArticleItemAny> {
31
33
  item: I;
@@ -52,6 +54,7 @@ export const Item: FC<ItemProps<TArticleItemAny>> = ({ item, sectionId}) => {
52
54
  const { scale, scaleAnchor } = useItemScale(item, sectionId);
53
55
  const { top, left } = useItemPosition(item, sectionId);
54
56
  const sectionHeight = useSectionHeightData(sectionId);
57
+ const stickyTop = useStickyItemTop(item, sectionHeight, sectionId);
55
58
  const layout = useCurrentLayout();
56
59
  const { width, height } = useItemDimensions(item, sectionId);
57
60
  const layoutValues: Record<string, any>[] = [item.area, item.hidden, item.state.hover];
@@ -87,6 +90,7 @@ export const Item: FC<ItemProps<TArticleItemAny>> = ({ item, sectionId}) => {
87
90
  transformOrigin: ScaleAnchorMap[scaleAnchor],
88
91
  width: `${sizingAxis.x === SizingType.Manual ? `${width * 100}vw` : 'max-content'}`,
89
92
  height: `${sizingAxis.y === SizingType.Manual ? `${height * 100}vw` : 'unset'}`,
93
+ top: stickyTop
90
94
  };
91
95
 
92
96
  return (
@@ -142,17 +146,6 @@ export const Item: FC<ItemProps<TArticleItemAny>> = ({ item, sectionId}) => {
142
146
  );
143
147
  };
144
148
 
145
- function getAnchoredItemTop(top: number, sectionHeight: string, anchorSide: AnchorSide) {
146
- const styleTop = `${top * 100}vw`;
147
- switch (anchorSide) {
148
- case AnchorSide.Center: return `calc(${styleTop} + ${sectionHeight} / 2)`;
149
- case AnchorSide.Bottom: return `calc(${styleTop} + ${sectionHeight})`;
150
- case AnchorSide.Top:
151
- default:
152
- return styleTop;
153
- }
154
- }
155
-
156
149
  function getStickyItemWrapperHeight(sticky: TStickyParams, itemHeight: number): number {
157
150
  const end = sticky.to ?? 100;
158
151
  return end - sticky.from + itemHeight;
@@ -11,7 +11,7 @@ import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverSty
11
11
  export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId }) => {
12
12
  const id = useId();
13
13
  const { layouts } = useCntrlContext();
14
- const { radius, strokeWidth, opacity, strokeColor } = useFileItem(item, sectionId);
14
+ const { radius, strokeWidth, opacity, strokeColor, blur } = useFileItem(item, sectionId);
15
15
  const angle = useItemAngle(item, sectionId);
16
16
  const borderColor = useMemo(() => CntrlColor.parse(strokeColor), [strokeColor]);
17
17
  return (
@@ -24,7 +24,8 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId }) => {
24
24
  borderWidth: `${strokeWidth * 100}vw`,
25
25
  opacity: `${opacity}`,
26
26
  borderColor: `${borderColor.toCss()}`,
27
- transform: `rotate(${angle}deg)`
27
+ transform: `rotate(${angle}deg)`,
28
+ filter: `blur(${blur * 100}vw)`
28
29
  }}
29
30
  >
30
31
  <img className="image" src={item.commonParams.url} />
@@ -54,10 +55,10 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId }) => {
54
55
  ${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
55
56
  return (`
56
57
  .image-wrapper-${item.id} {
57
- transition: ${getTransitions<ArticleItemType.Image>(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity'], hoverParams)};
58
+ transition: ${getTransitions<ArticleItemType.Image>(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity', 'blur'], hoverParams)};
58
59
  }
59
60
  .image-wrapper-${item.id}:hover {
60
- ${getHoverStyles<ArticleItemType.Image>(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity'], hoverParams)}
61
+ ${getHoverStyles<ArticleItemType.Image>(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity', 'blur'], hoverParams)}
61
62
  }
62
63
  `);
63
64
  })}
@@ -11,7 +11,7 @@ import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverSty
11
11
  export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId }) => {
12
12
  const id = useId();
13
13
  const { layouts } = useCntrlContext();
14
- const { fillColor, radius, strokeWidth, strokeColor } = useRectangleItem(item, sectionId);
14
+ const { fillColor, radius, strokeWidth, strokeColor, blur, backdropBlur } = useRectangleItem(item, sectionId);
15
15
  const angle = useItemAngle(item, sectionId);
16
16
  const backgroundColor = useMemo(() => CntrlColor.parse(fillColor), [fillColor]);
17
17
  const borderColor = useMemo(() => CntrlColor.parse(strokeColor), [strokeColor]);
@@ -25,7 +25,9 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId }
25
25
  borderRadius: `${radius * 100}vw`,
26
26
  borderWidth: `${strokeWidth * 100}vw`,
27
27
  borderColor: `${borderColor.toCss()}`,
28
- transform: `rotate(${angle}deg)`
28
+ transform: `rotate(${angle}deg)`,
29
+ filter: blur !== 0 ? `blur(${blur * 100}vw)` : '',
30
+ backdropFilter: backdropBlur !== 0 ? `blur(${backdropBlur * 100}vw)`: ''
29
31
  }}
30
32
  />
31
33
  <JSXStyle id={id}>{`
@@ -45,10 +47,10 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId }
45
47
  ${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
46
48
  return (`
47
49
  .rectangle-${item.id} {
48
- transition: ${getTransitions<ArticleItemType.Rectangle>(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor'], hoverParams)};
50
+ transition: ${getTransitions<ArticleItemType.Rectangle>(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur'], hoverParams)};
49
51
  }
50
52
  .rectangle-${item.id}:hover {
51
- ${getHoverStyles<ArticleItemType.Rectangle>(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor'], hoverParams)}
53
+ ${getHoverStyles<ArticleItemType.Rectangle>(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur'], hoverParams)}
52
54
  }
53
55
  `);
54
56
  })}
@@ -3,17 +3,17 @@ import { ArticleItemType, getLayoutStyles, TRichTextItem } from '@cntrl-site/sdk
3
3
  import JSXStyle from 'styled-jsx/style';
4
4
  import { ItemProps } from '../Item';
5
5
  import { useRichTextItem } from './useRichTextItem';
6
- import { useItemAngle } from '../useItemAngle';
7
6
  import { useCntrlContext } from '../../provider/useCntrlContext';
8
7
  import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
8
+ import { useRichTextItemValues } from './useRichTextItemValues';
9
9
 
10
10
  export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, onResize }) => {
11
11
  const [content, styles, preset] = useRichTextItem(item);
12
12
  const id = useId();
13
13
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
14
- const angle = useItemAngle(item, sectionId);
15
14
  const { layouts } = useCntrlContext();
16
15
  const className = preset ? `cntrl-preset-${preset.id}` : undefined;
16
+ const { angle, blur } = useRichTextItemValues(item, sectionId);
17
17
 
18
18
  useEffect(() => {
19
19
  if (!ref || !onResize) return;
@@ -33,7 +33,8 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
33
33
  ref={setRef}
34
34
  className={`${className} rich-text-wrapper-${item.id}`}
35
35
  style={{
36
- transform: `rotate(${angle}deg)`
36
+ transform: `rotate(${angle}deg)`,
37
+ filter: `blur(${blur * 100}vw)`
37
38
  }}
38
39
  >
39
40
  {content}
@@ -43,10 +44,10 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
43
44
  {`${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
44
45
  return (`
45
46
  .rich-text-wrapper-${item.id} {
46
- transition: ${getTransitions<ArticleItemType.RichText>(['angle'], hoverParams)};
47
+ transition: ${getTransitions<ArticleItemType.RichText>(['angle', 'blur'], hoverParams)};
47
48
  }
48
49
  .rich-text-wrapper-${item.id}:hover {
49
- ${getHoverStyles<ArticleItemType.RichText>(['angle'], hoverParams)}
50
+ ${getHoverStyles<ArticleItemType.RichText>(['angle', 'blur'], hoverParams)}
50
51
  }
51
52
  `);
52
53
  })}`}
@@ -11,7 +11,7 @@ import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverSty
11
11
  export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId }) => {
12
12
  const id = useId();
13
13
  const { layouts } = useCntrlContext();
14
- const { radius, strokeWidth, strokeColor, opacity } = useFileItem(item, sectionId);
14
+ const { radius, strokeWidth, strokeColor, opacity, blur } = useFileItem(item, sectionId);
15
15
  const angle = useItemAngle(item, sectionId);
16
16
  const borderColor = useMemo(() => CntrlColor.parse(strokeColor), [strokeColor]);
17
17
  return (
@@ -23,7 +23,8 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId }) => {
23
23
  borderWidth: `${strokeWidth * 100}vw`,
24
24
  opacity: `${opacity}`,
25
25
  borderColor: `${borderColor.toCss()}`,
26
- transform: `rotate(${angle}deg)`
26
+ transform: `rotate(${angle}deg)`,
27
+ filter: `blur(${blur * 100}vw)`
27
28
  }}
28
29
  >
29
30
  <video autoPlay muted loop playsInline className="video">
@@ -56,10 +57,10 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId }) => {
56
57
  ${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
57
58
  return (`
58
59
  .video-wrapper-${item.id} {
59
- transition: ${getTransitions<ArticleItemType.Video>(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity'], hoverParams)};
60
+ transition: ${getTransitions<ArticleItemType.Video>(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity', 'blur'], hoverParams)};
60
61
  }
61
62
  .video-wrapper-${item.id}:hover {
62
- ${getHoverStyles<ArticleItemType.Video>(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity'], hoverParams)}
63
+ ${getHoverStyles<ArticleItemType.Video>(['angle', 'strokeWidth', 'radius', 'strokeColor', 'opacity', 'blur'], hoverParams)}
63
64
  }
64
65
  `);
65
66
  })}
@@ -1,4 +1,4 @@
1
- import { FC, useEffect, useId, useMemo, useRef, useState } from 'react';
1
+ import { FC, useId, useMemo, useState } from 'react';
2
2
  import Player from '@vimeo/player';
3
3
  import JSXStyle from 'styled-jsx/style';
4
4
  import { TVimeoEmbedItem } from '@cntrl-site/core';
@@ -9,12 +9,11 @@ import { useItemAngle } from '../useItemAngle';
9
9
  import { ArticleItemType, getLayoutStyles } from '@cntrl-site/sdk';
10
10
  import { useCntrlContext } from '../../provider/useCntrlContext';
11
11
  import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
12
- import { useCurrentLayout } from '../../common/useCurrentLayout';
13
12
 
14
13
  export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId }) => {
15
14
  const id = useId();
16
15
  const { layouts } = useCntrlContext();
17
- const { radius } = useEmbedVideoItem(item, sectionId);
16
+ const { radius, blur } = useEmbedVideoItem(item, sectionId);
18
17
  const [iframeRef, setIframeRef] = useState<HTMLIFrameElement | null>(null);
19
18
  const vimeoPlayer = useMemo(() => iframeRef ? new Player(iframeRef) : undefined, [iframeRef]);
20
19
  const angle = useItemAngle(item, sectionId);
@@ -40,7 +39,8 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId
40
39
  <div className={`embed-video-wrapper-${item.id}`}
41
40
  style={{
42
41
  borderRadius: `${radius * 100}vw`,
43
- transform: `rotate(${angle}deg)`
42
+ transform: `rotate(${angle}deg)`,
43
+ filter: `blur(${blur * 100}vw)`
44
44
  }}
45
45
  onMouseEnter={() => {
46
46
  if (!vimeoPlayer || play !== 'on-hover') return;
@@ -75,10 +75,10 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId
75
75
  ${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
76
76
  return (`
77
77
  .embed-video-wrapper-${item.id} {
78
- transition: ${getTransitions<ArticleItemType.VimeoEmbed>(['angle', 'radius'], hoverParams)};
78
+ transition: ${getTransitions<ArticleItemType.VimeoEmbed>(['angle', 'radius', 'blur'], hoverParams)};
79
79
  }
80
80
  .embed-video-wrapper-${item.id}:hover {
81
- ${getHoverStyles<ArticleItemType.VimeoEmbed>(['angle', 'radius'], hoverParams)}
81
+ ${getHoverStyles<ArticleItemType.VimeoEmbed>(['angle', 'radius', 'blur'], hoverParams)}
82
82
  }
83
83
  `);
84
84
  })}
@@ -16,7 +16,7 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
16
16
  const id = useId();
17
17
  const { layouts } = useCntrlContext();
18
18
  const { play, controls, url } = item.commonParams;
19
- const { radius } = useEmbedVideoItem(item, sectionId);
19
+ const { radius, blur } = useEmbedVideoItem(item, sectionId);
20
20
  const angle = useItemAngle(item, sectionId);
21
21
  const YT = useYouTubeIframeApi();
22
22
  const [div, setDiv] = useState<HTMLDivElement | null>(null);
@@ -65,7 +65,8 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
65
65
  ref={setDiv}
66
66
  style={{
67
67
  borderRadius: `${radius * 100}vw`,
68
- transform: `rotate(${angle}deg)`
68
+ transform: `rotate(${angle}deg)`,
69
+ filter: `blur(${blur * 100}vw)`
69
70
  }}
70
71
  ></div>
71
72
  <JSXStyle id={id}>{`
@@ -84,10 +85,10 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
84
85
  ${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
85
86
  return (`
86
87
  .embed-youtube-video-wrapper-${item.id} {
87
- transition: ${getTransitions<ArticleItemType.YoutubeEmbed>(['angle', 'radius'], hoverParams)};
88
+ transition: ${getTransitions<ArticleItemType.YoutubeEmbed>(['angle', 'radius', 'blur'], hoverParams)};
88
89
  }
89
90
  .embed-youtube-video-wrapper-${item.id}:hover {
90
- ${getHoverStyles<ArticleItemType.YoutubeEmbed>(['angle', 'radius'], hoverParams)}
91
+ ${getHoverStyles<ArticleItemType.YoutubeEmbed>(['angle', 'radius', 'blur'], hoverParams)}
91
92
  }
92
93
  `);
93
94
  })}
@@ -9,12 +9,23 @@ export const useEmbedVideoItem = (item: TVimeoEmbedItem | TYoutubeEmbedItem, sec
9
9
  (item, layoutId) => {
10
10
  if (!layoutId) return 0;
11
11
  const layoutParams = item.layoutParams[layoutId];
12
- return 'radius' in layoutParams ? layoutParams.radius : 0;
12
+ return 'radius' in layoutParams ? layoutParams.radius : 0;
13
13
  },
14
14
  (animator, scroll, value) => animator.getRadius({ radius: value }, scroll).radius,
15
15
  sectionId,
16
16
  [layoutId]
17
17
  );
18
+ const blur = useKeyframeValue(
19
+ item,
20
+ (item, layoutId) => {
21
+ if (!layoutId) return 0;
22
+ const layoutParams = item.layoutParams[layoutId];
23
+ return 'blur' in layoutParams ? layoutParams.blur : 0;
24
+ },
25
+ (animator, scroll, value) => animator.getBlur({ blur: value }, scroll).blur,
26
+ sectionId,
27
+ [layoutId]
28
+ );
18
29
 
19
- return { radius };
30
+ return { radius, blur };
20
31
  };
@@ -52,5 +52,16 @@ export const useFileItem = (item: TImageItem | TVideoItem, sectionId: string) =>
52
52
  sectionId
53
53
  );
54
54
 
55
- return { radius, strokeWidth, opacity, strokeColor };
55
+ const blur = useKeyframeValue(
56
+ item,
57
+ (item, layoutId) => {
58
+ if (!layoutId) return 0;
59
+ const layoutParams = item.layoutParams[layoutId];
60
+ return 'blur' in layoutParams ? layoutParams.blur : 0;
61
+ },
62
+ (animator, scroll, value) => animator.getBlur({ blur: value }, scroll).blur,
63
+ sectionId
64
+ );
65
+
66
+ return { radius, strokeWidth, opacity, strokeColor, blur };
56
67
  };
@@ -50,5 +50,27 @@ export const useRectangleItem = (item: TRectangleItem, sectionId: string) => {
50
50
  sectionId,
51
51
  [layoutId]
52
52
  );
53
- return { fillColor, strokeWidth, radius, strokeColor };
53
+ const blur = useKeyframeValue(
54
+ item,
55
+ (item, layoutId) => {
56
+ if (!layoutId) return 0;
57
+ const layoutParams = item.layoutParams[layoutId];
58
+ return 'blur' in layoutParams ? layoutParams.blur : 0;
59
+ },
60
+ (animator, scroll, value) => animator.getBlur({ blur: value }, scroll).blur,
61
+ sectionId,
62
+ [layoutId]
63
+ );
64
+ const backdropBlur = useKeyframeValue(
65
+ item,
66
+ (item, layoutId) => {
67
+ if (!layoutId) return 0;
68
+ const layoutParams = item.layoutParams[layoutId];
69
+ return 'backdropBlur' in layoutParams ? layoutParams.backdropBlur : 0;
70
+ },
71
+ (animator, scroll, value) => animator.getBackdropBlur({ backdropBlur: value }, scroll).backdropBlur,
72
+ sectionId,
73
+ [layoutId]
74
+ );
75
+ return { fillColor, strokeWidth, radius, strokeColor, blur, backdropBlur };
54
76
  };
@@ -0,0 +1,28 @@
1
+ import { useCurrentLayout } from '../../common/useCurrentLayout';
2
+ import { useKeyframeValue } from '../../common/useKeyframeValue';
3
+ import { TRichTextItem } from '@cntrl-site/sdk';
4
+
5
+ export const useRichTextItemValues = (item: TRichTextItem, sectionId: string) => {
6
+ const layoutId = useCurrentLayout();
7
+
8
+ const angle = useKeyframeValue(
9
+ item,
10
+ (item, layoutId) => ({ angle: layoutId ? item.area[layoutId].angle : 0 }),
11
+ (animator, scroll, value) => animator.getRotation(value, scroll),
12
+ sectionId
13
+ );
14
+
15
+ const blur = useKeyframeValue(
16
+ item,
17
+ (item, layoutId) => {
18
+ if (!layoutId) return 0;
19
+ const layoutParams = item.layoutParams[layoutId];
20
+ return 'blur' in layoutParams ? layoutParams.blur : 0;
21
+ },
22
+ (animator, scroll, value) => animator.getBlur({ blur: value }, scroll).blur,
23
+ sectionId,
24
+ [layoutId]
25
+ );
26
+
27
+ return { angle, blur };
28
+ };
@@ -0,0 +1,18 @@
1
+ import { TArticleItemAny } from '@cntrl-site/sdk';
2
+ import { useCurrentLayout } from '../../common/useCurrentLayout';
3
+ import { useKeyframeValue } from '../../common/useKeyframeValue';
4
+ import { getAnchoredItemTop } from '../../utils/getAnchoredItemTop';
5
+
6
+ export function useStickyItemTop(item: TArticleItemAny, sectionHeightMap: Record<string, string>, sectionId: string) {
7
+ const layoutId = useCurrentLayout();
8
+ const { top } = useKeyframeValue<{ top: number; left: number }>(
9
+ item,
10
+ (item, layoutId) => item.area[layoutId],
11
+ (animator, scroll, value) => animator.getPositions(value, scroll),
12
+ sectionId,
13
+ [layoutId]
14
+ );
15
+ const sticky = item.sticky[layoutId];
16
+ const sectionHeight = sectionHeightMap[layoutId];
17
+ return sticky ? `${getAnchoredItemTop(top - sticky.from, sectionHeight, item.area[layoutId].anchorSide)}` : 0
18
+ }
@@ -196,6 +196,42 @@ export class Animator {
196
196
  };
197
197
  }
198
198
 
199
+ getBlur(
200
+ values: TKeyframeValueMap[KeyframeType.Blur],
201
+ pos: number
202
+ ): TKeyframeValueMap[KeyframeType.Blur] {
203
+ const keyframes = this.keyframesMap[KeyframeType.Blur];
204
+ if (!keyframes || !keyframes.length) return values;
205
+ if (keyframes.length === 1) {
206
+ const [keyframe] = keyframes;
207
+ return {
208
+ blur: keyframe.value.blur
209
+ };
210
+ }
211
+ const { start, end } = this.getStartEnd<KeyframeType.Blur>(pos, keyframes);
212
+ return {
213
+ blur: rangeMap(pos, start.position, end.position, start.value.blur, end.value.blur, true)
214
+ };
215
+ }
216
+
217
+ getBackdropBlur(
218
+ values: TKeyframeValueMap[KeyframeType.BackdropBlur],
219
+ pos: number
220
+ ): TKeyframeValueMap[KeyframeType.BackdropBlur] {
221
+ const keyframes = this.keyframesMap[KeyframeType.BackdropBlur];
222
+ if (!keyframes || !keyframes.length) return values;
223
+ if (keyframes.length === 1) {
224
+ const [keyframe] = keyframes;
225
+ return {
226
+ backdropBlur: keyframe.value.backdropBlur
227
+ };
228
+ }
229
+ const { start, end } = this.getStartEnd<KeyframeType.BackdropBlur>(pos, keyframes);
230
+ return {
231
+ backdropBlur: rangeMap(pos, start.position, end.position, start.value.backdropBlur, end.value.backdropBlur, true)
232
+ };
233
+ }
234
+
199
235
  getStartEnd<T extends KeyframeType>(position: number, keyframes: AnimationData<T>[]): PositionKeyframes<T> {
200
236
  const index = binSearchInsertAt(keyframes, { position }, compare);
201
237
  const end = index === keyframes.length ? index - 1 : index;
@@ -234,7 +270,9 @@ function createKeyframesMap(): KeyframesMap {
234
270
  [KeyframeType.Rotation]: [],
235
271
  [KeyframeType.BorderColor]: [],
236
272
  [KeyframeType.Opacity]: [],
237
- [KeyframeType.Scale]: []
273
+ [KeyframeType.Scale]: [],
274
+ [KeyframeType.Blur]: [],
275
+ [KeyframeType.BackdropBlur]: []
238
276
  };
239
277
  }
240
278
 
@@ -17,6 +17,8 @@ const hoverTransformationMap: Record<keyof ItemHoverParams, HoverParamsGetter> =
17
17
  'strokeWidth': (strokeWidth: number) => `border-width: ${strokeWidth * 100}vw !important;`,
18
18
  'strokeColor': (strokeColor: string) => `border-color: ${CntrlColor.parse(strokeColor).toCss()} !important;`,
19
19
  'fillColor': (fillColor: string) => `background-color: ${CntrlColor.parse(fillColor).toCss()} !important;`,
20
+ 'blur': (blur: number) => `filter: blur(${blur * 100}vw) !important;`,
21
+ 'backdropBlur': (backdropBlur: number) => `backdrop-filter: blur(${backdropBlur * 100}vw) !important;`
20
22
  };
21
23
 
22
24
  const CSSPropertyNameMap: Record<keyof ItemHoverParams, string> = {
@@ -30,7 +32,9 @@ const CSSPropertyNameMap: Record<keyof ItemHoverParams, string> = {
30
32
  'radius': 'border-radius',
31
33
  'strokeWidth': 'border-width',
32
34
  'strokeColor': 'border-color',
33
- 'fillColor': 'background-color'
35
+ 'fillColor': 'background-color',
36
+ 'blur': 'filter',
37
+ 'backdropBlur': 'backdrop-filter'
34
38
  };
35
39
 
36
40
  export function getTransitions<T extends ArticleItemType>(
@@ -0,0 +1,12 @@
1
+ import { AnchorSide } from '@cntrl-site/sdk';
2
+
3
+ export function getAnchoredItemTop(top: number, sectionHeight: string, anchorSide?: AnchorSide) {
4
+ const styleTop = `${top * 100}vw`;
5
+ switch (anchorSide) {
6
+ case AnchorSide.Center: return `calc(${styleTop} + ${sectionHeight} / 2)`;
7
+ case AnchorSide.Bottom: return `calc(${styleTop} + ${sectionHeight})`;
8
+ case AnchorSide.Top:
9
+ default:
10
+ return styleTop;
11
+ }
12
+ }
Binary file