@cntrl-site/sdk-nextjs 0.14.2 → 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.
@@ -0,0 +1,5 @@
1
+ <component name="ProjectCodeStyleConfiguration">
2
+ <state>
3
+ <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
4
+ </state>
5
+ </component>
@@ -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;
@@ -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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk-nextjs",
3
- "version": "0.14.2",
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"
@@ -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
+ };
@@ -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>(
Binary file
Binary file